@fiestaboard/ui 5.3.0 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +18 -0
  2. package/dist/components/chrome/nav-list.d.ts +53 -0
  3. package/dist/components/chrome/nav-list.js +84 -0
  4. package/dist/components/chrome/nav-list.js.map +1 -0
  5. package/dist/components/containment/icon-tile.d.ts +67 -0
  6. package/dist/components/containment/icon-tile.js +44 -0
  7. package/dist/components/containment/icon-tile.js.map +1 -0
  8. package/dist/components/containment/json-tree.js +1 -1
  9. package/dist/components/containment/json-tree.js.map +1 -1
  10. package/dist/components/containment/scroll-area.d.ts +17 -0
  11. package/dist/components/containment/scroll-area.js +2 -2
  12. package/dist/components/containment/scroll-area.js.map +1 -1
  13. package/dist/components/containment/tabs.js +2 -2
  14. package/dist/components/containment/tabs.js.map +1 -1
  15. package/dist/components/editor/formula/formula-editor-panel.js +2 -2
  16. package/dist/components/editor/formula/formula-editor-panel.js.map +1 -1
  17. package/dist/components/editor/template-editor-toolbar.d.ts +27 -3
  18. package/dist/components/editor/template-editor-toolbar.js +99 -96
  19. package/dist/components/editor/template-editor-toolbar.js.map +1 -1
  20. package/dist/components/forms/button.d.ts +1 -1
  21. package/dist/components/forms/button.js +2 -0
  22. package/dist/components/forms/button.js.map +1 -1
  23. package/dist/components/forms/selection-group.d.ts +82 -0
  24. package/dist/components/forms/selection-group.js +51 -0
  25. package/dist/components/forms/selection-group.js.map +1 -0
  26. package/dist/components/forms/swatch.d.ts +81 -0
  27. package/dist/components/forms/swatch.js +91 -0
  28. package/dist/components/forms/swatch.js.map +1 -0
  29. package/dist/components/forms/timezone-picker.d.ts +163 -0
  30. package/dist/components/forms/timezone-picker.js +158 -0
  31. package/dist/components/forms/timezone-picker.js.map +1 -0
  32. package/dist/components/forms/toggle-card.d.ts +45 -35
  33. package/dist/components/forms/toggle-card.js +187 -122
  34. package/dist/components/forms/toggle-card.js.map +1 -1
  35. package/dist/components/forms/toggle.d.ts +1 -1
  36. package/dist/components/layout/flex.d.ts +1 -1
  37. package/dist/components/layout/grid.d.ts +1 -1
  38. package/dist/components/layout/stack.d.ts +1 -1
  39. package/dist/components/overlays/lightbox.js +1 -1
  40. package/dist/components/overlays/lightbox.js.map +1 -1
  41. package/dist/components/overlays/overlay-close.js +1 -1
  42. package/dist/components/overlays/overlay-close.js.map +1 -1
  43. package/dist/components/plugin/board-showcase.js +1 -1
  44. package/dist/components/plugin/board-showcase.js.map +1 -1
  45. package/dist/components/typography/heading.d.ts +1 -1
  46. package/dist/components/typography/text-link.d.ts +27 -3
  47. package/dist/components/typography/text-link.js +1 -1
  48. package/dist/components/typography/text-link.js.map +1 -1
  49. package/dist/components/typography/text.d.ts +1 -1
  50. package/dist/components/wizard/wizard-shell.d.ts +6 -1
  51. package/dist/components/wizard/wizard-shell.js +38 -35
  52. package/dist/components/wizard/wizard-shell.js.map +1 -1
  53. package/dist/index.d.ts +4 -0
  54. package/dist/index.js +104 -100
  55. package/dist/theme.css +85 -4
  56. package/package.json +9 -2
