Design
llms.txt digitalcrew.tech

Forms

TimezoneSelector

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

MaxFormsDigitalCrew.TimezoneSelector
TimezoneSelector · liveOpen

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 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.

Facts#

GroupForms
ProductMax
Globalwindow.DigitalCrew.TimezoneSelector
Sourcemax-agent: src/components/shared/timezone-selector.tsx
Import in the appimport TimezoneSelector from "@/components/shared/timezone-selector";

API#

TypeScript declarations emitted from src/components/shared/timezone-selector.tsx (the module also exports TimezoneSelector).

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

Example#

The code of the preview above: plain React.createElement against window.DigitalCrew, with realistic data.

<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>