@gusnips/react 0.1.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 (138) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +180 -0
  3. package/dist/api-client.d.ts +139 -0
  4. package/dist/api-client.d.ts.map +1 -0
  5. package/dist/api-client.js +133 -0
  6. package/dist/api-client.js.map +1 -0
  7. package/dist/api-error.d.ts +45 -0
  8. package/dist/api-error.d.ts.map +1 -0
  9. package/dist/api-error.js +52 -0
  10. package/dist/api-error.js.map +1 -0
  11. package/dist/auth-store.d.ts +49 -0
  12. package/dist/auth-store.d.ts.map +1 -0
  13. package/dist/auth-store.js +27 -0
  14. package/dist/auth-store.js.map +1 -0
  15. package/dist/chunk-reload.d.ts +61 -0
  16. package/dist/chunk-reload.d.ts.map +1 -0
  17. package/dist/chunk-reload.js +92 -0
  18. package/dist/chunk-reload.js.map +1 -0
  19. package/dist/cn.d.ts +13 -0
  20. package/dist/cn.d.ts.map +1 -0
  21. package/dist/cn.js +16 -0
  22. package/dist/cn.js.map +1 -0
  23. package/dist/describe-error.d.ts +74 -0
  24. package/dist/describe-error.d.ts.map +1 -0
  25. package/dist/describe-error.js +70 -0
  26. package/dist/describe-error.js.map +1 -0
  27. package/dist/error-boundary.d.ts +61 -0
  28. package/dist/error-boundary.d.ts.map +1 -0
  29. package/dist/error-boundary.js +32 -0
  30. package/dist/error-boundary.js.map +1 -0
  31. package/dist/guards.d.ts +65 -0
  32. package/dist/guards.d.ts.map +1 -0
  33. package/dist/guards.js +52 -0
  34. package/dist/guards.js.map +1 -0
  35. package/dist/hydrate.d.ts +30 -0
  36. package/dist/hydrate.d.ts.map +1 -0
  37. package/dist/hydrate.js +25 -0
  38. package/dist/hydrate.js.map +1 -0
  39. package/dist/i18n.d.ts +70 -0
  40. package/dist/i18n.d.ts.map +1 -0
  41. package/dist/i18n.js +66 -0
  42. package/dist/i18n.js.map +1 -0
  43. package/dist/index.d.ts +12 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +31 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/prerender-contract.d.ts +30 -0
  48. package/dist/prerender-contract.d.ts.map +1 -0
  49. package/dist/prerender-contract.js +30 -0
  50. package/dist/prerender-contract.js.map +1 -0
  51. package/dist/query.d.ts +28 -0
  52. package/dist/query.d.ts.map +1 -0
  53. package/dist/query.js +51 -0
  54. package/dist/query.js.map +1 -0
  55. package/dist/sse.d.ts +64 -0
  56. package/dist/sse.d.ts.map +1 -0
  57. package/dist/sse.js +105 -0
  58. package/dist/sse.js.map +1 -0
  59. package/dist/states.d.ts +69 -0
  60. package/dist/states.d.ts.map +1 -0
  61. package/dist/states.js +2 -0
  62. package/dist/states.js.map +1 -0
  63. package/dist/ui/class-name.d.ts +16 -0
  64. package/dist/ui/class-name.d.ts.map +1 -0
  65. package/dist/ui/class-name.js +13 -0
  66. package/dist/ui/class-name.js.map +1 -0
  67. package/dist/ui/combobox.d.ts +81 -0
  68. package/dist/ui/combobox.d.ts.map +1 -0
  69. package/dist/ui/combobox.js +70 -0
  70. package/dist/ui/combobox.js.map +1 -0
  71. package/dist/ui/dialog.d.ts +39 -0
  72. package/dist/ui/dialog.d.ts.map +1 -0
  73. package/dist/ui/dialog.js +32 -0
  74. package/dist/ui/dialog.js.map +1 -0
  75. package/dist/ui/drawer.d.ts +63 -0
  76. package/dist/ui/drawer.d.ts.map +1 -0
  77. package/dist/ui/drawer.js +55 -0
  78. package/dist/ui/drawer.js.map +1 -0
  79. package/dist/ui/glyphs.d.ts +4 -0
  80. package/dist/ui/glyphs.d.ts.map +1 -0
  81. package/dist/ui/glyphs.js +20 -0
  82. package/dist/ui/glyphs.js.map +1 -0
  83. package/dist/ui/index.d.ts +9 -0
  84. package/dist/ui/index.d.ts.map +1 -0
  85. package/dist/ui/index.js +48 -0
  86. package/dist/ui/index.js.map +1 -0
  87. package/dist/ui/input.d.ts +45 -0
  88. package/dist/ui/input.d.ts.map +1 -0
  89. package/dist/ui/input.js +108 -0
  90. package/dist/ui/input.js.map +1 -0
  91. package/dist/ui/menu.d.ts +70 -0
  92. package/dist/ui/menu.d.ts.map +1 -0
  93. package/dist/ui/menu.js +85 -0
  94. package/dist/ui/menu.js.map +1 -0
  95. package/dist/ui/select.d.ts +69 -0
  96. package/dist/ui/select.d.ts.map +1 -0
  97. package/dist/ui/select.js +48 -0
  98. package/dist/ui/select.js.map +1 -0
  99. package/dist/ui/tabs.d.ts +52 -0
  100. package/dist/ui/tabs.d.ts.map +1 -0
  101. package/dist/ui/tabs.js +62 -0
  102. package/dist/ui/tabs.js.map +1 -0
  103. package/package.json +124 -0
  104. package/src/api-client.test.ts +256 -0
  105. package/src/api-client.ts +305 -0
  106. package/src/api-error.ts +60 -0
  107. package/src/auth-store.ts +59 -0
  108. package/src/chunk-reload.test.ts +46 -0
  109. package/src/chunk-reload.ts +94 -0
  110. package/src/cn.ts +16 -0
  111. package/src/describe-error.test.ts +102 -0
  112. package/src/describe-error.ts +140 -0
  113. package/src/error-boundary.tsx +88 -0
  114. package/src/guards.tsx +99 -0
  115. package/src/hydrate.ts +36 -0
  116. package/src/i18n.test.ts +68 -0
  117. package/src/i18n.ts +101 -0
  118. package/src/index.ts +63 -0
  119. package/src/prerender-contract.ts +31 -0
  120. package/src/query.test.ts +54 -0
  121. package/src/query.ts +79 -0
  122. package/src/sse.test.ts +50 -0
  123. package/src/sse.ts +117 -0
  124. package/src/states.ts +71 -0
  125. package/src/ui/class-name.ts +23 -0
  126. package/src/ui/combobox.test.ts +32 -0
  127. package/src/ui/combobox.tsx +242 -0
  128. package/src/ui/dialog.tsx +87 -0
  129. package/src/ui/drawer.tsx +161 -0
  130. package/src/ui/focus.test.ts +82 -0
  131. package/src/ui/glyphs.tsx +35 -0
  132. package/src/ui/index.ts +145 -0
  133. package/src/ui/input.test.ts +22 -0
  134. package/src/ui/input.tsx +181 -0
  135. package/src/ui/menu.test.ts +73 -0
  136. package/src/ui/menu.tsx +186 -0
  137. package/src/ui/select.tsx +191 -0
  138. package/src/ui/tabs.tsx +95 -0
