# TimezoneSelector

> A searchable combobox (a `Popover` holding a `Command` list) over every IANA time zone the browser knows.

## Facts

- **Product**: Max
- **Family**: Forms
- **Global**: `window.DigitalCrew.TimezoneSelector`
- **Source**: `max-agent: src/components/shared/timezone-selector.tsx`
- **Import in the app**: `import TimezoneSelector from "@/components/shared/timezone-selector";`
- **Live preview**: /design/components/timezone-selector/preview.html
- **Page**: /design/components/timezone-selector

## Guidelines

**Use it for** time-zone fields: a booking link's "Your time zone", and the time zone an email account uses for working hours and scheduling.

**What you provide**: `value` (an IANA id such as `Europe/Paris`), `onValueChange(id)`, and `className` for the trigger (Max passes `h-9`). In the source it is a default export; on `window.DigitalCrew` it is `TimezoneSelector`.

**Labels**: `(GMT±HH:MM) <long name> - <city>`, for example "(GMT+02:00) Central European Summer Time - Paris". They are built with `@date-fns/tz` for the current date, so they follow daylight saving time. The list is sorted by offset, then by id. Search matches both the label and the id. `src/lib/timezones.ts` also exports `DEFAULT_TIMEZONE = "Europe/London"`.

**Anatomy**:
- The trigger is an outline [`Button`](/design/components/button.md) with `role="combobox"`, `w-full justify-between`, the label truncated, and a `ChevronsUpDown` icon (`h-4 w-4 opacity-50`). With no value it reads "Select timezone...".
- The content is `p-0`, capped at `max-w-[calc(100vw-2rem)]` on phones.
- Search placeholder: "Search timezone...". Empty state: "No timezone found."
- A 16px `Check` marks the current value.

**Behaviour**: choosing an item calls `onValueChange` and closes the popover. The list scrolls on the mouse wheel through its own `onWheel` handler.

**Don't** store offsets like "+02:00". Store the IANA id so daylight saving time stays correct.

## API

```ts
interface TimezoneSelectorProps {
    value: string;
    onValueChange: (value: string) => void;
    className?: string;
}
export default function TimezoneSelector({ value, onValueChange, className, }: TimezoneSelectorProps): any;
```

## Example

```html
<div id="root" class="p-6"></div>
<script>
(function () {
  var h = React.createElement, DC = window.DigitalCrew, I = DC.Icons;
  function App() {
    var s = React.useState("Europe/Paris"), tz = s[0], setTz = s[1];
    var ref = React.useRef(null);
    React.useEffect(function () {
      // Open the popover and type a query so the card shows the searchable list.
      var t = setTimeout(function () {
        var btn = ref.current && ref.current.querySelector("button[role=combobox]");
        if (btn) btn.click();
        setTimeout(function () {
          var input = document.querySelector("[cmdk-input]");
          if (!input) return;
          var setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value").set;
          setter.call(input, "Central European");
          input.dispatchEvent(new Event("input", { bubbles: true }));
        }, 120);
      }, 150);
      return function () { clearTimeout(t); };
    }, []);
    return h("div", { ref: ref, className: "max-w-sm space-y-1.5" },
      h(DC.Label, null, "Your time zone"),
      h(DC.TimezoneSelector, { value: tz, onValueChange: setTz, className: "h-9" }),
      h("p", { className: "text-xs text-muted-foreground" }, "Booking slots are shown to guests in their own time zone."));
  }
  ReactDOM.createRoot(document.getElementById("root")).render(h(DC.TooltipProvider, { delayDuration: 80 }, h(App)));
})();
</script>
```

## More in Forms

- [Calendar](/design/components/calendar.md): A month grid (react-day-picker 10) in the app's tokens: today and the selection in `primary`, outside days muted.
- [Checkbox](/design/components/checkbox.md): A 16px square check for independent on/off choices and row selection; the box fills in 150ms, then the tick draws in over 350ms (`.t-check`).
- [DateTimePicker](/design/components/date-time-picker.md): A date-and-time field in the app's own widget, instead of the browser's `datetime-local`; works in ISO strings.
- [Form](/design/components/form.md): Form fields wired to react-hook-form: each field gets a label, a control, an optional hint and an error message, with ids and ARIA linked for you.
- [Input](/design/components/input.md): The single-line text field: 36px tall, `input` border, `radius-md`, transparent over its ground (dark themes fill it with `input` at 30%).
- [Label](/design/components/label.md): The accessible field label (Radix Label): 14px medium, `leading-none`, 8px gap for an inline icon or `HelpTooltip`.
- [PhoneInput](/design/components/phone-input.md): An international phone field: `react-phone-number-input` with Max's `Input` and a searchable country picker that shows flags.
- [RadioGroup](/design/components/radio-group.md): One choice from a short, visible list (Radix RadioGroup): 16px rings in `primary`, 10px dot.
- [SecretInput](/design/components/secret-input.md): A field for API keys and tokens: masked by default with a reveal toggle, monospace, opted out of autofill.
- [Select](/design/components/select.md): A native-feeling single choice from a list (Radix Select): 36px trigger (`size="sm"`: 32px) with a chevron; the list opens in 250ms from 97% (`.t-dropdown`).
- [Slider](/design/components/slider.md): A Radix slider for picking a number, or a range with two thumbs, on a continuous scale.
- [Switch](/design/components/switch.md): The on/off control for settings that apply immediately (Radix Switch): a 32 × 18px pill, `input` track off, `primary` on, 16px thumb with a double-bounce (`.t-toggle`, 350ms thumb, 150ms track).
- [Textarea](/design/components/textarea.md): The multi-line field; it grows with its content (`field-sizing: content`) from a 64px minimum.
- [TypedOptionPicker](/design/components/typed-option-picker.md): A searchable, grouped picker for long option lists (Popover + Command), in single and multi versions.
