summaryrefslogtreecommitdiffhomepage
path: root/src/components/SizeSelector.tsx
blob: cb4baef9c4f340b16d95ca198ae8fa07bfae70d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
 * SizeSelector.tsx
 *
 * A reusable pair of <select> + <input> for choosing a rim or tire size.
 *
 * Behaviour:
 *  - Selecting a dropdown option fills the text input with its diameter_mm.
 *  - Typing a number in the text input auto-selects the first matching
 *    dropdown option, or falls back to "Custom" if no match exists.
 *  - Choosing "Custom" in the dropdown does nothing to the text input.
 */

import type { SizeOption } from "../types";

/** Sentinel value used as the <option> value for "Custom". */
const CUSTOM_VALUE = "__custom__";

interface SizeSelectorProps {
  /** Human-readable label shown above the controls. */
  label: string;
  /** The list of predefined size options to populate the dropdown. */
  options: SizeOption[];
  /** Currently selected dropdown value (a label string or CUSTOM_VALUE). */
  selected_option: string;
  /** Current numeric value in the text input (as a string so we can allow empty). */
  input_value: string;
  /** Called when the dropdown selection changes. */
  on_option_change: (option_value: string) => void;
  /** Called when the text input value changes. */
  on_input_change: (value: string) => void;
}

export default function SizeSelector({
  label,
  options,
  selected_option,
  input_value,
  on_option_change,
  on_input_change,
}: SizeSelectorProps) {
  return (
    <fieldset className="fieldset">
      <legend className="fieldset-legend">{label}</legend>

      {/* Dropdown for predefined sizes */}
      <select
        className="select select-bordered w-full"
        value={selected_option}
        aria-label={`${label} preset`}
        onChange={(e) => on_option_change(e.target.value)}
      >
        <option disabled value="">
          Pick a size
        </option>
        {options.map((opt, idx) => (
          <option key={`${opt.label}-${idx}`} value={opt.label}>
            {opt.label} ({opt.diameter_mm} mm)
          </option>
        ))}
        <option value={CUSTOM_VALUE}>Custom</option>
      </select>

      {/* Numeric text input — mirrors / overrides the dropdown */}
      <label className="input w-full mt-2">
        <input
          type="number"
          step="any"
          min="0"
          className="grow"
          placeholder="mm"
          value={input_value}
          aria-label={`${label} diameter in mm`}
          onChange={(e) => on_input_change(e.target.value)}
        />
        <span className="label">mm</span>
      </label>
    </fieldset>
  );
}

export { CUSTOM_VALUE };