@@ -0,0 +1,51 @@
1
+ "use client";
2
+ import "react";
3
+ import { jsx as e } from "react/jsx-runtime";
4
+ import { Radio as t } from "@base-ui/react/radio";
5
+ import { RadioGroup as n } from "@base-ui/react/radio-group";
6
+ //#region src/components/forms/selection-group.tsx
7
+ function r({ slot: t, onValueChange: r, onKeyDown: i, className: a, children: o, ...s }) {
8
+ return /* @__PURE__ */ e(n, {
9
+ "data-slot": t,
10
+ className: a,
11
+ onValueChange: (e) => r?.(e),
12
+ onKeyDown: (e) => {
13
+ if (i?.(e), e.defaultPrevented || e.key !== "Home" && e.key !== "End" || e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.target?.getAttribute("role") !== "radio") return;
14
+ let t = Array.from(e.currentTarget.querySelectorAll("[role=\"radio\"]")).filter((e) => !e.hasAttribute("disabled") && e.getAttribute("aria-disabled") !== "true"), n = e.key === "Home" ? t[0] : t[t.length - 1];
15
+ n !== void 0 && (e.preventDefault(), n.focus(), n.click());
16
+ },
17
+ ...s,
18
+ children: o
19
+ });
20
+ }
21
+ function i({ grouped: n, value: r, pressed: i, onPressedChange: a, onClick: o, disabled: s, ref: c, children: l, ...u }) {
22
+ if (n) return /* @__PURE__ */ e(t.Root, {
23
+ value: r,
24
+ render: /* @__PURE__ */ e("button", { type: "button" }),
25
+ nativeButton: !0,
26
+ disabled: s,
27
+ onClick: o,
28
+ ref: c,
29
+ ...u,
30
+ children: l
31
+ });
32
+ let d = (e) => {
33
+ o?.(e), !e.defaultPrevented && a?.(!i);
34
+ };
35
+ return /* @__PURE__ */ e("button", {
36
+ type: "button",
37
+ disabled: s,
38
+ ref: c,
39
+ ...i === void 0 ? {} : {
40
+ "aria-pressed": i,
41
+ ...i ? { "data-checked": "" } : { "data-unchecked": "" }
42
+ },
43
+ onClick: d,
44
+ ...u,
45
+ children: l
46
+ });
47
+ }
48
+ //#endregion
49
+ export { i as SelectableItemRoot, r as SelectionGroupRoot };
50
+
51
+ //# sourceMappingURL=selection-group.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection-group.js","names":[],"sources":["../../../src/components/forms/selection-group.tsx"],"mappings":";;;;;;AAuEA,SAAS,EAAmB,EAC1B,SACA,kBACA,cACA,cACA,aACA,GAAG,KACuB;CAwB1B,OACE,kBAAC,GAAD;EACE,aAAW;EACA;EACX,gBAAgB,MAAS,IAAgB,CAAc;EACvD,YA5BmB,MAA+C;GAMpE,IALA,IAAY,CAAK,GACb,EAAM,oBACN,EAAM,QAAQ,UAAU,EAAM,QAAQ,SAEtC,EAAM,UAAU,EAAM,WAAW,EAAM,WAAW,EAAM,YACvD,EAAM,QAA+B,aAAa,MAAM,MAAM,SAAS;GAE5E,IAAM,IAAU,MAAM,KAAK,EAAM,cAAc,iBAA8B,kBAAgB,CAAC,CAAC,CAAC,QAC7F,MAAW,CAAC,EAAO,aAAa,UAAU,KAAK,EAAO,aAAa,eAAe,MAAM,MAC3F,GACM,IAAS,EAAM,QAAQ,SAAS,EAAQ,KAAK,EAAQ,EAAQ,SAAS;GACxE,MAAW,KAAA,MAGf,EAAM,eAAe,GACrB,EAAO,MAAM,GAIb,EAAO,MAAM;EACf;EAQI,GAAI;EAEH;CACkB,CAAA;AAEzB;AA+BA,SAAS,EAAmB,EAC1B,YACA,UACA,YACA,oBACA,YACA,aACA,QACA,aACA,GAAG,KACuB;CAC1B,IAAI,GACF,OAGE,kBAAC,EAAe,MAAhB;EACS;EACP,QAAQ,kBAAC,UAAD,EAAQ,MAAK,SAAU,CAAA;EAC/B,cAAA;EACU;EACD;EACJ;EACL,GAAI;EAEH;CACkB,CAAA;CAIzB,IAAM,KAAe,MAAyC;EAC5D,IAAU,CAAK,GACX,GAAM,oBACV,IAAkB,CAAC,CAAO;CAC5B;CAEA,OACE,kBAAC,UAAD;EACE,MAAK;EACK;EACL;EAIL,GAAK,MAAY,KAAA,IACb,CAAC,IACD;GAAE,gBAAgB;GAAS,GAAI,IAAU,EAAE,gBAAgB,GAAG,IAAI,EAAE,kBAAkB,GAAG;EAAG;EAChG,SAAS;EACT,GAAI;EAEH;CACK,CAAA;AAEZ"}
@@ -0,0 +1,81 @@
1
+ import { type VariantProps } from "class-variance-authority";
2
+ import * as React from "react";
3
+ import { type SelectionGroupBaseProps, type SelectionGroupNameProps } from "./selection-group";
4
+ declare const swatchGroupVariants: (props?: ({
5
+ wrap?: boolean | null | undefined;
6
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
+ declare const swatchVariants: (props?: ({
8
+ size?: "sm" | "md" | "lg" | null | undefined;
9
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
10
+ declare const swatchFillVariants: (props?: ({
11
+ size?: "sm" | "md" | "lg" | null | undefined;
12
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
13
+ declare const swatchIndicatorVariants: (props?: ({
14
+ size?: "sm" | "md" | "lg" | null | undefined;
15
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
16
+ type SwatchSize = NonNullable<VariantProps<typeof swatchVariants>["size"]>;
17
+ export type SwatchGroupProps = SelectionGroupBaseProps & SelectionGroupNameProps & VariantProps<typeof swatchGroupVariants> & {
18
+ /** Diameter applied to every swatch in the group; a swatch may override it. */
19
+ size?: SwatchSize;
20
+ className?: string;
21
+ children?: React.ReactNode;
22
+ } & Omit<React.ComponentProps<"div">, "defaultValue" | "onChange">;
23
+ /**
24
+ * Single-select group of {@link Swatch}es — a real `radiogroup`.
25
+ *
26
+ * One tab stop for the whole group; arrow keys move the selection and wrap,
27
+ * Home/End jump to the ends, and the selection can never be emptied (a board
28
+ * always has a colour). Swatches inside it take their state from the group's
29
+ * `value`, so they need a `value` of their own and must NOT be given
30
+ * `pressed`.
31
+ *
32
+ * The group needs its own accessible name ("Board colour") and the type
33
+ * requires it: swatch labels do not compose into one, and a nameless
34
+ * radiogroup fails axe.
35
+ *
36
+ * A wrapped group keeps Base UI's LINEAR arrow-key order — Up/Down step by
37
+ * one, not by the column count. Row-aware movement is only correct with a
38
+ * fixed column count, which a `flex-wrap` palette does not have; a ±1
39
+ * Up/Down that pretends otherwise is the half-measure `editor/color-picker-
40
+ * content.tsx` already regretted. Use a fixed-column grid via `className`
41
+ * only if you are prepared to own that arithmetic.
42
+ */
43
+ declare function SwatchGroup({ className, size, wrap, children, ...props }: SwatchGroupProps): React.JSX.Element;
44
+ export type SwatchProps = Omit<React.ComponentProps<"button">, "aria-label" | "children" | "color" | "value"> & VariantProps<typeof swatchVariants> & {
45
+ /**
46
+ * The colour being picked, as ANY CSS colour — a board-surface token
47
+ * (`var(--color-board-surface-dark)`), a plugin colour-rule hue, or a
48
+ * raw hex. It is applied as an inline `background-color`, which is the
49
+ * only way an arbitrary runtime value can reach the DOM; Tailwind cannot
50
+ * compile a class for a value it never sees.
51
+ */
52
+ color: string;
53
+ /**
54
+ * Localized accessible name — e.g. "Black", "Board white". REQUIRED: the
55
+ * swatch renders no text, so this is its only name. Name the colour, not
56
+ * its value; "#0d0d0d" is not a name.
57
+ */
58
+ label: string;
59
+ /** Identifies the swatch inside a {@link SwatchGroup}. */
60
+ value?: string;
61
+ /**
62
+ * Standalone toggle state — only outside a group. Pair with
63
+ * `onPressedChange`. Leave undefined for a swatch that merely opens a
64
+ * picker: it then gets no `aria-pressed` at all.
65
+ */
66
+ pressed?: boolean;
67
+ onPressedChange?: (pressed: boolean) => void;
68
+ };
69
+ /**
70
+ * One colour circle. Inside a {@link SwatchGroup} it is a `radio`
71
+ * (`aria-checked`, roving tabindex, arrow keys). On its own with `pressed`
72
+ * it is an `aria-pressed` toggle button.
73
+ *
74
+ * Fill diameters are 20 / 24 / 32px at `sm` / `md` / `lg`; the button around
75
+ * them is 28 / 32 / 40px, so every target clears WCAG 2.2 SC 2.5.8's 24×24
76
+ * minimum. Selection shows as a `--foreground` ring outside the fill plus a
77
+ * `--primary` check disc over it — never as a change to the fill, which is
78
+ * the thing being picked.
79
+ */
80
+ declare function Swatch({ className, size, value, color, label, pressed, onPressedChange, ...props }: SwatchProps): React.JSX.Element;
81
+ export { Swatch, swatchFillVariants, SwatchGroup, swatchGroupVariants, swatchIndicatorVariants, swatchVariants };
@@ -0,0 +1,91 @@
1
+ "use client";
2
+ import { cn as e } from "../../lib/utils.js";
3
+ import { SelectableItemRoot as t, SelectionGroupRoot as n } from "./selection-group.js";
4
+ import { Check as r } from "lucide-react";
5
+ import * as i from "react";
6
+ import { jsx as a } from "react/jsx-runtime";
7
+ import { cva as o } from "class-variance-authority";
8
+ //#region src/components/forms/swatch.tsx
9
+ var s = o(["flex items-center gap-2"], {
10
+ variants: { wrap: {
11
+ true: "flex-wrap",
12
+ false: "flex-nowrap"
13
+ } },
14
+ defaultVariants: { wrap: !0 }
15
+ }), c = o([
16
+ "group/swatch inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full",
17
+ "bg-transparent p-1",
18
+ "focus-ring transition-[box-shadow,opacity] duration-control",
19
+ "disabled:pointer-events-none disabled:opacity-50"
20
+ ], {
21
+ variants: { size: {
22
+ sm: "size-7",
23
+ md: "size-8",
24
+ lg: "size-10"
25
+ } },
26
+ defaultVariants: { size: "md" }
27
+ }), l = o([
28
+ "pointer-events-none flex items-center justify-center rounded-full",
29
+ "border border-input",
30
+ "ring-offset-2 ring-offset-background transition-[box-shadow] duration-control",
31
+ "group-data-[checked]/swatch:ring-2 group-data-[checked]/swatch:ring-foreground"
32
+ ], {
33
+ variants: { size: {
34
+ sm: "size-5",
35
+ md: "size-6",
36
+ lg: "size-8"
37
+ } },
38
+ defaultVariants: { size: "md" }
39
+ }), u = o([
40
+ "pointer-events-none flex items-center justify-center rounded-full bg-primary text-primary-foreground",
41
+ "ring-1 ring-background opacity-0 transition-opacity duration-control",
42
+ "group-data-[checked]/swatch:opacity-100"
43
+ ], {
44
+ variants: { size: {
45
+ sm: "size-3 [&_svg]:size-2",
46
+ md: "size-3.5 [&_svg]:size-2.5",
47
+ lg: "size-4 [&_svg]:size-3"
48
+ } },
49
+ defaultVariants: { size: "md" }
50
+ }), d = i.createContext(null);
51
+ function f({ className: t, size: r, wrap: o, children: c, ...l }) {
52
+ let u = i.useMemo(() => ({ size: r }), [r]);
53
+ return /* @__PURE__ */ a(d.Provider, {
54
+ value: u,
55
+ children: /* @__PURE__ */ a(n, {
56
+ slot: "swatch-group",
57
+ className: e(s({ wrap: o }), t),
58
+ ...l,
59
+ children: c
60
+ })
61
+ });
62
+ }
63
+ function p({ className: n, size: o, value: s, color: f, label: p, pressed: m, onPressedChange: h, ...g }) {
64
+ let _ = i.useContext(d), v = o ?? _?.size ?? "md";
65
+ return /* @__PURE__ */ a(t, {
66
+ grouped: _ !== null,
67
+ value: s,
68
+ pressed: m,
69
+ onPressedChange: h,
70
+ "aria-label": p,
71
+ "data-slot": "swatch",
72
+ "data-size": v,
73
+ className: e(c({ size: v }), n),
74
+ ...g,
75
+ children: /* @__PURE__ */ a("span", {
76
+ "data-slot": "swatch-fill",
77
+ "aria-hidden": "true",
78
+ style: { backgroundColor: f },
79
+ className: l({ size: v }),
80
+ children: /* @__PURE__ */ a("span", {
81
+ "data-slot": "swatch-indicator",
82
+ className: u({ size: v }),
83
+ children: /* @__PURE__ */ a(r, {})
84
+ })
85
+ })
86
+ });
87
+ }
88
+ //#endregion
89
+ export { p as Swatch, f as SwatchGroup, l as swatchFillVariants, s as swatchGroupVariants, u as swatchIndicatorVariants, c as swatchVariants };
90
+
91
+ //# sourceMappingURL=swatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swatch.js","names":[],"sources":["../../../src/components/forms/swatch.tsx"],"mappings":";;;;;;;;AAkGA,IAAM,IAAsB,EAC1B,CAIE,yBACF,GACA;CACE,UAAU,EACR,MAAM;EACJ,MAAM;EACN,OAAO;CACT,EACF;CACA,iBAAiB,EACf,MAAM,GACR;AACF,CACF,GAEM,IAAiB,EACrB;CAIE;CAIA;CAGA;CACA;AACF,GACA;CACE,UAAU,EACR,MAAM;EAEJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN,EACF;CACA,iBAAiB,EACf,MAAM,KACR;AACF,CACF,GAEM,IAAqB,EACzB;CACE;CAGA;CAGA;CACA;AACF,GACA;CACE,UAAU,EACR,MAAM;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN,EACF;CACA,iBAAiB,EACf,MAAM,KACR;AACF,CACF,GAEM,IAA0B,EAC9B;CAIE;CACA;CACA;AACF,GACA;CACE,UAAU,EACR,MAAM;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN,EACF;CACA,iBAAiB,EACf,MAAM,KACR;AACF,CACF,GAQM,IAAqB,EAAM,cAA8C,IAAI;AA+BnF,SAAS,EAAY,EAAE,cAAW,SAAM,SAAM,aAAU,GAAG,KAA2B;CACpF,IAAM,IAAU,EAAM,eAAwC,EAAE,QAAK,IAAI,CAAC,CAAI,CAAC;CAE/E,OACE,kBAAC,EAAmB,UAApB;EAA6B,OAAO;EAClC,UAAA,kBAAC,GAAD;GAAoB,MAAK;GAAe,WAAW,EAAG,EAAoB,EAAE,QAAK,CAAC,GAAG,CAAS;GAAG,GAAI;GAClG;EACiB,CAAA;CACO,CAAA;AAEjC;AAwCA,SAAS,EAAO,EAAE,cAAW,SAAM,UAAO,UAAO,UAAO,YAAS,oBAAiB,GAAG,KAAsB;CACzG,IAAM,IAAQ,EAAM,WAAW,CAAkB,GAC3C,IAAe,KAAQ,GAAO,QAAQ;CAE5C,OACE,kBAAC,GAAD;EACE,SAAS,MAAU;EACZ;EACE;EACQ;EACjB,cAAY;EACZ,aAAU;EACV,aAAW;EACX,WAAW,EAAG,EAAe,EAAE,MAAM,EAAa,CAAC,GAAG,CAAS;EAC/D,GAAI;EAEJ,UAAA,kBAAC,QAAD;GACE,aAAU;GAGV,eAAY;GACZ,OAAO,EAAE,iBAAiB,EAAM;GAChC,WAAW,EAAmB,EAAE,MAAM,EAAa,CAAC;GAEpD,UAAA,kBAAC,QAAD;IAAM,aAAU;IAAmB,WAAW,EAAwB,EAAE,MAAM,EAAa,CAAC;IAC1F,UAAA,kBAAC,GAAD,CAAQ,CAAA;GACJ,CAAA;EACF,CAAA;CACY,CAAA;AAExB"}
@@ -0,0 +1,163 @@
1
+ import * as React from "react";
2
+ /**
3
+ * Searchable IANA time-zone combobox: a text input that filters ~400 zones and
4
+ * a portalled listbox of matches.
5
+ *
6
+ * WHAT IT IS NOT.
7
+ * `Select` is a listbox with no text entry — usable at 10 options, unusable at
8
+ * 400, which is why the consuming app hand-rolled 336 lines of this and
9
+ * rendered it from three places. `TimePicker` picks an instant *within* a day
10
+ * (hour + minute + presets); this picks the zone that instant is read in. They
11
+ * compose — a schedule row is a `TimePicker` next to a `TimezonePicker` — so
12
+ * they deliberately share a controlled/uncontrolled shape: `value` +
13
+ * `defaultValue` + `onValueChange`, all optional, resolved with `??` so a
14
+ * controlled `""` still means "unset" instead of falling through to internal
15
+ * state.
16
+ *
17
+ * VALUE vs TEXT. `value` is the IANA id (`"Europe/Berlin"`), never the display
18
+ * label — the label is presentation and a consumer may localize it through
19
+ * `timezones`. The input's *text* is a separate thing the user can type
20
+ * anything into, so the component reports whether that text currently names a
21
+ * known zone via {@link TimezonePickerProps.onValidityChange}, which is what
22
+ * lets a form block "Europe/Berlim" from being saved.
23
+ *
24
+ * ACCESSIBILITY CONTRACT (all of it from Base UI's Combobox, none hand-rolled):
25
+ * the input is `role="combobox"` with `aria-expanded` / `aria-haspopup` /
26
+ * `aria-controls`; the panel is `role="listbox"` with `role="option"` rows;
27
+ * ArrowUp/ArrowDown move the highlight as `aria-activedescendant` while DOM
28
+ * focus stays on the input (the combobox pattern — unlike TimePicker's
29
+ * roving-tabindex listbox, where focus really moves); Enter commits, Escape
30
+ * closes, an outside press dismisses. The panel is portalled, so a picker
31
+ * inside a card or a scroll box with `overflow: hidden` is not clipped, and
32
+ * the positioner anchors it to the input.
33
+ *
34
+ * `aria-invalid` is NOT set from the validity check, deliberately. It would
35
+ * flip to "invalid" on every keystroke of a zone the user is halfway through
36
+ * typing, and a live combobox announcing an error mid-word is worse than no
37
+ * error at all. The unknown-text state is published as `data-unknown-zone` (a
38
+ * silent styling hook) and as `onValidityChange`; *when* to escalate it to
39
+ * `aria-invalid` — on blur, on submit — is the form's call, not the kit's.
40
+ *
41
+ * Rows are 32px tall (py-1.5 over a 20px line box), clearing WCAG 2.2 SC 2.5.8's
42
+ * 24×24 minimum target. The offset column is `text-muted-foreground`, which is
43
+ * text and therefore held to SC 1.4.3's 4.5:1 by the token, not to 3:1.
44
+ */
45
+ /** A zone as this component consumes it. See {@link listTimezones}. */
46
+ export interface TimezoneOption {
47
+ /** IANA identifier, e.g. `"Europe/Berlin"`. This is what `value` carries. */
48
+ id: string;
49
+ /**
50
+ * Localized display name shown in the input and the list (the app resolves
51
+ * i18n). Defaults to the id with underscores turned into spaces, so
52
+ * `"America/New_York"` reads `"America/New York"` and the visible text stays
53
+ * the identifier the user probably has in mind.
54
+ */
55
+ label?: string;
56
+ /**
57
+ * UTC offset as a bare signed string, `"+02:00"`. Derived from the zone for
58
+ * the current instant when omitted. Pass it explicitly when the rendering
59
+ * must not move with DST — a Storybook story or a VRT baseline.
60
+ */
61
+ offset?: string;
62
+ }
63
+ /** {@link TimezoneOption} after defaults are filled in. */
64
+ type ResolvedZone = Required<TimezoneOption>;
65
+ /**
66
+ * Every user-visible string the picker renders on its own. All optional with
67
+ * English defaults, the same shape TimePicker uses — the package never
68
+ * resolves i18n, the app passes copy in.
69
+ */
70
+ export interface TimezonePickerLabels {
71
+ /** Input placeholder while no zone is chosen. Default `"Search time zones"`. */
72
+ placeholder?: string;
73
+ /** Shown and announced politely when the query matches nothing. Default `"No matching time zone"`. */
74
+ empty?: string;
75
+ /** Accessible name of the results listbox. Default `"Time zones"`. */
76
+ list?: string;
77
+ }
78
+ /**
79
+ * The runtime's IANA zone table, each entry carrying its UTC offset at
80
+ * `referenceDate` (default: now).
81
+ *
82
+ * A zone's offset is a function of the date — Europe/Berlin is `+01:00` in
83
+ * January and `+02:00` in July — so this cannot be a constant. It is cached at
84
+ * hour granularity: deriving 418 offsets costs ~35 ms of `Intl.DateTimeFormat`
85
+ * construction, which must not run once per render per picker, and an hourly
86
+ * key means a DST transition corrects itself without anyone reloading the tab.
87
+ *
88
+ * Defensive by design, because `Intl.supportedValuesOf` is absent from thin
89
+ * ICU builds (and from some jsdom setups): if the zone table cannot be read at
90
+ * all it degrades to the single resolved local zone, and if that fails too it
91
+ * returns `[]`. Either way the escape hatch is the same — pass `timezones`.
92
+ */
93
+ export declare function listTimezones(referenceDate?: Date): ResolvedZone[];
94
+ export interface TimezonePickerProps {
95
+ /**
96
+ * Selected IANA id, controlled. `""` means unset. Pair with
97
+ * {@link TimezonePickerProps.onValueChange}; omit both for the uncontrolled
98
+ * form seeded by `defaultValue`.
99
+ */
100
+ value?: string;
101
+ /** Initial IANA id (uncontrolled). Default `""` — unset. */
102
+ defaultValue?: string;
103
+ /** Fired with the new IANA id when a zone is chosen. One argument, never Base UI's event-details pair. */
104
+ onValueChange?: (value: string) => void;
105
+ /**
106
+ * The zones to offer. Defaults to {@link listTimezones}, i.e. everything this
107
+ * runtime's ICU build knows about, labelled by id. Pass a list to shorten it
108
+ * to the zones your product supports, to localize the labels, or to pin the
109
+ * offsets so a story or a VRT baseline does not move at the next DST change.
110
+ */
111
+ timezones?: readonly TimezoneOption[];
112
+ /**
113
+ * The instant zone offsets are computed for, when they are derived rather
114
+ * than supplied. Defaults to now; pass a fixed date to make rendering
115
+ * deterministic.
116
+ */
117
+ referenceDate?: Date;
118
+ /**
119
+ * Called when the answer to "does the text in the input name a known zone?"
120
+ * flips, and once on mount so a form can gate its submit before any typing.
121
+ *
122
+ * An EMPTY input reports `true`. Emptiness is required-ness, which the form
123
+ * already models — it can see `value === ""` — and reporting it as invalid
124
+ * would make every untouched optional zone field shout on mount. `false`
125
+ * means the user typed something that is not a zone, which is the typo this
126
+ * callback exists to catch.
127
+ */
128
+ onValidityChange?: (valid: boolean) => void;
129
+ /**
130
+ * Maximum number of matches rendered at once. Default `100`: an unfiltered
131
+ * open would otherwise mount ~400 option rows, and nobody scrolls past the
132
+ * first screen — they type. `-1` renders every match.
133
+ */
134
+ limit?: number;
135
+ labels?: TimezonePickerLabels;
136
+ disabled?: boolean;
137
+ readOnly?: boolean;
138
+ required?: boolean;
139
+ /** Submits the selected IANA id under this name when inside a form. */
140
+ name?: string;
141
+ /** Forwarded to the input so an external `<Label htmlFor>` can name it. */
142
+ id?: string;
143
+ "aria-label"?: string;
144
+ "aria-labelledby"?: string;
145
+ "aria-describedby"?: string;
146
+ /**
147
+ * Passed straight through. The picker never derives this from the validity
148
+ * check — see the file header for why mid-word "invalid" is the wrong call.
149
+ */
150
+ "aria-invalid"?: React.AriaAttributes["aria-invalid"];
151
+ /** Classes for the input. */
152
+ className?: string;
153
+ /** Classes for the popup surface. */
154
+ contentClassName?: string;
155
+ /** Open the list on first render (uncontrolled). */
156
+ defaultOpen?: boolean;
157
+ /** Controlled open state; pair with `onOpenChange`. */
158
+ open?: boolean;
159
+ onOpenChange?: (open: boolean) => void;
160
+ ref?: React.Ref<HTMLInputElement>;
161
+ }
162
+ declare function TimezonePicker({ value: valueProp, defaultValue, onValueChange, timezones, referenceDate, onValidityChange, limit, labels, disabled, readOnly, required, name, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, className, contentClassName, defaultOpen, open, onOpenChange, ref, }: TimezonePickerProps): React.JSX.Element;
163
+ export { TimezonePicker };
@@ -0,0 +1,158 @@
1
+ "use client";
2
+ import { cn as e } from "../../lib/utils.js";
3
+ import { Globe as t } from "lucide-react";
4
+ import * as n from "react";
5
+ import { jsx as r, jsxs as i } from "react/jsx-runtime";
6
+ import { Combobox as a } from "@base-ui/react/combobox";
7
+ //#region src/components/forms/timezone-picker.tsx
8
+ var o = {
9
+ placeholder: "Search time zones",
10
+ empty: "No matching time zone",
11
+ list: "Time zones"
12
+ }, s = /GMT([+-]\d{2}:\d{2})/;
13
+ function c(e, t) {
14
+ try {
15
+ let n = new Intl.DateTimeFormat("en-US", {
16
+ timeZone: e,
17
+ timeZoneName: "longOffset"
18
+ }).formatToParts(t).find((e) => e.type === "timeZoneName")?.value ?? "";
19
+ return n === "GMT" ? "+00:00" : s.exec(n)?.[1] ?? "";
20
+ } catch {
21
+ return "";
22
+ }
23
+ }
24
+ var l = null;
25
+ function u(e = /* @__PURE__ */ new Date()) {
26
+ let t = e.toISOString().slice(0, 13);
27
+ if (l?.key === t) return l.zones;
28
+ let n = [];
29
+ try {
30
+ n = Intl.supportedValuesOf?.("timeZone") ?? [];
31
+ } catch {
32
+ n = [];
33
+ }
34
+ if (n.length === 0) try {
35
+ let e = new Intl.DateTimeFormat().resolvedOptions().timeZone;
36
+ n = e ? [e] : [];
37
+ } catch {
38
+ n = [];
39
+ }
40
+ let r = n.map((t) => ({
41
+ id: t,
42
+ label: d(t),
43
+ offset: c(t, e)
44
+ })).filter((e) => e.offset !== "");
45
+ return l = {
46
+ key: t,
47
+ zones: r
48
+ }, r;
49
+ }
50
+ function d(e) {
51
+ return e.replace(/_/g, " ");
52
+ }
53
+ function f(e, t) {
54
+ return e ? e.map((e) => ({
55
+ id: e.id,
56
+ label: e.label ?? d(e.id),
57
+ offset: e.offset ?? c(e.id, t)
58
+ })) : u(t);
59
+ }
60
+ function p(e) {
61
+ return `${e.id} ${d(e.id)} ${e.label} ${e.offset} utc${e.offset} gmt${e.offset}`.toLowerCase();
62
+ }
63
+ function m(e, t) {
64
+ let n = t.trim().toLowerCase().split(/\s+/).filter(Boolean);
65
+ if (n.length === 0) return !0;
66
+ let r = p(e);
67
+ return n.every((e) => r.includes(e));
68
+ }
69
+ function h({ value: s, defaultValue: c = "", onValueChange: l, timezones: u, referenceDate: d, onValidityChange: p, limit: h = 100, labels: g, disabled: _, readOnly: v, required: y, name: b, id: x, "aria-label": S, "aria-labelledby": C, "aria-describedby": w, "aria-invalid": T, className: E, contentClassName: D, defaultOpen: O, open: k, onOpenChange: A, ref: j }) {
70
+ let M = {
71
+ ...o,
72
+ ...g
73
+ }, N = n.useMemo(() => f(u, d ?? /* @__PURE__ */ new Date()), [u, d]), [P, F] = n.useState(c), I = s ?? P, L = n.useMemo(() => N.find((e) => e.id === I) ?? null, [N, I]), [R, z] = n.useState(() => L?.label ?? ""), [B, V] = n.useState(I);
74
+ B !== I && (V(I), z(L?.label ?? ""));
75
+ let H = n.useMemo(() => {
76
+ let e = R.trim().toLowerCase();
77
+ return e === "" || N.some((t) => t.id.toLowerCase() === e || t.label.toLowerCase() === e);
78
+ }, [R, N]), U = n.useRef(null);
79
+ return n.useEffect(() => {
80
+ U.current !== H && (U.current = H, p?.(H));
81
+ }, [H, p]), /* @__PURE__ */ i(a.Root, {
82
+ items: N,
83
+ value: L,
84
+ onValueChange: (e) => {
85
+ let t = e?.id ?? "";
86
+ s === void 0 && F(t), l?.(t);
87
+ },
88
+ inputValue: R,
89
+ onInputValueChange: (e) => z(e),
90
+ filter: m,
91
+ limit: h,
92
+ itemToStringLabel: (e) => e.label,
93
+ itemToStringValue: (e) => e.id,
94
+ isItemEqualToValue: (e, t) => e.id === t.id,
95
+ disabled: _,
96
+ readOnly: v,
97
+ required: y,
98
+ name: b,
99
+ defaultOpen: O,
100
+ open: k,
101
+ onOpenChange: (e) => A?.(e),
102
+ children: [/* @__PURE__ */ i("div", {
103
+ "data-slot": "timezone-picker",
104
+ className: "relative w-full",
105
+ children: [/* @__PURE__ */ r(t, {
106
+ "data-slot": "timezone-picker-icon",
107
+ "aria-hidden": "true",
108
+ className: "pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground"
109
+ }), /* @__PURE__ */ r(a.Input, {
110
+ ref: j,
111
+ id: x,
112
+ "data-slot": "timezone-picker-input",
113
+ "data-unknown-zone": H ? void 0 : "",
114
+ placeholder: M.placeholder,
115
+ "aria-label": S,
116
+ "aria-labelledby": C,
117
+ "aria-describedby": w,
118
+ "aria-invalid": T,
119
+ className: e("flex h-9 w-full rounded-md border border-input bg-transparent py-1 pl-9 pr-3 text-sm shadow-sm", "transition-[color,background-color,border-color,box-shadow] duration-control", "placeholder:text-muted-foreground hover:border-ring/60", "focus-ring", "aria-invalid:border-destructive", "disabled:cursor-not-allowed disabled:opacity-50", E)
120
+ })]
121
+ }), /* @__PURE__ */ r(a.Portal, { children: /* @__PURE__ */ r(a.Positioner, {
122
+ side: "bottom",
123
+ align: "start",
124
+ sideOffset: 4,
125
+ className: "z-[var(--z-select)]",
126
+ children: /* @__PURE__ */ i(a.Popup, {
127
+ "data-slot": "timezone-picker-popup",
128
+ className: e("max-h-[min(18rem,var(--available-height))] w-[var(--anchor-width)] min-w-[12rem] overflow-y-auto", "rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-hidden", "origin-[var(--transform-origin)] data-[ending-style]:animate-out data-[ending-style]:fade-out-0 data-[open]:animate-in data-[open]:fade-in-0", D),
129
+ children: [/* @__PURE__ */ r(a.Empty, {
130
+ "data-slot": "timezone-picker-empty",
131
+ className: "px-2 py-1.5 text-sm text-muted-foreground empty:hidden",
132
+ children: M.empty
133
+ }), /* @__PURE__ */ r(a.List, {
134
+ "data-slot": "timezone-picker-list",
135
+ "aria-label": M.list,
136
+ children: (t) => /* @__PURE__ */ i(a.Item, {
137
+ value: t,
138
+ "data-slot": "timezone-picker-item",
139
+ className: e("flex w-full cursor-default select-none items-center gap-3 rounded-sm px-2 py-1.5 text-sm outline-none", "data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground", "data-[selected]:font-medium"),
140
+ children: [/* @__PURE__ */ r("span", {
141
+ "data-slot": "timezone-picker-item-label",
142
+ className: "truncate",
143
+ children: t.label
144
+ }), /* @__PURE__ */ r("span", {
145
+ "data-slot": "timezone-picker-item-offset",
146
+ className: "ml-auto shrink-0 text-xs tabular-nums text-muted-foreground",
147
+ children: t.offset
148
+ })]
149
+ }, t.id)
150
+ })]
151
+ })
152
+ }) })]
153
+ });
154
+ }
155
+ //#endregion
156
+ export { h as TimezonePicker, u as listTimezones };
157
+
158
+ //# sourceMappingURL=timezone-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timezone-picker.js","names":[],"sources":["../../../src/components/forms/timezone-picker.tsx"],"mappings":"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"}