@@ -0,0 +1,48 @@
1
+ // @gusnips/react/ui — the Base UI wrappers, behind a subpath so an app on a different
2
+ // primitive library (or none) never resolves `@base-ui/react`.
3
+ //
4
+ // SEVEN, out of twenty-seven in the donor. The rest added a class string and nothing else:
5
+ // Base UI already does scroll lock, focus trap, escape, outside-dismiss, focus return, roving
6
+ // focus and typeahead, so a wrapper earns its place for one of three reasons only —
7
+ // composition a caller cannot skip (a dialog with no scrim is unwritable), a required a11y
8
+ // prop expressed as a TYPE (no close button without its label, no tab rail without a name),
9
+ // or an encoded gotcha (the drawer's four, the menu's caption, the tab rail's padding).
10
+ //
11
+ // These are not styled components. The classes here are the ones that make the primitive
12
+ // work — position, stacking, overflow, an opaque surface, the transition hooks Base UI
13
+ // animates through. Padding, radius, shadow and type are the product's, and every part takes
14
+ // a `className` that wins over ours.
15
+ //
16
+ // Nothing here draws a focus ring, and nothing removes one outside a popup container. The
17
+ // app's single `:focus-visible` outline lives in `@gusnips/tokens`' base layer; a wrapper
18
+ // that swaps it for a `ring-*` box-shadow looks identical in every normal browser and
19
+ // vanishes under `forced-colors: active`. `focus.test.ts` pins that.
20
+ //
21
+ // The stacking contract, in one place, because the parts are all portalled to `<body>` and
22
+ // nesting decides nothing:
23
+ // z-40 drawer backdrop
24
+ // z-50 the overlay layer — dialog, drawer. A tie on purpose: a dialog opened from inside
25
+ // a drawer must paint over it, and at a tie the later portal wins.
26
+ // z-60 popups anchored inside that layer — a select, combobox or menu opened in a dialog.
27
+ // Every one of the three takes `positionerProps={{ className: "z-…" }}` to move.
28
+ export { mergeClassName } from "./class-name.js";
29
+ export { Dialog, DialogBackdrop, DialogClose, DialogContent, DialogDescription, DialogPopup, DialogPortal, DialogTitle, DialogTrigger, } from "./dialog.js";
30
+ export { Drawer, DrawerBackdrop, DrawerClose, DrawerContent, DrawerDescription, DrawerHandle, DrawerPopup, DrawerPortal, DrawerRoot, DrawerSwipeArea, DrawerTitle, DrawerTrigger, DrawerViewport, } from "./drawer.js";
31
+ // `SelectGroupLabel` and `ComboboxGroupLabel` below ARE exported, and the menu's is not. The
32
+ // hazard is identical — all three read their group's context and throw when written outside
33
+ // it, reported in production as `Base UI error #56` for select, `#18` for combobox and `#31`
34
+ // for the menu — so the difference is a decision, not an oversight.
35
+ //
36
+ // It turns on what the wrapper already covers. `MenuGroup` composes the caption as a `label`
37
+ // prop, so the raw part has no job left and withholding it is free. `Select` and `Combobox`
38
+ // take a flat option list and cannot express a grouped one at all, so these are the escape
39
+ // hatch for that shape — and a caller reaching for `SelectGroup` is writing its
40
+ // `SelectGroupLabel` in the same breath, which is the arrangement that works.
41
+ export { Select, SelectGroup, SelectGroupLabel, SelectIcon, SelectItem, SelectItemIndicator, SelectItemText, SelectList, SelectPopup, SelectPortal, SelectPositioner, SelectRoot, SelectSeparator, SelectTrigger, SelectValue, } from "./select.js";
42
+ export { Combobox, comboboxInputValue, ComboboxClear, ComboboxEmpty, ComboboxGroup, ComboboxGroupLabel, ComboboxInput, ComboboxInputGroup, ComboboxItem, ComboboxList, ComboboxPopup, ComboboxPortal, ComboboxPositioner, ComboboxRoot, ComboboxTrigger, } from "./combobox.js";
43
+ export { Input, textareaHeight, } from "./input.js";
44
+ // No `MenuGroupLabel`, and no raw `Group` / `RadioGroup`. That is the whole guarantee — see
45
+ // the note at the top of menu.tsx.
46
+ export { Menu, MenuArrow, MenuBackdrop, MenuCheckboxItem, MenuCheckboxItemIndicator, MenuContent, MenuGroup, MenuItem, MenuLinkItem, MenuPopup, MenuPortal, MenuPositioner, MenuRadioGroup, MenuRadioItem, MenuRadioItemIndicator, MenuSeparator, MenuSubmenuRoot, MenuSubmenuTrigger, MenuTrigger, } from "./menu.js";
47
+ export { Tab, TabIndicator, TabList, TabPanel, Tabs } from "./tabs.js";
48
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,+DAA+D;AAC/D,EAAE;AACF,2FAA2F;AAC3F,8FAA8F;AAC9F,oFAAoF;AACpF,2FAA2F;AAC3F,4FAA4F;AAC5F,wFAAwF;AACxF,EAAE;AACF,yFAAyF;AACzF,uFAAuF;AACvF,6FAA6F;AAC7F,qCAAqC;AACrC,EAAE;AACF,0FAA0F;AAC1F,0FAA0F;AAC1F,sFAAsF;AACtF,qEAAqE;AACrE,EAAE;AACF,2FAA2F;AAC3F,2BAA2B;AAC3B,0BAA0B;AAC1B,4FAA4F;AAC5F,2EAA2E;AAC3E,6FAA6F;AAC7F,yFAAyF;AAEzF,OAAO,EAAE,cAAc,EAAuB,MAAM,iBAAiB,CAAC;AAEtE,OAAO,EACL,MAAM,EACN,cAAc,EACd,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,WAAW,EACX,YAAY,EACZ,WAAW,EACX,aAAa,GAEd,MAAM,aAAc,CAAC;AAEtB,OAAO,EACL,MAAM,EACN,cAAc,EACd,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,UAAU,EACV,eAAe,EACf,WAAW,EACX,aAAa,EACb,cAAc,GAGf,MAAM,aAAc,CAAC;AAEtB,6FAA6F;AAC7F,4FAA4F;AAC5F,6FAA6F;AAC7F,oEAAoE;AACpE,EAAE;AACF,6FAA6F;AAC7F,4FAA4F;AAC5F,2FAA2F;AAC3F,gFAAgF;AAChF,8EAA8E;AAC9E,OAAO,EACL,MAAM,EACN,WAAW,EACX,gBAAgB,EAChB,UAAU,EACV,UAAU,EACV,mBAAmB,EACnB,cAAc,EACd,UAAU,EACV,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,aAAa,EACb,WAAW,GAGZ,MAAM,aAAc,CAAC;AAEtB,OAAO,EACL,QAAQ,EACR,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,eAAe,GAEhB,MAAM,eAAgB,CAAC;AAExB,OAAO,EACL,KAAK,EACL,cAAc,GAIf,MAAM,YAAa,CAAC;AAErB,4FAA4F;AAC5F,mCAAmC;AACnC,OAAO,EACL,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,gBAAgB,EAChB,yBAAyB,EACzB,WAAW,EACX,SAAS,EACT,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,UAAU,EACV,cAAc,EACd,cAAc,EACd,aAAa,EACb,sBAAsB,EACtB,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,WAAW,GAGZ,MAAM,WAAY,CAAC;AAEpB,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAqB,MAAM,WAAY,CAAC"}
@@ -0,0 +1,45 @@
1
+ import { Input as Primitive } from "@base-ui/react/input";
2
+ import * as React from "react";
3
+ /**
4
+ * The inline height an autosizing textarea should carry, or `""` for "leave it to CSS".
5
+ *
6
+ * ponytail: this whole observer is here only until `field-sizing: content` is baseline —
7
+ * one CSS line does the same job. Ceiling: it does not exist in every browser we support yet.
8
+ */
9
+ export declare function textareaHeight(clientWidth: number, scrollHeight: number): string;
10
+ type FieldChrome = {
11
+ label?: React.ReactNode;
12
+ /**
13
+ * The message under the field. Its presence is what wires `aria-invalid` and
14
+ * `aria-describedby`, so a field that looks wrong also announces that it is.
15
+ */
16
+ error?: React.ReactNode;
17
+ /**
18
+ * Classes for the control. A plain string here, not Base UI's state-callback form: this
19
+ * component renders a native `<textarea>` for the multiline case, which has no Base UI
20
+ * state to call back with, and one field type styling differently from the other would be
21
+ * a worse trade than the callback is worth.
22
+ */
23
+ className?: string;
24
+ wrapperClassName?: string;
25
+ labelClassName?: string;
26
+ errorClassName?: string;
27
+ };
28
+ export type InputProps = FieldChrome & Omit<Primitive.Props, "className"> & {
29
+ multiline?: false;
30
+ };
31
+ export type MultilineInputProps = FieldChrome & Omit<React.ComponentProps<"textarea">, "className"> & {
32
+ multiline: true;
33
+ };
34
+ export type FieldProps = InputProps | MultilineInputProps;
35
+ /**
36
+ * A field with its label, its error, and the aria that ties the three together.
37
+ *
38
+ * That wiring is the reason this exists: `htmlFor`/`id`, `aria-describedby` pointing at the
39
+ * error, `aria-invalid` set from the same prop that draws the red border, and an id generated
40
+ * when the caller has not given one. Every one of those is a line somebody forgets, and
41
+ * forgetting it is invisible until a screen reader reads the field with no name and no reason.
42
+ */
43
+ export declare function Input(props: FieldProps): React.JSX.Element;
44
+ export {};
45
+ //# sourceMappingURL=input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/ui/input.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAU/B;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAGhF;AAED,KAAK,WAAW,GAAG;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,WAAW,CAAC,GAAG;IAAE,SAAS,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAElG,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAC3C,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,WAAW,CAAC,GAAG;IAAE,SAAS,EAAE,IAAI,CAAA;CAAE,CAAC;AAE5E,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,mBAAmB,CAAC;AAiC1D;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,qBAwCtC"}
@@ -0,0 +1,108 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Input as Primitive } from "@base-ui/react/input";
3
+ import * as React from "react";
4
+ import { cn } from "../cn.js";
5
+ /** Below this width a textarea wraps almost every word, so `scrollHeight` jumps straight to
6
+ * the cap and the field opens to full height for one word. Under it, let CSS decide. */
7
+ const TEXTAREA_MIN_AUTO_WIDTH = 180;
8
+ /** Past this the textarea stops growing and starts scrolling, so a long paste cannot push
9
+ * the submit button off the screen. */
10
+ const TEXTAREA_MAX_HEIGHT = 160;
11
+ /**
12
+ * The inline height an autosizing textarea should carry, or `""` for "leave it to CSS".
13
+ *
14
+ * ponytail: this whole observer is here only until `field-sizing: content` is baseline —
15
+ * one CSS line does the same job. Ceiling: it does not exist in every browser we support yet.
16
+ */
17
+ export function textareaHeight(clientWidth, scrollHeight) {
18
+ if (clientWidth < TEXTAREA_MIN_AUTO_WIDTH)
19
+ return "";
20
+ return `${Math.min(scrollHeight, TEXTAREA_MAX_HEIGHT)}px`;
21
+ }
22
+ function withoutChrome(props) {
23
+ const { label: _label, error: _error, className: _className, wrapperClassName: _wrapperClassName, labelClassName: _labelClassName, errorClassName: _errorClassName, multiline: _multiline, ...rest } = props;
24
+ return rest;
25
+ }
26
+ /**
27
+ * `border-input` rather than `border-border`: a control's boundary is a non-text contrast
28
+ * target and has to clear 3:1 (WCAG 1.4.11), which the hairline between two panels does not.
29
+ *
30
+ * `text-base` up to `md`: iOS Safari zooms the whole page in when a field it focuses has type
31
+ * smaller than 16px, and it does not zoom back out. The pair is the fix, not a size choice.
32
+ *
33
+ * No focus style here, deliberately. The app's one ring is the `:focus-visible` outline in
34
+ * `@gusnips/tokens`' base layer, and a control that swaps it for `outline-none` plus a
35
+ * `ring-*` gains nothing and loses forced-colors mode: a ring is a `box-shadow`, which the UA
36
+ * forces to `none` under Windows High Contrast, while an outline survives and takes a system
37
+ * colour. The swap therefore deletes the focus indicator for exactly the people who need it
38
+ * most — and it is invisible in review, because it looks correct in every normal browser.
39
+ */
40
+ const controlClasses = "w-full min-w-0 rounded-md border border-input bg-background px-3 py-2 text-base text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive md:text-sm";
41
+ /**
42
+ * A field with its label, its error, and the aria that ties the three together.
43
+ *
44
+ * That wiring is the reason this exists: `htmlFor`/`id`, `aria-describedby` pointing at the
45
+ * error, `aria-invalid` set from the same prop that draws the red border, and an id generated
46
+ * when the caller has not given one. Every one of those is a line somebody forgets, and
47
+ * forgetting it is invisible until a screen reader reads the field with no name and no reason.
48
+ */
49
+ export function Input(props) {
50
+ const { label, error, className, wrapperClassName, labelClassName, errorClassName } = props;
51
+ const generatedId = React.useId();
52
+ const fieldId = props.id ?? (label || error ? generatedId : undefined);
53
+ const errorId = error ? `${fieldId ?? generatedId}-error` : undefined;
54
+ // Appended, never replaced: a caller describing the field with a hint of their own keeps it.
55
+ const describedBy = errorId
56
+ ? [props["aria-describedby"], errorId].filter(Boolean).join(" ")
57
+ : props["aria-describedby"];
58
+ const shared = {
59
+ id: fieldId,
60
+ "aria-invalid": error ? true : props["aria-invalid"],
61
+ "aria-describedby": describedBy,
62
+ className: cn(controlClasses, props.multiline && "resize-none", className),
63
+ };
64
+ return (_jsxs("div", { className: cn("w-full", wrapperClassName), children: [label ? (_jsx("label", { htmlFor: fieldId, className: cn("mb-1.5 block text-sm", labelClassName), children: label })) : null, props.multiline ? (_jsx(AutosizeTextarea, { ...withoutChrome(props), ...shared })) : (_jsx(Primitive, { ...withoutChrome(props), ...shared })), error ? (_jsx("div", { id: errorId, role: "alert", className: cn("mt-1.5 text-xs text-destructive", errorClassName), children: error })) : null] }));
65
+ }
66
+ /**
67
+ * A textarea that grows with its content and then scrolls.
68
+ *
69
+ * It watches its own SIZE, not just its value: a textarea also reflows when the column it
70
+ * sits in changes width — a drawer opening, a sidebar collapsing, a phone turning — and
71
+ * height set from the old width is either a gap under the text or a scrollbar over two lines.
72
+ */
73
+ function AutosizeTextarea({ ref, onInput, ...rest }) {
74
+ const element = React.useRef(null);
75
+ const setElement = React.useCallback((node) => {
76
+ element.current = node;
77
+ if (typeof ref === "function")
78
+ ref(node);
79
+ else if (ref)
80
+ ref.current = node;
81
+ }, [ref]);
82
+ const fit = React.useCallback(() => {
83
+ const node = element.current;
84
+ if (!node)
85
+ return;
86
+ // Reset first. `scrollHeight` is the content height OR the box height, whichever is
87
+ // larger, so a box already held open at yesterday's height never reports a smaller one —
88
+ // the field would grow and never shrink.
89
+ node.style.height = "auto";
90
+ node.style.height = textareaHeight(node.clientWidth, node.scrollHeight);
91
+ }, []);
92
+ React.useEffect(() => {
93
+ fit();
94
+ }, [fit, rest.value, rest.placeholder]);
95
+ React.useEffect(() => {
96
+ const node = element.current;
97
+ if (!node || typeof ResizeObserver === "undefined")
98
+ return undefined;
99
+ const observer = new ResizeObserver(fit);
100
+ observer.observe(node);
101
+ return () => observer.disconnect();
102
+ }, [fit]);
103
+ return (_jsx("textarea", { ...rest, ref: setElement, onInput: (event) => {
104
+ fit();
105
+ onInput?.(event);
106
+ } }));
107
+ }
108
+ //# sourceMappingURL=input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.js","sourceRoot":"","sources":["../../src/ui/input.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAE9B;yFACyF;AACzF,MAAM,uBAAuB,GAAG,GAAG,CAAC;AACpC;wCACwC;AACxC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,WAAmB,EAAE,YAAoB;IACtE,IAAI,WAAW,GAAG,uBAAuB;QAAE,OAAO,EAAE,CAAC;IACrD,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,mBAAmB,CAAC,IAAI,CAAC;AAC5D,CAAC;AA4BD,SAAS,aAAa,CAAkD,KAAQ;IAC9E,MAAM,EACJ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,UAAU,EACrB,gBAAgB,EAAE,iBAAiB,EACnC,cAAc,EAAE,eAAe,EAC/B,cAAc,EAAE,eAAe,EAC/B,SAAS,EAAE,UAAU,EACrB,GAAG,IAAI,EACR,GAAG,KAAK,CAAC;IACV,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,cAAc,GAClB,+NAA+N,CAAC;AAElO;;;;;;;GAOG;AACH,MAAM,UAAU,KAAK,CAAC,KAAiB;IACrC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;IAC5F,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;IAClC,MAAM,OAAO,GAAG,KAAK,CAAC,EAAE,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACvE,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,WAAW,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IACtE,6FAA6F;IAC7F,MAAM,WAAW,GAAG,OAAO;QACzB,CAAC,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAChE,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAE9B,MAAM,MAAM,GAAG;QACb,EAAE,EAAE,OAAO;QACX,cAAc,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC;QACpD,kBAAkB,EAAE,WAAW;QAC/B,SAAS,EAAE,EAAE,CAAC,cAAc,EAAE,KAAK,CAAC,SAAS,IAAI,aAAa,EAAE,SAAS,CAAC;KAC3E,CAAC;IAEF,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,QAAQ,EAAE,gBAAgB,CAAC,aAC3C,KAAK,CAAC,CAAC,CAAC,CACP,gBAAO,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,CAAC,sBAAsB,EAAE,cAAc,CAAC,YAC3E,KAAK,GACA,CACT,CAAC,CAAC,CAAC,IAAI,EACP,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CACjB,KAAC,gBAAgB,OAAK,aAAa,CAAC,KAAK,CAAC,KAAM,MAAM,GAAI,CAC3D,CAAC,CAAC,CAAC,CACF,KAAC,SAAS,OAAK,aAAa,CAAC,KAAK,CAAC,KAAM,MAAM,GAAI,CACpD,EACA,KAAK,CAAC,CAAC,CAAC,CACP,cACE,EAAE,EAAE,OAAO,EACX,IAAI,EAAC,OAAO,EACZ,SAAS,EAAE,EAAE,CAAC,iCAAiC,EAAE,cAAc,CAAC,YAE/D,KAAK,GACF,CACP,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAS,gBAAgB,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,IAAI,EAAoC;IACnF,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAA6B,IAAI,CAAC,CAAC;IAE/D,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,CAClC,CAAC,IAAgC,EAAE,EAAE;QACnC,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;QACvB,IAAI,OAAO,GAAG,KAAK,UAAU;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC;aACpC,IAAI,GAAG;YAAE,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC;IACnC,CAAC,EACD,CAAC,GAAG,CAAC,CACN,CAAC;IAEF,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QACjC,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;QAC7B,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,oFAAoF;QACpF,yFAAyF;QACzF,yCAAyC;QACzC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;QAC3B,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;IAC1E,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,GAAG,EAAE,CAAC;IACR,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;IAExC,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;QAC7B,IAAI,CAAC,IAAI,IAAI,OAAO,cAAc,KAAK,WAAW;YAAE,OAAO,SAAS,CAAC;QACrE,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,GAAG,CAAC,CAAC;QACzC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvB,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;IACrC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAEV,OAAO,CACL,sBACM,IAAI,EACR,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACjB,GAAG,EAAE,CAAC;YACN,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;QACnB,CAAC,GACD,CACH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { Menu as Primitive } from "@base-ui/react/menu";
2
+ import type { ReactNode } from "react";
3
+ export declare const Menu: <Payload>(props: Primitive.Root.Props<Payload>) => React.JSX.Element;
4
+ export declare const MenuTrigger: Primitive.Trigger;
5
+ export declare const MenuPortal: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").ContextMenuPortalProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
6
+ export declare const MenuPositioner: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").MenuPositionerProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
7
+ export declare const MenuPopup: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").ContextMenuPopupProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
8
+ export declare const MenuBackdrop: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").ContextMenuBackdropProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
9
+ export declare const MenuArrow: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").ContextMenuArrowProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
10
+ export declare const MenuLinkItem: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").ContextMenuLinkItemProps, "ref"> & import("react").RefAttributes<Element>>;
11
+ export declare const MenuCheckboxItem: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").ContextMenuCheckboxItemProps, "ref"> & import("react").RefAttributes<HTMLElement>>;
12
+ export declare const MenuCheckboxItemIndicator: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").ContextMenuCheckboxItemIndicatorProps, "ref"> & import("react").RefAttributes<HTMLSpanElement>>;
13
+ export declare const MenuRadioItemIndicator: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").ContextMenuRadioItemIndicatorProps, "ref"> & import("react").RefAttributes<HTMLSpanElement>>;
14
+ export declare const MenuSubmenuRoot: typeof Primitive.SubmenuRoot;
15
+ export declare const MenuSubmenuTrigger: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").ContextMenuSubmenuTriggerProps, "ref"> & import("react").RefAttributes<HTMLElement>>;
16
+ export type MenuContentProps = Primitive.Popup.Props & {
17
+ /**
18
+ * Accessible name for the menu. Usually leave this out: Base UI already points the popup's
19
+ * `aria-labelledby` at whichever trigger opened it, so a name here REPLACES the words on
20
+ * the button a screen reader just read. Pass it only when the menu is about something the
21
+ * trigger does not say.
22
+ */
23
+ label?: string;
24
+ /** Where the portal mounts. Defaults to `<body>`. */
25
+ container?: Primitive.Portal.Props["container"];
26
+ /**
27
+ * Positioner props — `side`, `align`, `sideOffset`, `collisionPadding`, and the stacking
28
+ * order. The default `z-[60]` puts the menu above the overlay layer (z-50: dialog, drawer)
29
+ * so a menu opened inside a dialog is not painted behind it; raise it with
30
+ * `positionerProps={{ className: "z-[80]" }}` if the app stacks something higher.
31
+ */
32
+ positionerProps?: Primitive.Positioner.Props;
33
+ };
34
+ /**
35
+ * A dropdown of decisions — the account menu, a row's actions, a picker. Portal, positioner
36
+ * and popup composed once, for the same reason the dialog's are: the arrangement is not
37
+ * optional and getting it wrong throws a number.
38
+ *
39
+ * Roving focus, typeahead, escape, outside-dismiss and focus return all come from Base UI.
40
+ * Nothing here re-implements any of them.
41
+ */
42
+ export declare function MenuContent({ className, children, label, container, positionerProps, ...rest }: MenuContentProps): import("react").JSX.Element;
43
+ export declare function MenuItem({ className, ...rest }: Primitive.Item.Props): import("react").JSX.Element;
44
+ export declare function MenuSeparator({ className, ...rest }: Primitive.Separator.Props): import("react").JSX.Element;
45
+ /** The caption a group is named by, or nothing. Never a part you place yourself. */
46
+ type MenuGroupCaption = {
47
+ label?: string;
48
+ };
49
+ /**
50
+ * A set of related items with an optional caption.
51
+ *
52
+ * The caption is a PROP because that is the only arrangement that cannot be got wrong: it
53
+ * lands inside the group, which is both what stops Base UI throwing and what gives the set
54
+ * its `aria-labelledby`. It is also not focusable, so it never eats an arrow-key stop.
55
+ * Optional, because a group whose trigger already says what it is would only repeat itself.
56
+ */
57
+ export declare function MenuGroup({ label, children, ...rest }: Primitive.Group.Props & MenuGroupCaption): import("react").JSX.Element;
58
+ /** A set of choices — theme, locale, one axis of a filter. Same caption rule as `MenuGroup`. */
59
+ export declare function MenuRadioGroup({ label, children, ...rest }: Primitive.RadioGroup.Props & MenuGroupCaption): import("react").JSX.Element;
60
+ export type MenuRadioItemProps = Primitive.RadioItem.Props & {
61
+ /** Mark for the chosen item. */
62
+ indicator?: ReactNode;
63
+ };
64
+ /**
65
+ * One choice in a set. The mark keeps a fixed leading column whether or not it is showing, so
66
+ * the labels do not shift sideways when the selection moves.
67
+ */
68
+ export declare function MenuRadioItem({ className, children, indicator, ...rest }: MenuRadioItemProps): import("react").JSX.Element;
69
+ export {};
70
+ //# sourceMappingURL=menu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../../src/ui/menu.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAavC,eAAO,MAAM,IAAI,sEAAiB,CAAC;AACnC,eAAO,MAAM,WAAW,mBAAoB,CAAC;AAC7C,eAAO,MAAM,UAAU,yJAAmB,CAAC;AAC3C,eAAO,MAAM,cAAc,sJAAuB,CAAC;AACnD,eAAO,MAAM,SAAS,wJAAkB,CAAC;AACzC,eAAO,MAAM,YAAY,2JAAqB,CAAC;AAC/C,eAAO,MAAM,SAAS,wJAAkB,CAAC;AACzC,eAAO,MAAM,YAAY,oJAAqB,CAAC;AAC/C,eAAO,MAAM,gBAAgB,4JAAyB,CAAC;AACvD,eAAO,MAAM,yBAAyB,yKAAkC,CAAC;AACzE,eAAO,MAAM,sBAAsB,sKAA+B,CAAC;AACnE,eAAO,MAAM,eAAe,8BAAwB,CAAC;AACrD,eAAO,MAAM,kBAAkB,8JAA2B,CAAC;AAE3D,MAAM,MAAM,gBAAgB,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,GAAG;IACrD;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,SAAS,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAChD;;;;;OAKG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC;CAC9C,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,EAC1B,SAAS,EACT,QAAQ,EACR,KAAK,EACL,SAAS,EACT,eAAe,EACf,GAAG,IAAI,EACR,EAAE,gBAAgB,+BA6BlB;AAcD,wBAAgB,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,SAAS,CAAC,IAAI,CAAC,KAAK,+BAEpE;AAED,wBAAgB,aAAa,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,SAAS,CAAC,SAAS,CAAC,KAAK,+BAI9E;AAED,oFAAoF;AACpF,KAAK,gBAAgB,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAa3C;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,SAAS,CAAC,KAAK,CAAC,KAAK,GAAG,gBAAgB,+BAS/F;AAED,gGAAgG;AAChG,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,QAAQ,EACR,GAAG,IAAI,EACR,EAAE,SAAS,CAAC,UAAU,CAAC,KAAK,GAAG,gBAAgB,+BAS/C;AAED,MAAM,MAAM,kBAAkB,GAAG,SAAS,CAAC,SAAS,CAAC,KAAK,GAAG;IAC3D,gCAAgC;IAChC,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,kBAAkB,+BAY5F"}
@@ -0,0 +1,85 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Menu as Primitive } from "@base-ui/react/menu";
3
+ import { mergeClassName } from "./class-name.js";
4
+ import { CheckGlyph } from "./glyphs.js";
5
+ // The parts — every one of them EXCEPT `GroupLabel`, and every one except `Group` and
6
+ // `RadioGroup`, which are wrapped below. That omission is not tidying: it is the only thing
7
+ // that actually prevents the bug. `Menu.GroupLabel` reads its group's context, so a caption
8
+ // written as a SIBLING of the group throws at render, and a production build reports that as
9
+ // `Base UI error #31` plus a link — a number to go look up, naming neither the file, nor the
10
+ // part, nor the component it came from. A comment cannot stop that; not shipping the part
11
+ // can. The caption is a prop on the group instead, so the wrong arrangement has nowhere to be
12
+ // written. `Select.GroupLabel` and `Combobox.GroupLabel` throw the same way (#56 and #18) and
13
+ // ARE exported — see the note beside them in index.ts for why that is the right call there.
14
+ export const Menu = Primitive.Root;
15
+ export const MenuTrigger = Primitive.Trigger;
16
+ export const MenuPortal = Primitive.Portal;
17
+ export const MenuPositioner = Primitive.Positioner;
18
+ export const MenuPopup = Primitive.Popup;
19
+ export const MenuBackdrop = Primitive.Backdrop;
20
+ export const MenuArrow = Primitive.Arrow;
21
+ export const MenuLinkItem = Primitive.LinkItem;
22
+ export const MenuCheckboxItem = Primitive.CheckboxItem;
23
+ export const MenuCheckboxItemIndicator = Primitive.CheckboxItemIndicator;
24
+ export const MenuRadioItemIndicator = Primitive.RadioItemIndicator;
25
+ export const MenuSubmenuRoot = Primitive.SubmenuRoot;
26
+ export const MenuSubmenuTrigger = Primitive.SubmenuTrigger;
27
+ /**
28
+ * A dropdown of decisions — the account menu, a row's actions, a picker. Portal, positioner
29
+ * and popup composed once, for the same reason the dialog's are: the arrangement is not
30
+ * optional and getting it wrong throws a number.
31
+ *
32
+ * Roving focus, typeahead, escape, outside-dismiss and focus return all come from Base UI.
33
+ * Nothing here re-implements any of them.
34
+ */
35
+ export function MenuContent({ className, children, label, container, positionerProps, ...rest }) {
36
+ return (_jsx(Primitive.Portal, { container: container, children: _jsx(Primitive.Positioner, { side: "bottom", align: "end", sideOffset: 6, ...positionerProps, className: mergeClassName("z-[60] max-w-[var(--available-width)]", positionerProps?.className), children: _jsx(Primitive.Popup, { "aria-label": label, ...rest, className: mergeClassName("max-h-[var(--available-height)] min-w-[var(--anchor-width)] origin-[var(--transform-origin)] overflow-y-auto border border-border bg-popover text-popover-foreground outline-none transition-[scale,opacity] data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0", className), children: children }) }) }));
37
+ }
38
+ /**
39
+ * Base UI gives the highlighted item a real `tabIndex: 0` and moves DOM focus to it, so a
40
+ * keyboard user gets the app's `:focus-visible` outline for free and `data-highlighted` draws
41
+ * the pointer's highlight. Nothing here suppresses either — a menu row is one of the places
42
+ * the ring is most needed and most often deleted.
43
+ *
44
+ * The coarse-pointer minimum is a 44px row: a thumb misses anything smaller, and a menu is
45
+ * usually a list of one-way actions.
46
+ */
47
+ const itemClasses = "flex cursor-default items-center gap-2 px-3 py-1.5 select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-highlighted:bg-accent data-highlighted:text-accent-foreground [@media(pointer:coarse)]:min-h-11";
48
+ export function MenuItem({ className, ...rest }) {
49
+ return _jsx(Primitive.Item, { ...rest, className: mergeClassName(itemClasses, className) });
50
+ }
51
+ export function MenuSeparator({ className, ...rest }) {
52
+ return (_jsx(Primitive.Separator, { ...rest, className: mergeClassName("my-1 h-px bg-border", className) }));
53
+ }
54
+ const captionClasses = "px-3 pt-2 pb-1 text-muted-foreground";
55
+ // The caption block below is written out TWICE, once per group kind, and that is deliberate.
56
+ // Lifting it into a `caption(label)` helper is the obvious cleanup and it was tried: it moves
57
+ // the `<Primitive.GroupLabel>` out of the `<Primitive.Group>` body it is written inside, and
58
+ // `menu.test.ts` fails immediately — correctly. That guard proves placement by READING THE
59
+ // SOURCE, so an indirection it cannot follow does not just break the test, it retires the
60
+ // guarantee: after the extraction nothing stops a later edit calling `caption()` somewhere that
61
+ // is not inside a group, which is the render-time throw this whole file exists to prevent.
62
+ // Six duplicated lines are the price of a rule a machine can still check. Leave them.
63
+ /**
64
+ * A set of related items with an optional caption.
65
+ *
66
+ * The caption is a PROP because that is the only arrangement that cannot be got wrong: it
67
+ * lands inside the group, which is both what stops Base UI throwing and what gives the set
68
+ * its `aria-labelledby`. It is also not focusable, so it never eats an arrow-key stop.
69
+ * Optional, because a group whose trigger already says what it is would only repeat itself.
70
+ */
71
+ export function MenuGroup({ label, children, ...rest }) {
72
+ return (_jsxs(Primitive.Group, { ...rest, children: [label === undefined ? null : (_jsx(Primitive.GroupLabel, { className: captionClasses, children: label })), children] }));
73
+ }
74
+ /** A set of choices — theme, locale, one axis of a filter. Same caption rule as `MenuGroup`. */
75
+ export function MenuRadioGroup({ label, children, ...rest }) {
76
+ return (_jsxs(Primitive.RadioGroup, { ...rest, children: [label === undefined ? null : (_jsx(Primitive.GroupLabel, { className: captionClasses, children: label })), children] }));
77
+ }
78
+ /**
79
+ * One choice in a set. The mark keeps a fixed leading column whether or not it is showing, so
80
+ * the labels do not shift sideways when the selection moves.
81
+ */
82
+ export function MenuRadioItem({ className, children, indicator, ...rest }) {
83
+ return (_jsxs(Primitive.RadioItem, { ...rest, className: mergeClassName([itemClasses, "data-checked:text-foreground"], className), children: [_jsx("span", { "aria-hidden": "true", className: "grid size-4 shrink-0 place-items-center", children: _jsx(Primitive.RadioItemIndicator, { children: indicator ?? _jsx(CheckGlyph, {}) }) }), children] }));
84
+ }
85
+ //# sourceMappingURL=menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu.js","sourceRoot":"","sources":["../../src/ui/menu.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ import { Select as Primitive } from "@base-ui/react/select";
2
+ import type { ReactNode } from "react";
3
+ import { type StateClassName } from "./class-name.ts";
4
+ export declare const SelectRoot: typeof Primitive.Root;
5
+ export declare const SelectTrigger: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").SelectTriggerProps, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
6
+ export declare const SelectValue: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").SelectValueProps, "ref"> & import("react").RefAttributes<HTMLSpanElement>>;
7
+ export declare const SelectIcon: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").SelectIconProps, "ref"> & import("react").RefAttributes<HTMLSpanElement>>;
8
+ export declare const SelectPortal: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").SelectPortalProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
9
+ export declare const SelectPositioner: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").SelectPositionerProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
10
+ export declare const SelectPopup: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").SelectPopupProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
11
+ export declare const SelectList: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").SelectListProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
12
+ export declare const SelectItem: import("react").NamedExoticComponent<Omit<import("@base-ui/react").SelectItemProps, "ref"> & import("react").RefAttributes<HTMLElement>>;
13
+ export declare const SelectItemIndicator: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").SelectItemIndicatorProps, "ref"> & import("react").RefAttributes<HTMLSpanElement>>;
14
+ export declare const SelectItemText: import("react").NamedExoticComponent<Omit<import("@base-ui/react").SelectItemTextProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
15
+ export declare const SelectGroup: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").SelectGroupProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
16
+ export declare const SelectGroupLabel: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").SelectGroupLabelProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
17
+ export declare const SelectSeparator: import("react").ForwardRefExoticComponent<import("@base-ui/react").SelectSeparatorProps & import("react").RefAttributes<HTMLDivElement>>;
18
+ export interface SelectOption<T extends string = string> {
19
+ value: T;
20
+ label: string;
21
+ disabled?: boolean;
22
+ icon?: ReactNode;
23
+ }
24
+ export type SelectProps<T extends string = string> = Omit<Primitive.Root.Props<T, false>, "children" | "items" | "defaultValue" | "multiple"> & {
25
+ options?: readonly SelectOption<T>[];
26
+ /** Shown while nothing is selected. No default — a default would be English. */
27
+ placeholder?: ReactNode;
28
+ /**
29
+ * Shown instead of the list when `options` is empty. Required, because a popup that opens
30
+ * on nothing is a dead end: this is the line that says why it is empty.
31
+ */
32
+ emptyLabel: ReactNode;
33
+ /**
34
+ * Label of an entry that puts the field back to nothing selected — for an optional
35
+ * question, where "I'd rather not say" has to stay reachable after the first answer.
36
+ * Rendered only once there is a value to take back; picking it calls `onValueChange(null)`.
37
+ */
38
+ clearLabel?: string;
39
+ /** Accessible name for the trigger, when no visible label points at it. */
40
+ label?: string;
41
+ /** Mark that says "this opens". */
42
+ chevron?: ReactNode;
43
+ /** Mark next to the selected option. */
44
+ indicator?: ReactNode;
45
+ /** Classes for the trigger — the button you see when the popup is closed. */
46
+ className?: StateClassName<Primitive.Trigger.State>;
47
+ popupClassName?: StateClassName<Primitive.Popup.State>;
48
+ triggerProps?: Primitive.Trigger.Props;
49
+ /**
50
+ * Positioner props — `align`, `side`, `sideOffset`, `collisionPadding`, and the stacking
51
+ * order. The default `z-[60]` puts the list above the overlay layer (z-50: dialog, drawer);
52
+ * raise it with `positionerProps={{ className: "z-[80]" }}` if the app stacks something
53
+ * higher than a dialog above it.
54
+ */
55
+ positionerProps?: Primitive.Positioner.Props;
56
+ };
57
+ /**
58
+ * A single-choice field. Trigger, portal, positioner, popup and list composed once; `name`,
59
+ * `form`, `required`, `id`, `disabled` and every other root prop pass straight through to
60
+ * Base UI's hidden input, so this submits inside a `<form>` like a native `<select>`.
61
+ *
62
+ * Controlled only, and that is the point of `value ?? null`: React and Base UI both read
63
+ * `value={undefined}` as "uncontrolled", so a caller holding `T | undefined` in state would
64
+ * silently hand the field back to the primitive the moment the value cleared, and every
65
+ * later render would be ignored. `null` says "nothing selected" out loud. `defaultValue` is
66
+ * not accepted here for the same reason — use the parts for an uncontrolled select.
67
+ */
68
+ export declare function Select<T extends string = string>({ value, options, placeholder, emptyLabel, clearLabel, label, chevron, indicator, className, popupClassName, triggerProps, positionerProps, ...rest }: SelectProps<T>): import("react").JSX.Element;
69
+ //# sourceMappingURL=select.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../src/ui/select.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAKtE,eAAO,MAAM,UAAU,uBAAiB,CAAC;AACzC,eAAO,MAAM,aAAa,wJAAoB,CAAC;AAC/C,eAAO,MAAM,WAAW,oJAAkB,CAAC;AAC3C,eAAO,MAAM,UAAU,mJAAiB,CAAC;AACzC,eAAO,MAAM,YAAY,oJAAmB,CAAC;AAC7C,eAAO,MAAM,gBAAgB,wJAAuB,CAAC;AACrD,eAAO,MAAM,WAAW,mJAAkB,CAAC;AAC3C,eAAO,MAAM,UAAU,kJAAiB,CAAC;AACzC,eAAO,MAAM,UAAU,0IAAiB,CAAC;AACzC,eAAO,MAAM,mBAAmB,4JAA0B,CAAC;AAC3D,eAAO,MAAM,cAAc,iJAAqB,CAAC;AACjD,eAAO,MAAM,WAAW,mJAAkB,CAAC;AAC3C,eAAO,MAAM,gBAAgB,wJAAuB,CAAC;AACrD,eAAO,MAAM,eAAe,0IAAsB,CAAC;AAEnD,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IACrD,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,IAAI,CACvD,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAC9B,UAAU,GAAG,OAAO,GAAG,cAAc,GAAG,UAAU,CACnD,GAAG;IACF,OAAO,CAAC,EAAE,SAAS,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IACrC,gFAAgF;IAChF,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;;OAGG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,wCAAwC;IACxC,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACpD,cAAc,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACvD,YAAY,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC;IACvC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC;CAC9C,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,EAChD,KAAK,EACL,OAAY,EACZ,WAAW,EACX,UAAU,EACV,UAAU,EACV,KAAK,EACL,OAAO,EACP,SAAS,EACT,SAAS,EACT,cAAc,EACd,YAAY,EACZ,eAAe,EACf,GAAG,IAAI,EACR,EAAE,WAAW,CAAC,CAAC,CAAC,+BAmGhB"}
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Select as Primitive } from "@base-ui/react/select";
3
+ import { mergeClassName } from "./class-name.js";
4
+ import { CheckGlyph, ChevronGlyph } from "./glyphs.js";
5
+ // The parts, for a select this wrapper's option list cannot express — groups, separators,
6
+ // a scrollable list with arrows.
7
+ export const SelectRoot = Primitive.Root;
8
+ export const SelectTrigger = Primitive.Trigger;
9
+ export const SelectValue = Primitive.Value;
10
+ export const SelectIcon = Primitive.Icon;
11
+ export const SelectPortal = Primitive.Portal;
12
+ export const SelectPositioner = Primitive.Positioner;
13
+ export const SelectPopup = Primitive.Popup;
14
+ export const SelectList = Primitive.List;
15
+ export const SelectItem = Primitive.Item;
16
+ export const SelectItemIndicator = Primitive.ItemIndicator;
17
+ export const SelectItemText = Primitive.ItemText;
18
+ export const SelectGroup = Primitive.Group;
19
+ export const SelectGroupLabel = Primitive.GroupLabel;
20
+ export const SelectSeparator = Primitive.Separator;
21
+ /**
22
+ * A single-choice field. Trigger, portal, positioner, popup and list composed once; `name`,
23
+ * `form`, `required`, `id`, `disabled` and every other root prop pass straight through to
24
+ * Base UI's hidden input, so this submits inside a `<form>` like a native `<select>`.
25
+ *
26
+ * Controlled only, and that is the point of `value ?? null`: React and Base UI both read
27
+ * `value={undefined}` as "uncontrolled", so a caller holding `T | undefined` in state would
28
+ * silently hand the field back to the primitive the moment the value cleared, and every
29
+ * later render would be ignored. `null` says "nothing selected" out loud. `defaultValue` is
30
+ * not accepted here for the same reason — use the parts for an uncontrolled select.
31
+ */
32
+ export function Select({ value, options = [], placeholder, emptyLabel, clearLabel, label, chevron, indicator, className, popupClassName, triggerProps, positionerProps, ...rest }) {
33
+ const selected = options.find((option) => option.value === value);
34
+ // `undefined` when nothing is selected, and it has to be exactly that. Base UI's Value part
35
+ // falls back to `placeholder` only when it is handed NO children at all — an empty fragment
36
+ // still counts as children, which is how the donor ended up with a select that showed a
37
+ // blank trigger instead of "Choose one".
38
+ const selectedLabel = selected ? (_jsxs(_Fragment, { children: [selected.icon ? (_jsx("span", { className: "mr-2 inline-flex align-middle", children: selected.icon })) : null, selected.label] })) : undefined;
39
+ return (_jsxs(Primitive.Root, { value: value ?? null, items: options, ...rest, children: [_jsxs(Primitive.Trigger, { "aria-label": label, ...triggerProps, className: mergeClassName(
40
+ // No focus style: the trigger keeps the app's one `:focus-visible` outline. Swapping
41
+ // it for a `ring-*` loses forced-colors mode, where a box-shadow is forced to none.
42
+ "flex w-full items-center justify-between gap-2 rounded-md border border-input bg-background px-3 py-2 text-left text-foreground disabled:pointer-events-none disabled:opacity-50", className), children: [_jsx(Primitive.Value, { placeholder: placeholder, className: "min-w-0 flex-1 truncate data-placeholder:text-muted-foreground", children: selectedLabel }), _jsx(Primitive.Icon, { className: "shrink-0 text-muted-foreground", children: chevron ?? _jsx(ChevronGlyph, {}) })] }), _jsx(Primitive.Portal, { children: _jsx(Primitive.Positioner, { sideOffset: 4, ...positionerProps, className: mergeClassName("z-[60] max-w-[var(--available-width)] outline-none", positionerProps?.className), children: _jsx(Primitive.Popup, { className: mergeClassName("min-w-[var(--anchor-width)] origin-[var(--transform-origin)] overflow-hidden border border-border bg-popover text-popover-foreground outline-none transition-[scale,opacity] data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0", popupClassName), children: _jsxs(Primitive.List, { className: "max-h-[var(--available-height)] overflow-y-auto", children: [clearLabel !== undefined && value != null ? (
43
+ // An Item with `value={null}`, not a button above the list: only an item is
44
+ // reachable by arrow keys and typeahead, and only an item closes the popup
45
+ // on its own. `null` is what arrives at `onValueChange`.
46
+ _jsxs(Primitive.Item, { value: null, className: "flex cursor-default items-center gap-2 px-3 py-2 text-muted-foreground outline-none data-highlighted:bg-accent data-highlighted:text-accent-foreground", children: [_jsx("span", { className: "size-4 shrink-0", "aria-hidden": "true" }), _jsx(Primitive.ItemText, { children: clearLabel })] })) : null, options.length === 0 ? (_jsx("div", { className: "px-3 py-2 text-muted-foreground", children: emptyLabel })) : (options.map((option) => (_jsxs(Primitive.Item, { value: option.value, disabled: option.disabled, className: "flex cursor-default items-center gap-2 px-3 py-2 outline-none data-disabled:pointer-events-none data-disabled:opacity-50 data-highlighted:bg-accent data-highlighted:text-accent-foreground", children: [_jsx("span", { className: "size-4 shrink-0", children: _jsx(Primitive.ItemIndicator, { children: indicator ?? _jsx(CheckGlyph, {}) }) }), option.icon ? (_jsx("span", { className: "inline-flex shrink-0", children: option.icon })) : null, _jsx(Primitive.ItemText, { children: option.label })] }, option.value))))] }) }) }) })] }));
47
+ }
48
+ //# sourceMappingURL=select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.js","sourceRoot":"","sources":["../../src/ui/select.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import { Tabs as Primitive } from "@base-ui/react/tabs";
2
+ export declare const Tabs: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").TabsRootProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
3
+ export declare const TabPanel: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").TabsPanelProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
4
+ type TabListLabel = {
5
+ label: string;
6
+ "aria-labelledby"?: undefined;
7
+ } | {
8
+ label?: undefined;
9
+ "aria-labelledby": string;
10
+ };
11
+ export type TabListProps = Omit<Primitive.List.Props, "aria-labelledby"> & TabListLabel & {
12
+ /** Classes for the scroll wrapper — the element that owns the sideways scroll. */
13
+ wrapperClassName?: string;
14
+ };
15
+ /**
16
+ * The rail, and the reason this file exists.
17
+ *
18
+ * It scrolls sideways rather than wrapping, because a second row of tabs pushes the panel
19
+ * below the fold on a phone. But the scroll cannot live on the rail itself: `overflow-x: auto`
20
+ * makes the element a scroll container, a scroll container clips everything its children paint
21
+ * outside the padding box, and a computed `overflow-x` of `auto` forces `overflow-y` from
22
+ * `visible` to `auto` as well. A self-scrolling rail therefore crops the focus ring on all
23
+ * FOUR sides of whichever tab the keyboard is on — top and bottom always, the left of the
24
+ * first tab and the right of the last.
25
+ *
26
+ * So the scroll goes on a wrapper, and the wrapper's padding is the room the ring needs. The
27
+ * number is derived, not inherited: `@gusnips/tokens` draws `outline: 2px solid` at
28
+ * `outline-offset: 2px`, so the ring paints from 2px to 4px beyond a tab's border box, and
29
+ * `p-1` is exactly those 4px. An outline reaches FURTHER than the `ring-2` box-shadow the
30
+ * donor had here, so the wrapper matters more now than it did there, not less.
31
+ *
32
+ * Naming the rail is not optional and not a comment: pass `label`, or point
33
+ * `aria-labelledby` at a heading. A tablist with neither is announced as nothing at all.
34
+ */
35
+ export declare function TabList({ label, className, wrapperClassName, children, ...rest }: TabListProps): import("react").JSX.Element;
36
+ export declare function Tab({ className, ...rest }: Primitive.Tab.Props): import("react").JSX.Element;
37
+ /**
38
+ * The travelling marker under the active tab — a SEPARATE part, because the two donors
39
+ * disagreed about whether to have one at all and both were right about their own app. Render
40
+ * it or do not; the rail works either way.
41
+ *
42
+ * It must be a child of `TabList`: Base UI measures the active tab against the enclosing
43
+ * `[role="tablist"]` and writes `--active-tab-left` / `--active-tab-width` onto this element.
44
+ *
45
+ * `renderBeforeHydration` is on by default because this package's apps are prerendered — with
46
+ * it off, every prerendered page shows the rail with no marker until React arrives. It writes
47
+ * one inline script to do that; under a strict CSP without Base UI's `CspProvider` the script
48
+ * is blocked and the behaviour falls back to exactly what `false` would have done.
49
+ */
50
+ export declare function TabIndicator({ className, ...rest }: Primitive.Indicator.Props): import("react").JSX.Element;
51
+ export {};
52
+ //# sourceMappingURL=tabs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../src/ui/tabs.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAOxD,eAAO,MAAM,IAAI,gJAAiB,CAAC;AACnC,eAAO,MAAM,QAAQ,iJAAkB,CAAC;AAExC,KAAK,YAAY,GACb;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,SAAS,CAAA;CAAE,GAChD;IAAE,KAAK,CAAC,EAAE,SAAS,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,iBAAiB,CAAC,GACtE,YAAY,GAAG;IACb,kFAAkF;IAClF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEJ;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,YAAY,+BAa9F;AAYD,wBAAgB,GAAG,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,SAAS,CAAC,GAAG,CAAC,KAAK,+BAE9D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,SAAS,CAAC,SAAS,CAAC,KAAK,+BAY7E"}