@tea-ui/core 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 (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +33 -0
  3. package/dist/card.d.ts +60 -0
  4. package/dist/card.js +116 -0
  5. package/dist/card.js.map +1 -0
  6. package/dist/feedback/alert.d.ts +57 -0
  7. package/dist/feedback/alert.js +104 -0
  8. package/dist/feedback/alert.js.map +1 -0
  9. package/dist/feedback/badge.d.ts +34 -0
  10. package/dist/feedback/badge.js +62 -0
  11. package/dist/feedback/badge.js.map +1 -0
  12. package/dist/feedback/index.d.ts +13 -0
  13. package/dist/feedback/index.js +62 -0
  14. package/dist/feedback/index.js.map +1 -0
  15. package/dist/feedback/progress.d.ts +25 -0
  16. package/dist/feedback/progress.js +49 -0
  17. package/dist/feedback/progress.js.map +1 -0
  18. package/dist/feedback/skeleton.d.ts +38 -0
  19. package/dist/feedback/skeleton.js +49 -0
  20. package/dist/feedback/skeleton.js.map +1 -0
  21. package/dist/feedback/spinner.d.ts +31 -0
  22. package/dist/feedback/spinner.js +55 -0
  23. package/dist/feedback/spinner.js.map +1 -0
  24. package/dist/feedback/status.d.ts +63 -0
  25. package/dist/feedback/status.js +156 -0
  26. package/dist/feedback/status.js.map +1 -0
  27. package/dist/feedback/toast.d.ts +72 -0
  28. package/dist/feedback/toast.js +211 -0
  29. package/dist/feedback/toast.js.map +1 -0
  30. package/dist/format/index.d.ts +63 -0
  31. package/dist/format/index.js +84 -0
  32. package/dist/format/index.js.map +1 -0
  33. package/dist/index.d.ts +43 -0
  34. package/dist/index.js +163 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/inputs/button-group.d.ts +70 -0
  37. package/dist/inputs/button-group.js +59 -0
  38. package/dist/inputs/button-group.js.map +1 -0
  39. package/dist/inputs/button.d.ts +52 -0
  40. package/dist/inputs/button.js +84 -0
  41. package/dist/inputs/button.js.map +1 -0
  42. package/dist/inputs/checkbox.d.ts +48 -0
  43. package/dist/inputs/checkbox.js +74 -0
  44. package/dist/inputs/checkbox.js.map +1 -0
  45. package/dist/inputs/combobox.d.ts +77 -0
  46. package/dist/inputs/combobox.js +273 -0
  47. package/dist/inputs/combobox.js.map +1 -0
  48. package/dist/inputs/field.d.ts +220 -0
  49. package/dist/inputs/field.js +183 -0
  50. package/dist/inputs/field.js.map +1 -0
  51. package/dist/inputs/icon-button.d.ts +44 -0
  52. package/dist/inputs/icon-button.js +30 -0
  53. package/dist/inputs/icon-button.js.map +1 -0
  54. package/dist/inputs/index.d.ts +22 -0
  55. package/dist/inputs/index.js +92 -0
  56. package/dist/inputs/index.js.map +1 -0
  57. package/dist/inputs/input-group.d.ts +77 -0
  58. package/dist/inputs/input-group.js +76 -0
  59. package/dist/inputs/input-group.js.map +1 -0
  60. package/dist/inputs/input.d.ts +58 -0
  61. package/dist/inputs/input.js +68 -0
  62. package/dist/inputs/input.js.map +1 -0
  63. package/dist/inputs/label.d.ts +47 -0
  64. package/dist/inputs/label.js +29 -0
  65. package/dist/inputs/label.js.map +1 -0
  66. package/dist/inputs/number-input.d.ts +62 -0
  67. package/dist/inputs/number-input.js +86 -0
  68. package/dist/inputs/number-input.js.map +1 -0
  69. package/dist/inputs/password-input.d.ts +66 -0
  70. package/dist/inputs/password-input.js +73 -0
  71. package/dist/inputs/password-input.js.map +1 -0
  72. package/dist/inputs/radio.d.ts +66 -0
  73. package/dist/inputs/radio.js +96 -0
  74. package/dist/inputs/radio.js.map +1 -0
  75. package/dist/inputs/search-input.d.ts +66 -0
  76. package/dist/inputs/search-input.js +76 -0
  77. package/dist/inputs/search-input.js.map +1 -0
  78. package/dist/inputs/select.d.ts +65 -0
  79. package/dist/inputs/select.js +139 -0
  80. package/dist/inputs/select.js.map +1 -0
  81. package/dist/inputs/slider.d.ts +67 -0
  82. package/dist/inputs/slider.js +107 -0
  83. package/dist/inputs/slider.js.map +1 -0
  84. package/dist/inputs/switch.d.ts +42 -0
  85. package/dist/inputs/switch.js +69 -0
  86. package/dist/inputs/switch.js.map +1 -0
  87. package/dist/inputs/textarea.d.ts +51 -0
  88. package/dist/inputs/textarea.js +82 -0
  89. package/dist/inputs/textarea.js.map +1 -0
  90. package/dist/inputs/toggle.d.ts +57 -0
  91. package/dist/inputs/toggle.js +91 -0
  92. package/dist/inputs/toggle.js.map +1 -0
  93. package/dist/internal/index.d.ts +5 -0
  94. package/dist/internal/index.js +19 -0
  95. package/dist/internal/index.js.map +1 -0
  96. package/dist/internal/refs.d.ts +31 -0
  97. package/dist/internal/refs.js +34 -0
  98. package/dist/internal/refs.js.map +1 -0
  99. package/dist/internal/slot.d.ts +76 -0
  100. package/dist/internal/slot.js +40 -0
  101. package/dist/internal/slot.js.map +1 -0
  102. package/dist/internal/use-controllable-state.d.ts +25 -0
  103. package/dist/internal/use-controllable-state.js +38 -0
  104. package/dist/internal/use-controllable-state.js.map +1 -0
  105. package/dist/layout/index.d.ts +131 -0
  106. package/dist/layout/index.js +212 -0
  107. package/dist/layout/index.js.map +1 -0
  108. package/dist/navigation/index.d.ts +114 -0
  109. package/dist/navigation/index.js +523 -0
  110. package/dist/navigation/index.js.map +1 -0
  111. package/dist/overlays/alert-dialog.d.ts +75 -0
  112. package/dist/overlays/alert-dialog.js +163 -0
  113. package/dist/overlays/alert-dialog.js.map +1 -0
  114. package/dist/overlays/dialog.d.ts +44 -0
  115. package/dist/overlays/dialog.js +121 -0
  116. package/dist/overlays/dialog.js.map +1 -0
  117. package/dist/overlays/drawer.d.ts +49 -0
  118. package/dist/overlays/drawer.js +125 -0
  119. package/dist/overlays/drawer.js.map +1 -0
  120. package/dist/overlays/dropdown-menu.d.ts +53 -0
  121. package/dist/overlays/dropdown-menu.js +192 -0
  122. package/dist/overlays/dropdown-menu.js.map +1 -0
  123. package/dist/overlays/index.d.ts +10 -0
  124. package/dist/overlays/index.js +169 -0
  125. package/dist/overlays/index.js.map +1 -0
  126. package/dist/overlays/popover.d.ts +58 -0
  127. package/dist/overlays/popover.js +105 -0
  128. package/dist/overlays/popover.js.map +1 -0
  129. package/dist/styles.css +2 -0
  130. package/dist/typography/index.d.ts +79 -0
  131. package/dist/typography/index.js +280 -0
  132. package/dist/typography/index.js.map +1 -0
  133. package/package.json +60 -0
@@ -0,0 +1,34 @@
1
+ function assignRef(ref, value) {
2
+ if (typeof ref === "function") {
3
+ ref(value);
4
+ } else if (ref && typeof ref === "object") {
5
+ ref.current = value;
6
+ }
7
+ }
8
+ function setRefs(...refs) {
9
+ return (value) => {
10
+ for (const ref of refs) assignRef(ref, value);
11
+ };
12
+ }
13
+ function composeRefs(...refs) {
14
+ return setRefs(...refs);
15
+ }
16
+ function splitProps(props, ownKeys) {
17
+ const own = {};
18
+ const rest = {};
19
+ const ownSet = new Set(ownKeys);
20
+ for (const key of Object.keys(props)) {
21
+ if (ownSet.has(key)) {
22
+ own[key] = props[key];
23
+ } else {
24
+ rest[key] = props[key];
25
+ }
26
+ }
27
+ return { own, rest };
28
+ }
29
+ export {
30
+ composeRefs,
31
+ setRefs,
32
+ splitProps
33
+ };
34
+ //# sourceMappingURL=refs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/internal/refs.ts"],"sourcesContent":["import type * as React from \"react\";\n\n/**\n * Ref plumbing for DOM-backed components.\n *\n * Every interactive TEA UI component forwards its ref, and every TEA UI\n * component accepts an external ref. That means every component needs the same\n * three-way merge between its own ref, the forwarded ref, and any prop ref a\n * Radix primitive hands to it. Doing it once here is the only way it is done\n * consistently.\n */\n\nexport type PossibleRef<T> = React.Ref<T> | undefined;\n\nfunction assignRef<T>(ref: PossibleRef<T>, value: T): void {\n if (typeof ref === \"function\") {\n ref(value);\n } else if (ref && typeof ref === \"object\") {\n (ref as React.RefObject<T | null>).current = value;\n }\n}\n\n/** A ref that can hold several refs at once. */\nexport function setRefs<T>(...refs: PossibleRef<T>[]): (value: T) => void {\n return (value: T) => {\n for (const ref of refs) assignRef(ref, value);\n };\n}\n\n/**\n * Merge a component's internal ref with a forwarded one into a single\n * callback ref. Internal first, so a component always knows its own node.\n */\nexport function composeRefs<T>(...refs: PossibleRef<T>[]): (value: T) => void {\n return setRefs(...refs);\n}\n\n/**\n * Split an incoming props object into \"ours to handle\" and \"the consumer's\",\n * merging anything that is not ours forward. This is how a TEA UI wrapper stays\n * transparent: unknown props are never swallowed, and every known prop is\n * consumed exactly once.\n */\nexport function splitProps<T extends Record<string, unknown>, K extends keyof T>(\n props: T,\n ownKeys: readonly K[],\n): { own: Pick<T, K>; rest: Omit<T, K> } {\n const own = {} as Pick<T, K>;\n const rest = {} as Omit<T, K>;\n const ownSet = new Set<PropertyKey>(ownKeys as readonly PropertyKey[]);\n\n for (const key of Object.keys(props) as Array<Extract<keyof T, string>>) {\n if (ownSet.has(key)) {\n (own as Record<string, unknown>)[key] = props[key];\n } else {\n (rest as Record<string, unknown>)[key] = props[key];\n }\n }\n return { own, rest };\n}\n"],"mappings":"AAcA,SAAS,UAAa,KAAqB,OAAgB;AACzD,MAAI,OAAO,QAAQ,YAAY;AAC7B,QAAI,KAAK;AAAA,EACX,WAAW,OAAO,OAAO,QAAQ,UAAU;AACzC,IAAC,IAAkC,UAAU;AAAA,EAC/C;AACF;AAGO,SAAS,WAAc,MAA4C;AACxE,SAAO,CAAC,UAAa;AACnB,eAAW,OAAO,KAAM,WAAU,KAAK,KAAK;AAAA,EAC9C;AACF;AAMO,SAAS,eAAkB,MAA4C;AAC5E,SAAO,QAAQ,GAAG,IAAI;AACxB;AAQO,SAAS,WACd,OACA,SACuC;AACvC,QAAM,MAAM,CAAC;AACb,QAAM,OAAO,CAAC;AACd,QAAM,SAAS,IAAI,IAAiB,OAAiC;AAErE,aAAW,OAAO,OAAO,KAAK,KAAK,GAAsC;AACvE,QAAI,OAAO,IAAI,GAAG,GAAG;AACnB,MAAC,IAAgC,GAAG,IAAI,MAAM,GAAG;AAAA,IACnD,OAAO;AACL,MAAC,KAAiC,GAAG,IAAI,MAAM,GAAG;AAAA,IACpD;AAAA,EACF;AACA,SAAO,EAAE,KAAK,KAAK;AACrB;","names":[]}
@@ -0,0 +1,76 @@
1
+ import * as React from 'react';
2
+ export { cn } from '@tea-ui/utils';
3
+
4
+ /**
5
+ * The `asChild` contract.
6
+ *
7
+ * Native semantics first is a TEA UI rule, and this is the mechanism that makes
8
+ * it practical: a consumer can render a real `<a>` with the styles of a
9
+ * `<button>`, or a real `<summary>` with the styles of a `<div>`, without the
10
+ * library pretending to be the other element. The audit's `<a>` styled by hand
11
+ * as a primary button, and its `<button>` wrapping an icon, both disappear into
12
+ * `<Button asChild>`.
13
+ */
14
+ interface SlotProps {
15
+ /** Render the consumer's single child element instead of a `button`/`div`. */
16
+ asChild?: boolean | undefined;
17
+ /** The element this component renders when `asChild` is not used. */
18
+ as?: React.ElementType | undefined;
19
+ }
20
+ interface RenderProps {
21
+ asChild?: boolean | undefined;
22
+ as?: React.ElementType | undefined;
23
+ children?: React.ReactNode;
24
+ ref?: React.Ref<HTMLElement>;
25
+ className?: string | undefined;
26
+ }
27
+ /**
28
+ * Render either the default element or the consumer's child, forwarding the ref
29
+ * and merging the class names into whichever element actually lands in the DOM.
30
+ */
31
+ declare function renderElement({ asChild, as, className, children, ref, ...rest }: RenderProps & Record<string, unknown>, defaultTag: React.ElementType): React.ReactElement;
32
+ /**
33
+ * The `data-slot` value a component stamps on its root. Present so that a
34
+ * consumer can target TEA UI internals from CSS or a test without depending on
35
+ * class names, which are an implementation detail.
36
+ */
37
+ declare function dataSlot(...parts: string[]): Record<`data-slot`, string>;
38
+ /**
39
+ * Every interactive TEA UI component exposes its state as data attributes, so
40
+ * state is inspectable from the DOM and assertable in a test — not only visible.
41
+ *
42
+ * This is the attribute vocabulary, in one object, so no component invents its
43
+ * own spelling.
44
+ */
45
+ interface StateAttributes {
46
+ "data-disabled"?: true;
47
+ "data-loading"?: true;
48
+ "data-invalid"?: true;
49
+ "data-readonly"?: true;
50
+ "data-selected"?: true;
51
+ "data-active"?: true;
52
+ "data-checked"?: true;
53
+ "data-open"?: true;
54
+ "data-empty"?: true;
55
+ "data-orientation"?: "horizontal" | "vertical";
56
+ }
57
+ declare function stateAttributes(state: {
58
+ disabled?: boolean | undefined;
59
+ loading?: boolean | undefined;
60
+ invalid?: boolean | undefined;
61
+ readOnly?: boolean | undefined;
62
+ selected?: boolean | undefined;
63
+ active?: boolean | undefined;
64
+ checked?: boolean | undefined;
65
+ open?: boolean | undefined;
66
+ empty?: boolean | undefined;
67
+ orientation?: "horizontal" | "vertical" | undefined;
68
+ }): StateAttributes;
69
+ /**
70
+ * Merge refs from a forwarded ref, a prop-supplied ref and a private one.
71
+ * `privateRef` may be a function that receives the node as well as a ref object,
72
+ * which is how a component observes its own element without a state update.
73
+ */
74
+ declare function withPrivateRef<T>(forwarded: React.Ref<T> | undefined, propRef: React.Ref<T> | undefined, privateRef?: React.Ref<T>): React.RefCallback<T>;
75
+
76
+ export { type RenderProps, type SlotProps, type StateAttributes, dataSlot, renderElement, stateAttributes, withPrivateRef };
@@ -0,0 +1,40 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Slot } from "radix-ui";
3
+ import * as React from "react";
4
+ import { cn, slot as slotName } from "@tea-ui/utils";
5
+ import { composeRefs } from "./refs.js";
6
+ function renderElement({ asChild, as, className, children, ref, ...rest }, defaultTag) {
7
+ const Component = asChild ? Slot.Root : as ?? defaultTag;
8
+ if (asChild) {
9
+ return /* @__PURE__ */ jsx(Component, { className, ref, ...rest, children });
10
+ }
11
+ return /* @__PURE__ */ jsx(Component, { className, ref, ...rest, children });
12
+ }
13
+ function dataSlot(...parts) {
14
+ return { "data-slot": slotName(...parts) };
15
+ }
16
+ function stateAttributes(state) {
17
+ const attrs = {};
18
+ if (state.disabled) attrs["data-disabled"] = true;
19
+ if (state.loading) attrs["data-loading"] = true;
20
+ if (state.invalid) attrs["data-invalid"] = true;
21
+ if (state.readOnly) attrs["data-readonly"] = true;
22
+ if (state.selected) attrs["data-selected"] = true;
23
+ if (state.active) attrs["data-active"] = true;
24
+ if (state.checked) attrs["data-checked"] = true;
25
+ if (state.open) attrs["data-open"] = true;
26
+ if (state.empty) attrs["data-empty"] = true;
27
+ if (state.orientation) attrs["data-orientation"] = state.orientation;
28
+ return attrs;
29
+ }
30
+ function withPrivateRef(forwarded, propRef, privateRef) {
31
+ return composeRefs(forwarded, propRef, privateRef);
32
+ }
33
+ export {
34
+ cn,
35
+ dataSlot,
36
+ renderElement,
37
+ stateAttributes,
38
+ withPrivateRef
39
+ };
40
+ //# sourceMappingURL=slot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/internal/slot.tsx"],"sourcesContent":["import { Slot } from \"radix-ui\";\nimport * as React from \"react\";\nimport { cn, slot as slotName } from \"@tea-ui/utils\";\n\nimport { composeRefs } from \"./refs\";\n\n/**\n * The `asChild` contract.\n *\n * Native semantics first is a TEA UI rule, and this is the mechanism that makes\n * it practical: a consumer can render a real `<a>` with the styles of a\n * `<button>`, or a real `<summary>` with the styles of a `<div>`, without the\n * library pretending to be the other element. The audit's `<a>` styled by hand\n * as a primary button, and its `<button>` wrapping an icon, both disappear into\n * `<Button asChild>`.\n */\n\nexport interface SlotProps {\n /** Render the consumer's single child element instead of a `button`/`div`. */\n asChild?: boolean | undefined;\n /** The element this component renders when `asChild` is not used. */\n as?: React.ElementType | undefined;\n}\n\nexport interface RenderProps {\n asChild?: boolean | undefined;\n as?: React.ElementType | undefined;\n children?: React.ReactNode;\n ref?: React.Ref<HTMLElement>;\n className?: string | undefined;\n}\n\n/**\n * Render either the default element or the consumer's child, forwarding the ref\n * and merging the class names into whichever element actually lands in the DOM.\n */\nexport function renderElement(\n { asChild, as, className, children, ref, ...rest }: RenderProps & Record<string, unknown>,\n defaultTag: React.ElementType,\n): React.ReactElement {\n const Component = (asChild ? Slot.Root : (as ?? defaultTag)) as React.ElementType;\n\n if (asChild) {\n return (\n <Component className={className} ref={ref} {...rest}>\n {children}\n </Component>\n );\n }\n\n return (\n <Component className={className} ref={ref} {...rest}>\n {children}\n </Component>\n );\n}\n\n/**\n * The `data-slot` value a component stamps on its root. Present so that a\n * consumer can target TEA UI internals from CSS or a test without depending on\n * class names, which are an implementation detail.\n */\nexport function dataSlot(...parts: string[]): Record<`data-slot`, string> {\n return { \"data-slot\": slotName(...parts) };\n}\n\n/**\n * Every interactive TEA UI component exposes its state as data attributes, so\n * state is inspectable from the DOM and assertable in a test — not only visible.\n *\n * This is the attribute vocabulary, in one object, so no component invents its\n * own spelling.\n */\nexport interface StateAttributes {\n \"data-disabled\"?: true;\n \"data-loading\"?: true;\n \"data-invalid\"?: true;\n \"data-readonly\"?: true;\n \"data-selected\"?: true;\n \"data-active\"?: true;\n \"data-checked\"?: true;\n \"data-open\"?: true;\n \"data-empty\"?: true;\n \"data-orientation\"?: \"horizontal\" | \"vertical\";\n}\n\nexport function stateAttributes(state: {\n disabled?: boolean | undefined;\n loading?: boolean | undefined;\n invalid?: boolean | undefined;\n readOnly?: boolean | undefined;\n selected?: boolean | undefined;\n active?: boolean | undefined;\n checked?: boolean | undefined;\n open?: boolean | undefined;\n empty?: boolean | undefined;\n orientation?: \"horizontal\" | \"vertical\" | undefined;\n}): StateAttributes {\n const attrs: StateAttributes = {};\n if (state.disabled) attrs[\"data-disabled\"] = true;\n if (state.loading) attrs[\"data-loading\"] = true;\n if (state.invalid) attrs[\"data-invalid\"] = true;\n if (state.readOnly) attrs[\"data-readonly\"] = true;\n if (state.selected) attrs[\"data-selected\"] = true;\n if (state.active) attrs[\"data-active\"] = true;\n if (state.checked) attrs[\"data-checked\"] = true;\n if (state.open) attrs[\"data-open\"] = true;\n if (state.empty) attrs[\"data-empty\"] = true;\n if (state.orientation) attrs[\"data-orientation\"] = state.orientation;\n return attrs;\n}\n\n/**\n * Merge refs from a forwarded ref, a prop-supplied ref and a private one.\n * `privateRef` may be a function that receives the node as well as a ref object,\n * which is how a component observes its own element without a state update.\n */\nexport function withPrivateRef<T>(\n forwarded: React.Ref<T> | undefined,\n propRef: React.Ref<T> | undefined,\n privateRef?: React.Ref<T>,\n): React.RefCallback<T> {\n return composeRefs(forwarded, propRef, privateRef);\n}\n\nexport { cn };\n"],"mappings":"AA4CM;AA5CN,SAAS,YAAY;AACrB,YAAY,WAAW;AACvB,SAAS,IAAI,QAAQ,gBAAgB;AAErC,SAAS,mBAAmB;AAgCrB,SAAS,cACd,EAAE,SAAS,IAAI,WAAW,UAAU,KAAK,GAAG,KAAK,GACjD,YACoB;AACpB,QAAM,YAAa,UAAU,KAAK,OAAQ,MAAM;AAEhD,MAAI,SAAS;AACX,WACE,oBAAC,aAAU,WAAsB,KAAW,GAAG,MAC5C,UACH;AAAA,EAEJ;AAEA,SACE,oBAAC,aAAU,WAAsB,KAAW,GAAG,MAC5C,UACH;AAEJ;AAOO,SAAS,YAAY,OAA8C;AACxE,SAAO,EAAE,aAAa,SAAS,GAAG,KAAK,EAAE;AAC3C;AAsBO,SAAS,gBAAgB,OAWZ;AAClB,QAAM,QAAyB,CAAC;AAChC,MAAI,MAAM,SAAU,OAAM,eAAe,IAAI;AAC7C,MAAI,MAAM,QAAS,OAAM,cAAc,IAAI;AAC3C,MAAI,MAAM,QAAS,OAAM,cAAc,IAAI;AAC3C,MAAI,MAAM,SAAU,OAAM,eAAe,IAAI;AAC7C,MAAI,MAAM,SAAU,OAAM,eAAe,IAAI;AAC7C,MAAI,MAAM,OAAQ,OAAM,aAAa,IAAI;AACzC,MAAI,MAAM,QAAS,OAAM,cAAc,IAAI;AAC3C,MAAI,MAAM,KAAM,OAAM,WAAW,IAAI;AACrC,MAAI,MAAM,MAAO,OAAM,YAAY,IAAI;AACvC,MAAI,MAAM,YAAa,OAAM,kBAAkB,IAAI,MAAM;AACzD,SAAO;AACT;AAOO,SAAS,eACd,WACA,SACA,YACsB;AACtB,SAAO,YAAY,WAAW,SAAS,UAAU;AACnD;","names":[]}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Controlled/uncontrolled state in one place.
3
+ *
4
+ * TEA UI components are controlled, uncontrolled, or switch between the two at
5
+ * runtime without losing state — and the switch must not fire a spurious change
6
+ * callback, because a consumer's `onChange` writing back to state is the
7
+ * normal pattern and a spurious call turns it into a render loop.
8
+ *
9
+ * The distinction that matters: an *uncontrolled-to-controlled* transition is a
10
+ * real error and is reported. A *controlled* value that equals the internal one
11
+ * never notifies.
12
+ */
13
+ interface UseControllableStateParams<T> {
14
+ /** The controlled value. `undefined` means "the component owns it". */
15
+ value?: T | undefined;
16
+ /** The uncontrolled initial value. Ignored once `value` is provided. */
17
+ defaultValue?: T | undefined;
18
+ /** Called on every real change, in both modes. */
19
+ onChange?: ((next: T) => void) | undefined;
20
+ /** Name used in the controlled/uncontrolled error message. */
21
+ name?: string | undefined;
22
+ }
23
+ declare function useControllableState<T>({ value, defaultValue, onChange, name, }: UseControllableStateParams<T>): [T, (next: T | ((prev: T) => T)) => void];
24
+
25
+ export { type UseControllableStateParams, useControllableState };
@@ -0,0 +1,38 @@
1
+ import * as React from "react";
2
+ function useControllableState({
3
+ value,
4
+ defaultValue,
5
+ onChange,
6
+ name = "component"
7
+ }) {
8
+ const [uncontrolled, setUncontrolled] = React.useState(defaultValue);
9
+ const isControlled = value !== void 0;
10
+ const current = isControlled ? value : uncontrolled;
11
+ const onChangeRef = React.useRef(onChange);
12
+ React.useEffect(() => {
13
+ onChangeRef.current = onChange;
14
+ });
15
+ const wasControlled = React.useRef(isControlled);
16
+ React.useEffect(() => {
17
+ if (wasControlled.current !== isControlled) {
18
+ console.warn(
19
+ `[tea-ui] <${name}> switched from ${wasControlled.current ? "controlled" : "uncontrolled"} to ${isControlled ? "controlled" : "uncontrolled"}. Pick one mode and keep it.`
20
+ );
21
+ wasControlled.current = isControlled;
22
+ }
23
+ }, [isControlled, name]);
24
+ const setValue = React.useCallback(
25
+ (next) => {
26
+ const resolved = typeof next === "function" ? next(current) : next;
27
+ if (Object.is(resolved, current)) return;
28
+ if (!isControlled) setUncontrolled(resolved);
29
+ onChangeRef.current?.(resolved);
30
+ },
31
+ [current, isControlled]
32
+ );
33
+ return [current, setValue];
34
+ }
35
+ export {
36
+ useControllableState
37
+ };
38
+ //# sourceMappingURL=use-controllable-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/internal/use-controllable-state.ts"],"sourcesContent":["import * as React from \"react\";\n\n/**\n * Controlled/uncontrolled state in one place.\n *\n * TEA UI components are controlled, uncontrolled, or switch between the two at\n * runtime without losing state — and the switch must not fire a spurious change\n * callback, because a consumer's `onChange` writing back to state is the\n * normal pattern and a spurious call turns it into a render loop.\n *\n * The distinction that matters: an *uncontrolled-to-controlled* transition is a\n * real error and is reported. A *controlled* value that equals the internal one\n * never notifies.\n */\nexport interface UseControllableStateParams<T> {\n /** The controlled value. `undefined` means \"the component owns it\". */\n value?: T | undefined;\n /** The uncontrolled initial value. Ignored once `value` is provided. */\n defaultValue?: T | undefined;\n /** Called on every real change, in both modes. */\n onChange?: ((next: T) => void) | undefined;\n /** Name used in the controlled/uncontrolled error message. */\n name?: string | undefined;\n}\n\nexport function useControllableState<T>({\n value,\n defaultValue,\n onChange,\n name = \"component\",\n}: UseControllableStateParams<T>): [T, (next: T | ((prev: T) => T)) => void] {\n const [uncontrolled, setUncontrolled] = React.useState<T>(defaultValue as T);\n const isControlled = value !== undefined;\n const current = (isControlled ? value : uncontrolled) as T;\n\n // `onChange` is read through a ref so a caller passing an inline arrow does\n // not invalidate the setter identity and re-render every consumer of it.\n const onChangeRef = React.useRef(onChange);\n React.useEffect(() => {\n onChangeRef.current = onChange;\n });\n\n const wasControlled = React.useRef(isControlled);\n React.useEffect(() => {\n if (wasControlled.current !== isControlled) {\n // Not fatal, but it means the consumer's expectation and the component's\n // state have diverged — usually a missing `value` in a conditional render.\n console.warn(\n `[tea-ui] <${name}> switched from ${wasControlled.current ? \"controlled\" : \"uncontrolled\"} to ${\n isControlled ? \"controlled\" : \"uncontrolled\"\n }. Pick one mode and keep it.`,\n );\n wasControlled.current = isControlled;\n }\n }, [isControlled, name]);\n\n const setValue = React.useCallback(\n (next: T | ((prev: T) => T)) => {\n const resolved =\n typeof next === \"function\" ? (next as (prev: T) => T)(current) : next;\n if (Object.is(resolved, current)) return;\n if (!isControlled) setUncontrolled(resolved);\n onChangeRef.current?.(resolved);\n },\n [current, isControlled],\n );\n\n return [current, setValue];\n}\n"],"mappings":"AAAA,YAAY,WAAW;AAyBhB,SAAS,qBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AACT,GAA6E;AAC3E,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAY,YAAiB;AAC3E,QAAM,eAAe,UAAU;AAC/B,QAAM,UAAW,eAAe,QAAQ;AAIxC,QAAM,cAAc,MAAM,OAAO,QAAQ;AACzC,QAAM,UAAU,MAAM;AACpB,gBAAY,UAAU;AAAA,EACxB,CAAC;AAED,QAAM,gBAAgB,MAAM,OAAO,YAAY;AAC/C,QAAM,UAAU,MAAM;AACpB,QAAI,cAAc,YAAY,cAAc;AAG1C,cAAQ;AAAA,QACN,aAAa,IAAI,mBAAmB,cAAc,UAAU,eAAe,cAAc,OACvF,eAAe,eAAe,cAChC;AAAA,MACF;AACA,oBAAc,UAAU;AAAA,IAC1B;AAAA,EACF,GAAG,CAAC,cAAc,IAAI,CAAC;AAEvB,QAAM,WAAW,MAAM;AAAA,IACrB,CAAC,SAA+B;AAC9B,YAAM,WACJ,OAAO,SAAS,aAAc,KAAwB,OAAO,IAAI;AACnE,UAAI,OAAO,GAAG,UAAU,OAAO,EAAG;AAClC,UAAI,CAAC,aAAc,iBAAgB,QAAQ;AAC3C,kBAAY,UAAU,QAAQ;AAAA,IAChC;AAAA,IACA,CAAC,SAAS,YAAY;AAAA,EACxB;AAEA,SAAO,CAAC,SAAS,QAAQ;AAC3B;","names":[]}
@@ -0,0 +1,131 @@
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * TEA UI — layout primitives.
5
+ *
6
+ * Product-agnostic structure, with no opinions about what a page *is*. Density
7
+ * is not a prop here: `Stack` and friends read `--tea-gap` and
8
+ * `--tea-section-gap`, so a compact table inside a comfortable page retunes
9
+ * itself without either container knowing about the other. That is the whole
10
+ * reason density is expressed in CSS rather than in props.
11
+ */
12
+ interface BoxProps extends React.ComponentProps<"div"> {
13
+ asChild?: boolean | undefined;
14
+ as?: React.ElementType | undefined;
15
+ }
16
+ /** The escape hatch. A `div` that contributes nothing but structure. */
17
+ declare const Box: React.ForwardRefExoticComponent<Omit<BoxProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
18
+ interface StackProps extends React.ComponentProps<"div"> {
19
+ direction?: "vertical" | "horizontal" | undefined;
20
+ /**
21
+ * The gap step. `ui` reads the density variable; `section` uses the larger
22
+ * one; a Tailwind class is accepted for deliberate one-offs.
23
+ */
24
+ gap?: "ui" | "section" | "none" | string | undefined;
25
+ align?: React.CSSProperties["alignItems"] | undefined;
26
+ justify?: React.CSSProperties["justifyContent"] | undefined;
27
+ wrap?: boolean | undefined;
28
+ children: React.ReactNode;
29
+ }
30
+ declare const Stack: React.ForwardRefExoticComponent<Omit<StackProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
31
+ declare const HStack: React.ForwardRefExoticComponent<Omit<Omit<StackProps, "direction">, "ref"> & React.RefAttributes<HTMLDivElement>>;
32
+ declare const VStack: React.ForwardRefExoticComponent<Omit<Omit<StackProps, "direction">, "ref"> & React.RefAttributes<HTMLDivElement>>;
33
+ interface FlexProps extends React.ComponentProps<"div"> {
34
+ direction?: React.CSSProperties["flexDirection"] | undefined;
35
+ align?: React.CSSProperties["alignItems"] | undefined;
36
+ justify?: React.CSSProperties["justifyContent"] | undefined;
37
+ wrap?: boolean | undefined;
38
+ grow?: boolean | undefined;
39
+ shrink?: boolean | undefined;
40
+ basis?: string | number | undefined;
41
+ gap?: string | number | undefined;
42
+ children: React.ReactNode;
43
+ }
44
+ /** Escape-hatch flexbox, for when a Stack's opinion is wrong. */
45
+ declare const Flex: React.ForwardRefExoticComponent<Omit<FlexProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
46
+ interface GridProps extends React.ComponentProps<"div"> {
47
+ cols?: number | undefined;
48
+ /**
49
+ * Auto-fit columns that never fall below `minChildWidth`. This is the
50
+ * responsive default for card grids, and it is why a TEA card grid needs no
51
+ * per-breakpoint column counts.
52
+ */
53
+ minChildWidth?: string | undefined;
54
+ gap?: "ui" | "section" | "none" | string | undefined;
55
+ children: React.ReactNode;
56
+ }
57
+ declare const Grid: React.ForwardRefExoticComponent<Omit<GridProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
58
+ declare const CONTAINER_SIZES: {
59
+ readonly xs: "max-w-xs";
60
+ readonly sm: "max-w-sm";
61
+ readonly md: "max-w-md";
62
+ readonly lg: "max-w-lg";
63
+ readonly xl: "max-w-xl";
64
+ readonly "2xl": "max-w-2xl";
65
+ readonly "3xl": "max-w-3xl";
66
+ readonly "4xl": "max-w-4xl";
67
+ readonly "5xl": "max-w-5xl";
68
+ readonly "6xl": "max-w-6xl";
69
+ readonly full: "max-w-none";
70
+ };
71
+ interface ContainerProps extends React.ComponentProps<"div"> {
72
+ /** The measure. Admin panels are `full`; Public UI prose uses `lg`–`3xl`. */
73
+ size?: keyof typeof CONTAINER_SIZES | undefined;
74
+ /** Centre the container. Off for a container inside a grid cell. */
75
+ center?: boolean | undefined;
76
+ children: React.ReactNode;
77
+ }
78
+ declare const Container: React.ForwardRefExoticComponent<Omit<ContainerProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
79
+ interface SpacerProps extends React.ComponentProps<"div"> {
80
+ /** The gap step to insert. */
81
+ size?: "ui" | "section" | "none" | string | undefined;
82
+ /** Grow to fill the remaining space instead of a fixed gap. */
83
+ grow?: boolean | undefined;
84
+ }
85
+ declare const Spacer: React.ForwardRefExoticComponent<Omit<SpacerProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
86
+ interface CenterProps extends React.ComponentProps<"div"> {
87
+ /** Restrict to a full-viewport centring, for empty routes and error pages. */
88
+ full?: boolean | undefined;
89
+ children: React.ReactNode;
90
+ }
91
+ declare const Center: React.ForwardRefExoticComponent<Omit<CenterProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
92
+ interface DividerProps extends Omit<React.ComponentProps<"hr">, "children"> {
93
+ orientation?: "horizontal" | "vertical" | undefined;
94
+ /**
95
+ * A label rendered in a gap in the rule. A visible divider that separates
96
+ * nothing is decoration, and a labelled one is a section boundary worth
97
+ * announcing.
98
+ */
99
+ label?: React.ReactNode | undefined;
100
+ }
101
+ /**
102
+ * A real `<hr>`. The audit found a divider rendering `role="none"` by default,
103
+ * which removed it from the accessibility tree entirely — so the structural
104
+ * boundary a screen-reader user most needs was the one they could not perceive.
105
+ */
106
+ declare const Divider: React.ForwardRefExoticComponent<Omit<DividerProps, "ref"> & React.RefAttributes<HTMLHRElement>>;
107
+ interface AspectRatioProps extends React.ComponentProps<"div"> {
108
+ ratio?: number | undefined;
109
+ children: React.ReactNode;
110
+ }
111
+ /** Reserves space before content arrives, so nothing reflows on load. */
112
+ declare const AspectRatio: React.ForwardRefExoticComponent<Omit<AspectRatioProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
113
+ interface ScrollAreaProps extends React.ComponentProps<"div"> {
114
+ orientation?: "vertical" | "horizontal" | "both" | undefined;
115
+ /** How far a wheel or trackpad gesture scrolls per event. */
116
+ scrollStep?: number | undefined;
117
+ children: React.ReactNode;
118
+ }
119
+ /**
120
+ * A styled scroll container.
121
+ *
122
+ * This is *not* Radix's `ScrollArea`, on purpose: that primitive hides the
123
+ * native scrollbar and reinvents a thumb, which costs a full extra layer of
124
+ * layout and breaks native scroll chaining and momentum on touch. Here the
125
+ * browser keeps its own scrollbar, and the token stylesheet styles it — with
126
+ * the standards `scrollbar-color` first, so Firefox is not left on UA defaults
127
+ * as both source projects were.
128
+ */
129
+ declare const ScrollArea: React.ForwardRefExoticComponent<Omit<ScrollAreaProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
130
+
131
+ export { AspectRatio, type AspectRatioProps, Box, type BoxProps, CONTAINER_SIZES, Center, type CenterProps, Container, type ContainerProps, Divider, type DividerProps, Flex, type FlexProps, Grid, type GridProps, HStack, ScrollArea, type ScrollAreaProps, Spacer, type SpacerProps, Stack, type StackProps, VStack };
@@ -0,0 +1,212 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Slot } from "radix-ui";
4
+ import { cn } from "@tea-ui/utils";
5
+ import { dataSlot } from "../internal/index.js";
6
+ const Box = React.forwardRef(function Box2({ asChild = false, as, className, ...props }, ref) {
7
+ const Component = asChild ? Slot.Root : as ?? "div";
8
+ return /* @__PURE__ */ jsx(Component, { ref, className: cn(className), ...dataSlot("box"), ...props });
9
+ });
10
+ const GAP = {
11
+ ui: "gap-ui",
12
+ section: "section-gap",
13
+ none: "gap-0"
14
+ };
15
+ const Stack = React.forwardRef(function Stack2({ direction = "vertical", gap = "ui", align, justify, wrap = false, className, style, children, ...props }, ref) {
16
+ return /* @__PURE__ */ jsx(
17
+ "div",
18
+ {
19
+ ref,
20
+ style: { alignItems: align, justifyContent: justify, ...style },
21
+ className: cn(
22
+ "flex",
23
+ direction === "vertical" ? "flex-col" : "flex-row",
24
+ wrap && "flex-wrap",
25
+ gap === "ui" || gap === "section" || gap === "none" ? GAP[gap] : gap,
26
+ className
27
+ ),
28
+ ...dataSlot("stack"),
29
+ ...props,
30
+ children
31
+ }
32
+ );
33
+ });
34
+ const HStack = React.forwardRef(
35
+ function HStack2(props, ref) {
36
+ return /* @__PURE__ */ jsx(Stack, { ref, direction: "horizontal", ...props });
37
+ }
38
+ );
39
+ const VStack = React.forwardRef(
40
+ function VStack2(props, ref) {
41
+ return /* @__PURE__ */ jsx(Stack, { ref, direction: "vertical", ...props });
42
+ }
43
+ );
44
+ const Flex = React.forwardRef(function Flex2({ direction, align, justify, wrap, grow, shrink, basis, gap, className, style, children, ...props }, ref) {
45
+ return /* @__PURE__ */ jsx(
46
+ "div",
47
+ {
48
+ ref,
49
+ style: {
50
+ flexDirection: direction,
51
+ alignItems: align,
52
+ justifyContent: justify,
53
+ flexGrow: grow ? 1 : void 0,
54
+ flexShrink: shrink ? 1 : void 0,
55
+ flexBasis: basis,
56
+ gap,
57
+ ...style
58
+ },
59
+ className: cn("flex", wrap && "flex-wrap", className),
60
+ ...dataSlot("flex"),
61
+ ...props,
62
+ children
63
+ }
64
+ );
65
+ });
66
+ const Grid = React.forwardRef(function Grid2({ cols, minChildWidth, gap = "ui", className, style, children, ...props }, ref) {
67
+ return /* @__PURE__ */ jsx(
68
+ "div",
69
+ {
70
+ ref,
71
+ style: {
72
+ gridTemplateColumns: minChildWidth ? `repeat(auto-fit, minmax(min(${minChildWidth}, 100%), 1fr))` : cols ? `repeat(${cols}, minmax(0, 1fr))` : void 0,
73
+ ...style
74
+ },
75
+ className: cn("grid", gap === "ui" || gap === "section" || gap === "none" ? GAP[gap] : gap, className),
76
+ ...dataSlot("grid"),
77
+ ...props,
78
+ children
79
+ }
80
+ );
81
+ });
82
+ const CONTAINER_SIZES = {
83
+ xs: "max-w-xs",
84
+ sm: "max-w-sm",
85
+ md: "max-w-md",
86
+ lg: "max-w-lg",
87
+ xl: "max-w-xl",
88
+ "2xl": "max-w-2xl",
89
+ "3xl": "max-w-3xl",
90
+ "4xl": "max-w-4xl",
91
+ "5xl": "max-w-5xl",
92
+ "6xl": "max-w-6xl",
93
+ full: "max-w-none"
94
+ };
95
+ const Container = React.forwardRef(function Container2({ size = "6xl", center = true, className, children, ...props }, ref) {
96
+ return /* @__PURE__ */ jsx(
97
+ "div",
98
+ {
99
+ ref,
100
+ className: cn(CONTAINER_SIZES[size], center && "mx-auto", "w-full px-4 md:px-6", className),
101
+ ...dataSlot("container"),
102
+ ...props,
103
+ children
104
+ }
105
+ );
106
+ });
107
+ const Spacer = React.forwardRef(function Spacer2({ size = "ui", grow = false, className, ...props }, ref) {
108
+ return /* @__PURE__ */ jsx(
109
+ "div",
110
+ {
111
+ ref,
112
+ "aria-hidden": "true",
113
+ className: cn(grow ? "flex-1" : size === "ui" || size === "section" || size === "none" ? GAP[size] : size, className),
114
+ ...dataSlot("spacer"),
115
+ ...props
116
+ }
117
+ );
118
+ });
119
+ const Center = React.forwardRef(function Center2({ full = false, className, children, ...props }, ref) {
120
+ return /* @__PURE__ */ jsx(
121
+ "div",
122
+ {
123
+ ref,
124
+ className: cn("flex flex-col items-center justify-center gap-3 text-center", full && "min-h-[60dvh]", className),
125
+ ...dataSlot("center"),
126
+ ...props,
127
+ children
128
+ }
129
+ );
130
+ });
131
+ const Divider = React.forwardRef(function Divider2({ orientation = "horizontal", label, className, ...props }, ref) {
132
+ if (orientation === "vertical") {
133
+ return /* @__PURE__ */ jsx(
134
+ "div",
135
+ {
136
+ ref,
137
+ role: "separator",
138
+ "aria-orientation": "vertical",
139
+ className: cn("h-full w-px bg-line", className),
140
+ ...dataSlot("divider"),
141
+ ...props
142
+ }
143
+ );
144
+ }
145
+ if (label) {
146
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-3", className), ...dataSlot("divider", "labelled"), children: [
147
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "h-px flex-1 bg-line" }),
148
+ /* @__PURE__ */ jsx("span", { className: "text-label font-medium uppercase tracking-widest text-fg-muted", children: label }),
149
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "h-px flex-1 bg-line" })
150
+ ] });
151
+ }
152
+ return /* @__PURE__ */ jsx(
153
+ "hr",
154
+ {
155
+ ref,
156
+ className: cn("h-px w-full border-0 bg-line", className),
157
+ ...dataSlot("divider"),
158
+ ...props
159
+ }
160
+ );
161
+ });
162
+ const AspectRatio = React.forwardRef(function AspectRatio2({ ratio = 16 / 9, className, children, ...props }, ref) {
163
+ return /* @__PURE__ */ jsx(
164
+ "div",
165
+ {
166
+ ref,
167
+ style: { aspectRatio: String(ratio) },
168
+ className: cn("w-full overflow-hidden", className),
169
+ ...dataSlot("aspect-ratio"),
170
+ ...props,
171
+ children
172
+ }
173
+ );
174
+ });
175
+ const ScrollArea = React.forwardRef(function ScrollArea2({ orientation = "vertical", scrollStep, className, children, style, ...props }, ref) {
176
+ return /* @__PURE__ */ jsx(
177
+ "div",
178
+ {
179
+ ref,
180
+ "data-orientation": orientation,
181
+ onWheel: scrollStep ? (event) => {
182
+ const node = event.currentTarget;
183
+ if (orientation === "vertical") node.scrollTop += event.deltaY * scrollStep;
184
+ else if (orientation === "horizontal") node.scrollLeft += event.deltaY * scrollStep;
185
+ } : void 0,
186
+ style: {
187
+ overflow: orientation === "both" ? "auto" : orientation === "horizontal" ? "auto hidden" : "hidden auto",
188
+ ...style
189
+ },
190
+ className: cn("min-h-0", className),
191
+ ...dataSlot("scroll-area"),
192
+ ...props,
193
+ children
194
+ }
195
+ );
196
+ });
197
+ export {
198
+ AspectRatio,
199
+ Box,
200
+ CONTAINER_SIZES,
201
+ Center,
202
+ Container,
203
+ Divider,
204
+ Flex,
205
+ Grid,
206
+ HStack,
207
+ ScrollArea,
208
+ Spacer,
209
+ Stack,
210
+ VStack
211
+ };
212
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/layout/index.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { Slot } from \"radix-ui\";\nimport { cn } from \"@tea-ui/utils\";\n\nimport { dataSlot } from \"../internal\";\n\n/**\n * TEA UI — layout primitives.\n *\n * Product-agnostic structure, with no opinions about what a page *is*. Density\n * is not a prop here: `Stack` and friends read `--tea-gap` and\n * `--tea-section-gap`, so a compact table inside a comfortable page retunes\n * itself without either container knowing about the other. That is the whole\n * reason density is expressed in CSS rather than in props.\n */\n\nexport interface BoxProps extends React.ComponentProps<\"div\"> {\n asChild?: boolean | undefined;\n as?: React.ElementType | undefined;\n}\n\n/** The escape hatch. A `div` that contributes nothing but structure. */\nexport const Box = React.forwardRef<HTMLDivElement, BoxProps>(function Box(\n { asChild = false, as, className, ...props },\n ref,\n) {\n const Component = (asChild ? Slot.Root : (as ?? \"div\")) as React.ElementType;\n return <Component ref={ref} className={cn(className)} {...dataSlot(\"box\")} {...props} />;\n});\n\nexport interface StackProps extends React.ComponentProps<\"div\"> {\n direction?: \"vertical\" | \"horizontal\" | undefined;\n /**\n * The gap step. `ui` reads the density variable; `section` uses the larger\n * one; a Tailwind class is accepted for deliberate one-offs.\n */\n gap?: \"ui\" | \"section\" | \"none\" | string | undefined;\n align?: React.CSSProperties[\"alignItems\"] | undefined;\n justify?: React.CSSProperties[\"justifyContent\"] | undefined;\n wrap?: boolean | undefined;\n children: React.ReactNode;\n}\n\nconst GAP: Record<\"ui\" | \"section\" | \"none\", string> = {\n ui: \"gap-ui\",\n section: \"section-gap\",\n none: \"gap-0\",\n};\n\nexport const Stack = React.forwardRef<HTMLDivElement, StackProps>(function Stack(\n { direction = \"vertical\", gap = \"ui\", align, justify, wrap = false, className, style, children, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n style={{ alignItems: align, justifyContent: justify, ...style }}\n className={cn(\n \"flex\",\n direction === \"vertical\" ? \"flex-col\" : \"flex-row\",\n wrap && \"flex-wrap\",\n gap === \"ui\" || gap === \"section\" || gap === \"none\" ? GAP[gap] : gap,\n className,\n )}\n {...dataSlot(\"stack\")}\n {...props}\n >\n {children}\n </div>\n );\n});\n\nexport const HStack = React.forwardRef<HTMLDivElement, Omit<StackProps, \"direction\">>(\n function HStack(props, ref) {\n return <Stack ref={ref} direction=\"horizontal\" {...props} />;\n },\n);\n\nexport const VStack = React.forwardRef<HTMLDivElement, Omit<StackProps, \"direction\">>(\n function VStack(props, ref) {\n return <Stack ref={ref} direction=\"vertical\" {...props} />;\n },\n);\n\nexport interface FlexProps extends React.ComponentProps<\"div\"> {\n direction?: React.CSSProperties[\"flexDirection\"] | undefined;\n align?: React.CSSProperties[\"alignItems\"] | undefined;\n justify?: React.CSSProperties[\"justifyContent\"] | undefined;\n wrap?: boolean | undefined;\n grow?: boolean | undefined;\n shrink?: boolean | undefined;\n basis?: string | number | undefined;\n gap?: string | number | undefined;\n children: React.ReactNode;\n}\n\n/** Escape-hatch flexbox, for when a Stack's opinion is wrong. */\nexport const Flex = React.forwardRef<HTMLDivElement, FlexProps>(function Flex(\n { direction, align, justify, wrap, grow, shrink, basis, gap, className, style, children, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n style={{\n flexDirection: direction,\n alignItems: align,\n justifyContent: justify,\n flexGrow: grow ? 1 : undefined,\n flexShrink: shrink ? 1 : undefined,\n flexBasis: basis,\n gap,\n ...style,\n }}\n className={cn(\"flex\", wrap && \"flex-wrap\", className)}\n {...dataSlot(\"flex\")}\n {...props}\n >\n {children}\n </div>\n );\n});\n\nexport interface GridProps extends React.ComponentProps<\"div\"> {\n cols?: number | undefined;\n /**\n * Auto-fit columns that never fall below `minChildWidth`. This is the\n * responsive default for card grids, and it is why a TEA card grid needs no\n * per-breakpoint column counts.\n */\n minChildWidth?: string | undefined;\n gap?: \"ui\" | \"section\" | \"none\" | string | undefined;\n children: React.ReactNode;\n}\n\nexport const Grid = React.forwardRef<HTMLDivElement, GridProps>(function Grid(\n { cols, minChildWidth, gap = \"ui\", className, style, children, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n style={{\n gridTemplateColumns: minChildWidth\n ? `repeat(auto-fit, minmax(min(${minChildWidth}, 100%), 1fr))`\n : cols\n ? `repeat(${cols}, minmax(0, 1fr))`\n : undefined,\n ...style,\n }}\n className={cn(\"grid\", gap === \"ui\" || gap === \"section\" || gap === \"none\" ? GAP[gap] : gap, className)}\n {...dataSlot(\"grid\")}\n {...props}\n >\n {children}\n </div>\n );\n});\n\nexport const CONTAINER_SIZES = {\n xs: \"max-w-xs\",\n sm: \"max-w-sm\",\n md: \"max-w-md\",\n lg: \"max-w-lg\",\n xl: \"max-w-xl\",\n \"2xl\": \"max-w-2xl\",\n \"3xl\": \"max-w-3xl\",\n \"4xl\": \"max-w-4xl\",\n \"5xl\": \"max-w-5xl\",\n \"6xl\": \"max-w-6xl\",\n full: \"max-w-none\",\n} as const;\n\nexport interface ContainerProps extends React.ComponentProps<\"div\"> {\n /** The measure. Admin panels are `full`; Public UI prose uses `lg`–`3xl`. */\n size?: keyof typeof CONTAINER_SIZES | undefined;\n /** Centre the container. Off for a container inside a grid cell. */\n center?: boolean | undefined;\n children: React.ReactNode;\n}\n\nexport const Container = React.forwardRef<HTMLDivElement, ContainerProps>(function Container(\n { size = \"6xl\", center = true, className, children, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n className={cn(CONTAINER_SIZES[size], center && \"mx-auto\", \"w-full px-4 md:px-6\", className)}\n {...dataSlot(\"container\")}\n {...props}\n >\n {children}\n </div>\n );\n});\n\nexport interface SpacerProps extends React.ComponentProps<\"div\"> {\n /** The gap step to insert. */\n size?: \"ui\" | \"section\" | \"none\" | string | undefined;\n /** Grow to fill the remaining space instead of a fixed gap. */\n grow?: boolean | undefined;\n}\n\nexport const Spacer = React.forwardRef<HTMLDivElement, SpacerProps>(function Spacer(\n { size = \"ui\", grow = false, className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n aria-hidden=\"true\"\n className={cn(grow ? \"flex-1\" : size === \"ui\" || size === \"section\" || size === \"none\" ? GAP[size] : size, className)}\n {...dataSlot(\"spacer\")}\n {...props}\n />\n );\n});\n\nexport interface CenterProps extends React.ComponentProps<\"div\"> {\n /** Restrict to a full-viewport centring, for empty routes and error pages. */\n full?: boolean | undefined;\n children: React.ReactNode;\n}\n\nexport const Center = React.forwardRef<HTMLDivElement, CenterProps>(function Center(\n { full = false, className, children, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n className={cn(\"flex flex-col items-center justify-center gap-3 text-center\", full && \"min-h-[60dvh]\", className)}\n {...dataSlot(\"center\")}\n {...props}\n >\n {children}\n </div>\n );\n});\n\nexport interface DividerProps extends Omit<React.ComponentProps<\"hr\">, \"children\"> {\n orientation?: \"horizontal\" | \"vertical\" | undefined;\n /**\n * A label rendered in a gap in the rule. A visible divider that separates\n * nothing is decoration, and a labelled one is a section boundary worth\n * announcing.\n */\n label?: React.ReactNode | undefined;\n}\n\n/**\n * A real `<hr>`. The audit found a divider rendering `role=\"none\"` by default,\n * which removed it from the accessibility tree entirely — so the structural\n * boundary a screen-reader user most needs was the one they could not perceive.\n */\nexport const Divider = React.forwardRef<HTMLHRElement, DividerProps>(function Divider(\n { orientation = \"horizontal\", label, className, ...props },\n ref,\n) {\n if (orientation === \"vertical\") {\n return (\n <div\n ref={ref as unknown as React.Ref<HTMLDivElement>}\n role=\"separator\"\n aria-orientation=\"vertical\"\n className={cn(\"h-full w-px bg-line\", className)}\n {...dataSlot(\"divider\")}\n {...(props as React.ComponentProps<\"div\">)}\n />\n );\n }\n\n if (label) {\n return (\n <div className={cn(\"flex items-center gap-3\", className)} {...dataSlot(\"divider\", \"labelled\")}>\n <span aria-hidden=\"true\" className=\"h-px flex-1 bg-line\" />\n <span className=\"text-label font-medium uppercase tracking-widest text-fg-muted\">{label}</span>\n <span aria-hidden=\"true\" className=\"h-px flex-1 bg-line\" />\n </div>\n );\n }\n\n return (\n <hr\n ref={ref}\n className={cn(\"h-px w-full border-0 bg-line\", className)}\n {...dataSlot(\"divider\")}\n {...props}\n />\n );\n});\n\nexport interface AspectRatioProps extends React.ComponentProps<\"div\"> {\n ratio?: number | undefined;\n children: React.ReactNode;\n}\n\n/** Reserves space before content arrives, so nothing reflows on load. */\nexport const AspectRatio = React.forwardRef<HTMLDivElement, AspectRatioProps>(function AspectRatio(\n { ratio = 16 / 9, className, children, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n style={{ aspectRatio: String(ratio) }}\n className={cn(\"w-full overflow-hidden\", className)}\n {...dataSlot(\"aspect-ratio\")}\n {...props}\n >\n {children}\n </div>\n );\n});\n\nexport interface ScrollAreaProps extends React.ComponentProps<\"div\"> {\n orientation?: \"vertical\" | \"horizontal\" | \"both\" | undefined;\n /** How far a wheel or trackpad gesture scrolls per event. */\n scrollStep?: number | undefined;\n children: React.ReactNode;\n}\n\n/**\n * A styled scroll container.\n *\n * This is *not* Radix's `ScrollArea`, on purpose: that primitive hides the\n * native scrollbar and reinvents a thumb, which costs a full extra layer of\n * layout and breaks native scroll chaining and momentum on touch. Here the\n * browser keeps its own scrollbar, and the token stylesheet styles it — with\n * the standards `scrollbar-color` first, so Firefox is not left on UA defaults\n * as both source projects were.\n */\nexport const ScrollArea = React.forwardRef<HTMLDivElement, ScrollAreaProps>(function ScrollArea(\n { orientation = \"vertical\", scrollStep, className, children, style, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n data-orientation={orientation}\n onWheel={\n scrollStep\n ? (event) => {\n const node = event.currentTarget;\n if (orientation === \"vertical\") node.scrollTop += event.deltaY * scrollStep;\n else if (orientation === \"horizontal\") node.scrollLeft += event.deltaY * scrollStep;\n }\n : undefined\n }\n style={{\n overflow: orientation === \"both\" ? \"auto\" : orientation === \"horizontal\" ? \"auto hidden\" : \"hidden auto\",\n ...style,\n }}\n className={cn(\"min-h-0\", className)}\n {...dataSlot(\"scroll-area\")}\n {...props}\n >\n {children}\n </div>\n );\n});\n"],"mappings":"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","names":["Box","Stack","HStack","VStack","Flex","Grid","Container","Spacer","Center","Divider","AspectRatio","ScrollArea"]}