Forms
TimezoneSelector
A searchable combobox (a Popover holding a Command list) over every IANA time zone the browser knows.
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
Buttonwithrole="combobox",w-full justify-between, the label truncated, and aChevronsUpDownicon (h-4 w-4 opacity-50). With no value it reads "Select timezone...". - The content is
p-0, capped atmax-w-[calc(100vw-2rem)]on phones. - Search placeholder: "Search timezone...". Empty state: "No timezone found."
- A 16px
Checkmarks 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#
| Group | Forms |
|---|---|
| Product | Max |
| Global | window.DigitalCrew.TimezoneSelector |
| Source | max-agent: src/components/shared/timezone-selector.tsx |
| Import in the app | import 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>