@funstack/ui 0.0.0-stage → 0.1.0-alpha.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 (179) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +42 -2
  3. package/dist/accordion/accordion-context.d.mts +5 -0
  4. package/dist/accordion/accordion-context.d.mts.map +1 -0
  5. package/dist/accordion/accordion-context.mjs +9 -0
  6. package/dist/accordion/accordion-context.mjs.map +1 -0
  7. package/dist/accordion/accordion-item.d.mts +53 -0
  8. package/dist/accordion/accordion-item.d.mts.map +1 -0
  9. package/dist/accordion/accordion-item.mjs +85 -0
  10. package/dist/accordion/accordion-item.mjs.map +1 -0
  11. package/dist/accordion/accordion-panel.d.mts +40 -0
  12. package/dist/accordion/accordion-panel.d.mts.map +1 -0
  13. package/dist/accordion/accordion-panel.mjs +40 -0
  14. package/dist/accordion/accordion-panel.mjs.map +1 -0
  15. package/dist/accordion/accordion-root.d.mts +90 -0
  16. package/dist/accordion/accordion-root.d.mts.map +1 -0
  17. package/dist/accordion/accordion-root.mjs +229 -0
  18. package/dist/accordion/accordion-root.mjs.map +1 -0
  19. package/dist/accordion/accordion-trigger.d.mts +37 -0
  20. package/dist/accordion/accordion-trigger.d.mts.map +1 -0
  21. package/dist/accordion/accordion-trigger.mjs +81 -0
  22. package/dist/accordion/accordion-trigger.mjs.map +1 -0
  23. package/dist/accordion/index.d.mts +20 -0
  24. package/dist/accordion/index.d.mts.map +1 -0
  25. package/dist/accordion/index.mjs +20 -0
  26. package/dist/accordion/index.mjs.map +1 -0
  27. package/dist/checkbox/checkbox.d.mts +92 -0
  28. package/dist/checkbox/checkbox.d.mts.map +1 -0
  29. package/dist/checkbox/checkbox.mjs +82 -0
  30. package/dist/checkbox/checkbox.mjs.map +1 -0
  31. package/dist/checkbox/index.d.mts +3 -0
  32. package/dist/checkbox/index.mjs +1 -0
  33. package/dist/dialog/dialog-close.d.mts +44 -0
  34. package/dist/dialog/dialog-close.d.mts.map +1 -0
  35. package/dist/dialog/dialog-close.mjs +76 -0
  36. package/dist/dialog/dialog-close.mjs.map +1 -0
  37. package/dist/dialog/dialog-context.mjs +13 -0
  38. package/dist/dialog/dialog-context.mjs.map +1 -0
  39. package/dist/dialog/dialog-description.d.mts +22 -0
  40. package/dist/dialog/dialog-description.d.mts.map +1 -0
  41. package/dist/dialog/dialog-description.mjs +31 -0
  42. package/dist/dialog/dialog-description.mjs.map +1 -0
  43. package/dist/dialog/dialog-form.d.mts +58 -0
  44. package/dist/dialog/dialog-form.d.mts.map +1 -0
  45. package/dist/dialog/dialog-form.mjs +108 -0
  46. package/dist/dialog/dialog-form.mjs.map +1 -0
  47. package/dist/dialog/dialog-popup.d.mts +81 -0
  48. package/dist/dialog/dialog-popup.d.mts.map +1 -0
  49. package/dist/dialog/dialog-popup.mjs +366 -0
  50. package/dist/dialog/dialog-popup.mjs.map +1 -0
  51. package/dist/dialog/dialog-root.d.mts +70 -0
  52. package/dist/dialog/dialog-root.d.mts.map +1 -0
  53. package/dist/dialog/dialog-root.mjs +105 -0
  54. package/dist/dialog/dialog-root.mjs.map +1 -0
  55. package/dist/dialog/dialog-title.d.mts +23 -0
  56. package/dist/dialog/dialog-title.d.mts.map +1 -0
  57. package/dist/dialog/dialog-title.mjs +31 -0
  58. package/dist/dialog/dialog-title.mjs.map +1 -0
  59. package/dist/dialog/dialog-trigger.d.mts +39 -0
  60. package/dist/dialog/dialog-trigger.d.mts.map +1 -0
  61. package/dist/dialog/dialog-trigger.mjs +80 -0
  62. package/dist/dialog/dialog-trigger.mjs.map +1 -0
  63. package/dist/dialog/index.d.mts +28 -0
  64. package/dist/dialog/index.d.mts.map +1 -0
  65. package/dist/dialog/index.mjs +28 -0
  66. package/dist/dialog/index.mjs.map +1 -0
  67. package/dist/index.d.mts +38 -0
  68. package/dist/index.mjs +9 -0
  69. package/dist/internal/action-details.mjs +47 -0
  70. package/dist/internal/action-details.mjs.map +1 -0
  71. package/dist/internal/data-attr.mjs +13 -0
  72. package/dist/internal/data-attr.mjs.map +1 -0
  73. package/dist/internal/merge-props.mjs +119 -0
  74. package/dist/internal/merge-props.mjs.map +1 -0
  75. package/dist/internal/platform-attributes.d.mts +49 -0
  76. package/dist/internal/platform-attributes.d.mts.map +1 -0
  77. package/dist/internal/transition-boundary.mjs +52 -0
  78. package/dist/internal/transition-boundary.mjs.map +1 -0
  79. package/dist/internal/transition-types.mjs +15 -0
  80. package/dist/internal/transition-types.mjs.map +1 -0
  81. package/dist/internal/types.d.mts +38 -0
  82. package/dist/internal/types.d.mts.map +1 -0
  83. package/dist/internal/use-controllable-action.mjs +115 -0
  84. package/dist/internal/use-controllable-action.mjs.map +1 -0
  85. package/dist/internal/use-dev-warning.mjs +21 -0
  86. package/dist/internal/use-dev-warning.mjs.map +1 -0
  87. package/dist/internal/use-form-reset.mjs +50 -0
  88. package/dist/internal/use-form-reset.mjs.map +1 -0
  89. package/dist/internal/use-native-checked.mjs +113 -0
  90. package/dist/internal/use-native-checked.mjs.map +1 -0
  91. package/dist/internal/use-native-group-reconciliation.mjs +64 -0
  92. package/dist/internal/use-native-group-reconciliation.mjs.map +1 -0
  93. package/dist/internal/use-render.mjs +51 -0
  94. package/dist/internal/use-render.mjs.map +1 -0
  95. package/dist/internal/use-until-found.mjs +40 -0
  96. package/dist/internal/use-until-found.mjs.map +1 -0
  97. package/dist/radio-group/index.d.mts +20 -0
  98. package/dist/radio-group/index.d.mts.map +1 -0
  99. package/dist/radio-group/index.mjs +20 -0
  100. package/dist/radio-group/index.mjs.map +1 -0
  101. package/dist/radio-group/radio-group-context.mjs +13 -0
  102. package/dist/radio-group/radio-group-context.mjs.map +1 -0
  103. package/dist/radio-group/radio-group-item.d.mts +54 -0
  104. package/dist/radio-group/radio-group-item.d.mts.map +1 -0
  105. package/dist/radio-group/radio-group-item.mjs +88 -0
  106. package/dist/radio-group/radio-group-item.mjs.map +1 -0
  107. package/dist/radio-group/radio-group-label.d.mts +21 -0
  108. package/dist/radio-group/radio-group-label.d.mts.map +1 -0
  109. package/dist/radio-group/radio-group-label.mjs +30 -0
  110. package/dist/radio-group/radio-group-label.mjs.map +1 -0
  111. package/dist/radio-group/radio-group-root.d.mts +92 -0
  112. package/dist/radio-group/radio-group-root.d.mts.map +1 -0
  113. package/dist/radio-group/radio-group-root.mjs +137 -0
  114. package/dist/radio-group/radio-group-root.mjs.map +1 -0
  115. package/dist/switch/index.d.mts +3 -0
  116. package/dist/switch/index.mjs +1 -0
  117. package/dist/switch/switch.d.mts +78 -0
  118. package/dist/switch/switch.d.mts.map +1 -0
  119. package/dist/switch/switch.mjs +77 -0
  120. package/dist/switch/switch.mjs.map +1 -0
  121. package/dist/tabs/index.d.mts +24 -0
  122. package/dist/tabs/index.d.mts.map +1 -0
  123. package/dist/tabs/index.mjs +23 -0
  124. package/dist/tabs/index.mjs.map +1 -0
  125. package/dist/tabs/tabs-context.d.mts +7 -0
  126. package/dist/tabs/tabs-context.d.mts.map +1 -0
  127. package/dist/tabs/tabs-context.mjs +13 -0
  128. package/dist/tabs/tabs-context.mjs.map +1 -0
  129. package/dist/tabs/tabs-ids.mjs +39 -0
  130. package/dist/tabs/tabs-ids.mjs.map +1 -0
  131. package/dist/tabs/tabs-indicator.d.mts +32 -0
  132. package/dist/tabs/tabs-indicator.d.mts.map +1 -0
  133. package/dist/tabs/tabs-indicator.mjs +49 -0
  134. package/dist/tabs/tabs-indicator.mjs.map +1 -0
  135. package/dist/tabs/tabs-list.d.mts +34 -0
  136. package/dist/tabs/tabs-list.d.mts.map +1 -0
  137. package/dist/tabs/tabs-list.mjs +122 -0
  138. package/dist/tabs/tabs-list.mjs.map +1 -0
  139. package/dist/tabs/tabs-panel.d.mts +50 -0
  140. package/dist/tabs/tabs-panel.d.mts.map +1 -0
  141. package/dist/tabs/tabs-panel.mjs +81 -0
  142. package/dist/tabs/tabs-panel.mjs.map +1 -0
  143. package/dist/tabs/tabs-root.d.mts +64 -0
  144. package/dist/tabs/tabs-root.d.mts.map +1 -0
  145. package/dist/tabs/tabs-root.mjs +90 -0
  146. package/dist/tabs/tabs-root.mjs.map +1 -0
  147. package/dist/tabs/tabs-tab.d.mts +49 -0
  148. package/dist/tabs/tabs-tab.d.mts.map +1 -0
  149. package/dist/tabs/tabs-tab.mjs +86 -0
  150. package/dist/tabs/tabs-tab.mjs.map +1 -0
  151. package/dist/toggle/index.d.mts +3 -0
  152. package/dist/toggle/index.mjs +1 -0
  153. package/dist/toggle/toggle.d.mts +57 -0
  154. package/dist/toggle/toggle.d.mts.map +1 -0
  155. package/dist/toggle/toggle.mjs +59 -0
  156. package/dist/toggle/toggle.mjs.map +1 -0
  157. package/dist/tooltip/index.d.mts +20 -0
  158. package/dist/tooltip/index.d.mts.map +1 -0
  159. package/dist/tooltip/index.mjs +19 -0
  160. package/dist/tooltip/index.mjs.map +1 -0
  161. package/dist/tooltip/tooltip-context.d.mts +5 -0
  162. package/dist/tooltip/tooltip-context.d.mts.map +1 -0
  163. package/dist/tooltip/tooltip-context.mjs +13 -0
  164. package/dist/tooltip/tooltip-context.mjs.map +1 -0
  165. package/dist/tooltip/tooltip-popup.d.mts +49 -0
  166. package/dist/tooltip/tooltip-popup.d.mts.map +1 -0
  167. package/dist/tooltip/tooltip-popup.mjs +57 -0
  168. package/dist/tooltip/tooltip-popup.mjs.map +1 -0
  169. package/dist/tooltip/tooltip-root.d.mts +41 -0
  170. package/dist/tooltip/tooltip-root.d.mts.map +1 -0
  171. package/dist/tooltip/tooltip-root.mjs +86 -0
  172. package/dist/tooltip/tooltip-root.mjs.map +1 -0
  173. package/dist/tooltip/tooltip-trigger.d.mts +38 -0
  174. package/dist/tooltip/tooltip-trigger.d.mts.map +1 -0
  175. package/dist/tooltip/tooltip-trigger.mjs +55 -0
  176. package/dist/tooltip/tooltip-trigger.mjs.map +1 -0
  177. package/dist/tooltip/use-tooltip-interaction.mjs +131 -0
  178. package/dist/tooltip/use-tooltip-interaction.mjs.map +1 -0
  179. package/package.json +70 -3
@@ -0,0 +1,78 @@
1
+ import { ActionDetails, ChangeAction, ChangeReason, RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { CSSProperties, ComponentPropsWithRef, ViewTransitionClass } from "react";
3
+ //#region src/switch/switch.d.ts
4
+ export interface SwitchState {
5
+ /** Optimistic checked state: what the user sees, including an in-flight change. */
6
+ readonly checked: boolean;
7
+ /** An action dispatched by this Switch is in flight (including the resulting render). */
8
+ readonly pending: boolean;
9
+ /** The `disabled` prop. */
10
+ readonly disabled: boolean;
11
+ }
12
+ /** Details passed as the second argument of `checkedChangeAction`. */
13
+ export interface SwitchCheckedChangeDetails extends ActionDetails {
14
+ /**
15
+ * `"activate"`: the user toggled the switch (click, Space, label click); `event` is the native
16
+ * `click` event. `"platform"`: the browser changed the value itself: a form reset (`event` is
17
+ * the `reset` event) or a click before hydration / Firefox form-state restoration found on
18
+ * mount (`event` is `undefined`).
19
+ */
20
+ readonly reason: ChangeReason;
21
+ }
22
+ export interface SwitchProps extends Omit<ComponentPropsWithRef<"input">, "type" | "role" | "checked" | "defaultChecked" | "onChange" | "aria-checked" | "className" | "style" | "children"> {
23
+ /** Controlled checked state. `undefined` means uncontrolled. */
24
+ checked?: boolean;
25
+ /**
26
+ * Initial checked state in uncontrolled mode (default `false`). Also the value a form reset
27
+ * returns to: the latest prop value is mirrored into the input's `defaultChecked`.
28
+ */
29
+ defaultChecked?: boolean;
30
+ /**
31
+ * Called with the next checked state on every change. Runs inside the Switch's transition; may
32
+ * be sync or async (also a Server Function). Rejections reach the nearest Error Boundary.
33
+ * Also called with `details.reason === "platform"` for a form reset or a pre-hydration click.
34
+ *
35
+ * A `setState` placed after your own `await` is not a Transition update; wrap it in
36
+ * `startTransition` (React limitation). `details.event.preventDefault()` is not a veto and is
37
+ * unsupported here; use controlled mode to veto a change.
38
+ */
39
+ checkedChangeAction?: ChangeAction<boolean, SwitchCheckedChangeDetails>;
40
+ /** Renders a natively disabled input; never dispatches. Default `false`. */
41
+ disabled?: boolean;
42
+ /** Submitted as the input's value when checked. Default `"on"` (platform). */
43
+ value?: string;
44
+ /**
45
+ * Forwarded as `default` to the Switch's `<ViewTransition>`. Default: no boundary. Animates the
46
+ * commit that ends an action, not the optimistic flip; animate the flip with CSS on `:checked`.
47
+ *
48
+ * Static: without `transition` the part renders no `<ViewTransition>`; changing it between
49
+ * `undefined` and a value remounts the element. Any value, `"none"` included, makes a mount or
50
+ * `<Activity>` reveal of this part inside a transition start a document view transition: the
51
+ * page cross-fades for about 250 ms and the next transition commit waits for it (issue #35).
52
+ */
53
+ transition?: ViewTransitionClass;
54
+ className?: StateValue<string | undefined, SwitchState>;
55
+ style?: StateValue<CSSProperties | undefined, SwitchState>;
56
+ /**
57
+ * Replace the rendered element (element form) or take over rendering (function form). Must keep
58
+ * an `<input type="checkbox">`.
59
+ */
60
+ render?: RenderProp<ComponentPropsWithRef<"input">, SwitchState>;
61
+ }
62
+ /**
63
+ * An on/off switch (WAI-ARIA "Switch" pattern, HTML checkbox input variant). Renders
64
+ * `<input type="checkbox" role="switch">` whose DOM `checked` is the optimistic state; the input
65
+ * carries `data-pending` while `checkedChangeAction` is in flight and takes part in forms natively.
66
+ *
67
+ * Owned keys (a `render` element's value is overridden): `type`, `role`, `checked`,
68
+ * `defaultChecked`, `aria-checked` (always removed), `disabled`, `data-checked`, `data-pending`,
69
+ * `data-disabled`. Event handlers are merged rather than owned: a `render` element's own
70
+ * `onChange` (or `onClick`) runs first, then the library's; an `onClick` that calls
71
+ * `preventDefault()` cancels the change (nothing is dispatched).
72
+ */
73
+ export declare function Switch({ checked, defaultChecked, checkedChangeAction, disabled, transition, className, style, render, ...rest }: SwitchProps): import("react").ReactNode;
74
+ export declare namespace Switch {
75
+ var displayName: string;
76
+ }
77
+ //#endregion
78
+ //# sourceMappingURL=switch.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"switch.d.mts","names":[],"sources":["../../src/switch/switch.tsx"],"mappings":";;;iBAiBiB;;WAEN;;WAEA;;WAEA;;;iBAIM,mCAAmC;;;;;;;WAOzC,QAAQ;;iBAGF,oBAAoB,KACnC;;EAYA;;;;;EAKA;;;;;;;;;;EAUA,sBAAsB,sBAAsB;;EAE5C;;EAEA;;;;;;;;;;EAUA,aAAa;EACb,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;;;;EAK9C,SAAS,WAAW,gCAAgC;;;;;;;;;;;;;wBActC,SACd,SACA,gBACA,qBACA,UACA,YACA,WACA,OACA,WACG,QACF,8BAAW;yBAVE"}
@@ -0,0 +1,77 @@
1
+ "use client";
2
+ import { useTransitionBoundary } from "../internal/transition-boundary.mjs";
3
+ import { createActionDetails } from "../internal/action-details.mjs";
4
+ import { dataAttr } from "../internal/data-attr.mjs";
5
+ import { transitionTypes } from "../internal/transition-types.mjs";
6
+ import { useControllableAction } from "../internal/use-controllable-action.mjs";
7
+ import { useRender } from "../internal/use-render.mjs";
8
+ import { guardCheckableChange, useNativeChecked } from "../internal/use-native-checked.mjs";
9
+ //#region src/switch/switch.tsx
10
+ /**
11
+ * An on/off switch (WAI-ARIA "Switch" pattern, HTML checkbox input variant). Renders
12
+ * `<input type="checkbox" role="switch">` whose DOM `checked` is the optimistic state; the input
13
+ * carries `data-pending` while `checkedChangeAction` is in flight and takes part in forms natively.
14
+ *
15
+ * Owned keys (a `render` element's value is overridden): `type`, `role`, `checked`,
16
+ * `defaultChecked`, `aria-checked` (always removed), `disabled`, `data-checked`, `data-pending`,
17
+ * `data-disabled`. Event handlers are merged rather than owned: a `render` element's own
18
+ * `onChange` (or `onClick`) runs first, then the library's; an `onClick` that calls
19
+ * `preventDefault()` cancels the change (nothing is dispatched).
20
+ */
21
+ function Switch({ checked, defaultChecked, checkedChangeAction, disabled = false, transition, className, style, render, ...rest }) {
22
+ const { optimistic, pending, dispatch, latestInFlight } = useControllableAction({
23
+ controlled: checked,
24
+ defaultValue: defaultChecked,
25
+ initialValue: false,
26
+ action: checkedChangeAction,
27
+ transitionTypes: (next) => transitionTypes("switch", next ? "on" : "off"),
28
+ warn: {
29
+ component: "Switch",
30
+ prop: "checked"
31
+ }
32
+ });
33
+ const inputRef = useNativeChecked({
34
+ type: "checkbox",
35
+ component: "Switch",
36
+ checked: optimistic,
37
+ defaultChecked: checked === void 0 ? defaultChecked ?? false : void 0,
38
+ latestInFlight,
39
+ onPlatformChange: (next, event) => dispatch(next, createActionDetails(event, { reason: "platform" }))
40
+ });
41
+ const element = useRender({
42
+ defaultElement: "input",
43
+ render,
44
+ state: {
45
+ checked: optimistic,
46
+ pending,
47
+ disabled
48
+ },
49
+ className,
50
+ style,
51
+ children: void 0,
52
+ ref: inputRef,
53
+ props: rest,
54
+ libraryProps: {
55
+ type: "checkbox",
56
+ role: "switch",
57
+ checked: optimistic,
58
+ defaultChecked: void 0,
59
+ "aria-checked": void 0,
60
+ disabled,
61
+ "data-checked": dataAttr(optimistic),
62
+ "data-pending": dataAttr(pending),
63
+ "data-disabled": dataAttr(disabled),
64
+ onChange: (event) => {
65
+ if (disabled) return;
66
+ const nativeEvent = event.nativeEvent;
67
+ guardCheckableChange(nativeEvent, () => dispatch(!optimistic, createActionDetails(nativeEvent, { reason: "activate" })), "Switch");
68
+ }
69
+ }
70
+ });
71
+ return useTransitionBoundary("Switch", transition, element);
72
+ }
73
+ Switch.displayName = "Switch";
74
+ //#endregion
75
+ export { Switch };
76
+
77
+ //# sourceMappingURL=switch.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"switch.mjs","names":[],"sources":["../../src/switch/switch.tsx"],"sourcesContent":["\"use client\";\nimport type { ChangeEvent, ComponentPropsWithRef, CSSProperties, ViewTransitionClass } from \"react\";\nimport { useTransitionBoundary } from \"../internal/transition-boundary.js\";\nimport { createActionDetails } from \"../internal/action-details.js\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport { transitionTypes } from \"../internal/transition-types.js\";\nimport type {\n ActionDetails,\n ChangeAction,\n ChangeReason,\n RenderProp,\n StateValue,\n} from \"../internal/types.js\";\nimport { useControllableAction } from \"../internal/use-controllable-action.js\";\nimport { guardCheckableChange, useNativeChecked } from \"../internal/use-native-checked.js\";\nimport { useRender } from \"../internal/use-render.js\";\n\nexport interface SwitchState {\n /** Optimistic checked state: what the user sees, including an in-flight change. */\n readonly checked: boolean;\n /** An action dispatched by this Switch is in flight (including the resulting render). */\n readonly pending: boolean;\n /** The `disabled` prop. */\n readonly disabled: boolean;\n}\n\n/** Details passed as the second argument of `checkedChangeAction`. */\nexport interface SwitchCheckedChangeDetails extends ActionDetails {\n /**\n * `\"activate\"`: the user toggled the switch (click, Space, label click); `event` is the native\n * `click` event. `\"platform\"`: the browser changed the value itself: a form reset (`event` is\n * the `reset` event) or a click before hydration / Firefox form-state restoration found on\n * mount (`event` is `undefined`).\n */\n readonly reason: ChangeReason;\n}\n\nexport interface SwitchProps extends Omit<\n ComponentPropsWithRef<\"input\">,\n | \"type\"\n | \"role\"\n | \"checked\"\n | \"defaultChecked\"\n | \"onChange\"\n | \"aria-checked\"\n | \"className\"\n | \"style\"\n | \"children\"\n> {\n /** Controlled checked state. `undefined` means uncontrolled. */\n checked?: boolean;\n /**\n * Initial checked state in uncontrolled mode (default `false`). Also the value a form reset\n * returns to: the latest prop value is mirrored into the input's `defaultChecked`.\n */\n defaultChecked?: boolean;\n /**\n * Called with the next checked state on every change. Runs inside the Switch's transition; may\n * be sync or async (also a Server Function). Rejections reach the nearest Error Boundary.\n * Also called with `details.reason === \"platform\"` for a form reset or a pre-hydration click.\n *\n * A `setState` placed after your own `await` is not a Transition update; wrap it in\n * `startTransition` (React limitation). `details.event.preventDefault()` is not a veto and is\n * unsupported here; use controlled mode to veto a change.\n */\n checkedChangeAction?: ChangeAction<boolean, SwitchCheckedChangeDetails>;\n /** Renders a natively disabled input; never dispatches. Default `false`. */\n disabled?: boolean;\n /** Submitted as the input's value when checked. Default `\"on\"` (platform). */\n value?: string;\n /**\n * Forwarded as `default` to the Switch's `<ViewTransition>`. Default: no boundary. Animates the\n * commit that ends an action, not the optimistic flip; animate the flip with CSS on `:checked`.\n *\n * Static: without `transition` the part renders no `<ViewTransition>`; changing it between\n * `undefined` and a value remounts the element. Any value, `\"none\"` included, makes a mount or\n * `<Activity>` reveal of this part inside a transition start a document view transition: the\n * page cross-fades for about 250 ms and the next transition commit waits for it (issue #35).\n */\n transition?: ViewTransitionClass;\n className?: StateValue<string | undefined, SwitchState>;\n style?: StateValue<CSSProperties | undefined, SwitchState>;\n /**\n * Replace the rendered element (element form) or take over rendering (function form). Must keep\n * an `<input type=\"checkbox\">`.\n */\n render?: RenderProp<ComponentPropsWithRef<\"input\">, SwitchState>;\n}\n\n/**\n * An on/off switch (WAI-ARIA \"Switch\" pattern, HTML checkbox input variant). Renders\n * `<input type=\"checkbox\" role=\"switch\">` whose DOM `checked` is the optimistic state; the input\n * carries `data-pending` while `checkedChangeAction` is in flight and takes part in forms natively.\n *\n * Owned keys (a `render` element's value is overridden): `type`, `role`, `checked`,\n * `defaultChecked`, `aria-checked` (always removed), `disabled`, `data-checked`, `data-pending`,\n * `data-disabled`. Event handlers are merged rather than owned: a `render` element's own\n * `onChange` (or `onClick`) runs first, then the library's; an `onClick` that calls\n * `preventDefault()` cancels the change (nothing is dispatched).\n */\nexport function Switch({\n checked,\n defaultChecked,\n checkedChangeAction,\n disabled = false,\n transition,\n className,\n style,\n render,\n ...rest\n}: SwitchProps) {\n const { optimistic, pending, dispatch, latestInFlight } = useControllableAction<\n boolean,\n SwitchCheckedChangeDetails\n >({\n controlled: checked,\n defaultValue: defaultChecked,\n initialValue: false,\n action: checkedChangeAction,\n transitionTypes: (next) => transitionTypes(\"switch\", next ? \"on\" : \"off\"),\n warn: { component: \"Switch\", prop: \"checked\" },\n });\n\n const inputRef = useNativeChecked({\n type: \"checkbox\",\n component: \"Switch\",\n checked: optimistic,\n // Uncontrolled: the latest prop is the reset target. Controlled: React's mount-time value.\n defaultChecked: checked === undefined ? (defaultChecked ?? false) : undefined,\n latestInFlight,\n onPlatformChange: (next, event) =>\n dispatch(next, createActionDetails(event, { reason: \"platform\" })),\n });\n\n const state: SwitchState = { checked: optimistic, pending, disabled };\n\n const element = useRender<ComponentPropsWithRef<\"input\">, SwitchState>({\n defaultElement: \"input\",\n render,\n state,\n className,\n style,\n children: undefined,\n ref: inputRef,\n // Pass-through layer: a `render` element's own props win over these.\n props: rest,\n // Library-controlled layer (owned keys, conventions 1.2).\n libraryProps: {\n type: \"checkbox\",\n role: \"switch\",\n // The DOM state is the optimistic state (conventions 3.2): react-dom restores a controlled\n // input to its props after every event, and only the sync-lane optimistic value survives.\n checked: optimistic,\n defaultChecked: undefined,\n \"aria-checked\": undefined,\n // Owned so that native disabledness, `data-disabled` and the click guard always agree: a\n // `render` element cannot re-enable (`disabled={false}`) or disable the input on its own.\n disabled,\n \"data-checked\": dataAttr(optimistic),\n \"data-pending\": dataAttr(pending),\n \"data-disabled\": dataAttr(disabled),\n onChange: (event: ChangeEvent<HTMLInputElement>) => {\n // A natively disabled input fires no click; this covers `render` elements that do not\n // honour `disabled`.\n if (disabled) return;\n const nativeEvent = event.nativeEvent;\n guardCheckableChange(\n nativeEvent,\n () => dispatch(!optimistic, createActionDetails(nativeEvent, { reason: \"activate\" })),\n \"Switch\",\n );\n },\n },\n });\n\n return useTransitionBoundary(\"Switch\", transition, element);\n}\n\nSwitch.displayName = \"Switch\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAoGA,SAAgB,OAAO,EACrB,SACA,gBACA,qBACA,WAAW,OACX,YACA,WACA,OACA,QACA,GAAG,QACW;CACd,MAAM,EAAE,YAAY,SAAS,UAAU,mBAAmB,sBAGxD;EACA,YAAY;EACZ,cAAc;EACd,cAAc;EACd,QAAQ;EACR,kBAAkB,SAAS,gBAAgB,UAAU,OAAO,OAAO,KAAK;EACxE,MAAM;GAAE,WAAW;GAAU,MAAM;EAAU;CAC/C,CAAC;CAED,MAAM,WAAW,iBAAiB;EAChC,MAAM;EACN,WAAW;EACX,SAAS;EAET,gBAAgB,YAAY,KAAA,IAAa,kBAAkB,QAAS,KAAA;EACpE;EACA,mBAAmB,MAAM,UACvB,SAAS,MAAM,oBAAoB,OAAO,EAAE,QAAQ,WAAW,CAAC,CAAC;CACrE,CAAC;CAID,MAAM,UAAU,UAAuD;EACrE,gBAAgB;EAChB;EACA,OAAA;GAL2B,SAAS;GAAY;GAAS;EAKrD;EACJ;EACA;EACA,UAAU,KAAA;EACV,KAAK;EAEL,OAAO;EAEP,cAAc;GACZ,MAAM;GACN,MAAM;GAGN,SAAS;GACT,gBAAgB,KAAA;GAChB,gBAAgB,KAAA;GAGhB;GACA,gBAAgB,SAAS,UAAU;GACnC,gBAAgB,SAAS,OAAO;GAChC,iBAAiB,SAAS,QAAQ;GAClC,WAAW,UAAyC;IAGlD,IAAI,UAAU;IACd,MAAM,cAAc,MAAM;IAC1B,qBACE,mBACM,SAAS,CAAC,YAAY,oBAAoB,aAAa,EAAE,QAAQ,WAAW,CAAC,CAAC,GACpF,QACF;GACF;EACF;CACF,CAAC;CAED,OAAO,sBAAsB,UAAU,YAAY,OAAO;AAC5D;AAEA,OAAO,cAAc"}
@@ -0,0 +1,24 @@
1
+ import "../internal/platform-attributes.mjs";
2
+ import { TabsActivationMode, TabsOrientation } from "./tabs-context.mjs";
3
+ import { TabsIndicator, TabsIndicatorProps, TabsIndicatorState } from "./tabs-indicator.mjs";
4
+ import { TabsList, TabsListProps, TabsListState } from "./tabs-list.mjs";
5
+ import { TabsPanel, TabsPanelProps, TabsPanelState } from "./tabs-panel.mjs";
6
+ import { TabsRoot, TabsRootProps, TabsRootState, TabsValueChangeDetails } from "./tabs-root.mjs";
7
+ import { TabsTab, TabsTabProps, TabsTabState } from "./tabs-tab.mjs";
8
+ //#region src/tabs/index.d.ts
9
+ /**
10
+ * Tabs (WAI-ARIA APG Tabs pattern): `Root` (div) > `List` (tablist) > `Tab` (button) and an
11
+ * optional `Indicator` (span), plus one `Panel` (tabpanel) per tab. Tabs and the Indicator render
12
+ * the optimistic selection; Panels render the committed one, so a suspending panel keeps the
13
+ * current panel on screen while the Root and the selected Tab show `data-pending`.
14
+ */
15
+ export declare const Tabs: {
16
+ readonly Root: typeof TabsRoot;
17
+ readonly List: typeof TabsList;
18
+ readonly Tab: typeof TabsTab;
19
+ readonly Panel: typeof TabsPanel;
20
+ readonly Indicator: typeof TabsIndicator;
21
+ };
22
+ //#endregion
23
+ export type { TabsActivationMode, TabsIndicatorProps, TabsIndicatorState, TabsListProps, TabsListState, TabsOrientation, TabsPanelProps, TabsPanelState, TabsRootProps, TabsRootState, TabsTabProps, TabsTabState, TabsValueChangeDetails };
24
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/tabs/index.ts"],"mappings":";;;;;;;;;;;;;;qBAea;WACX,aAAI;WACJ,aAAI;WACJ,YAAG;WACH,cAAK;WACL,kBAAS"}
@@ -0,0 +1,23 @@
1
+ import { TabsIndicator } from "./tabs-indicator.mjs";
2
+ import { TabsList } from "./tabs-list.mjs";
3
+ import { TabsPanel } from "./tabs-panel.mjs";
4
+ import { TabsRoot } from "./tabs-root.mjs";
5
+ import { TabsTab } from "./tabs-tab.mjs";
6
+ //#region src/tabs/index.ts
7
+ /**
8
+ * Tabs (WAI-ARIA APG Tabs pattern): `Root` (div) > `List` (tablist) > `Tab` (button) and an
9
+ * optional `Indicator` (span), plus one `Panel` (tabpanel) per tab. Tabs and the Indicator render
10
+ * the optimistic selection; Panels render the committed one, so a suspending panel keeps the
11
+ * current panel on screen while the Root and the selected Tab show `data-pending`.
12
+ */
13
+ const Tabs = {
14
+ Root: TabsRoot,
15
+ List: TabsList,
16
+ Tab: TabsTab,
17
+ Panel: TabsPanel,
18
+ Indicator: TabsIndicator
19
+ };
20
+ //#endregion
21
+ export { Tabs };
22
+
23
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/tabs/index.ts"],"sourcesContent":["// Ships the platform attribute typings with this subpath (conventions 7.4). Type-only: the\n// built JS has no import of it.\nimport \"../internal/platform-attributes.js\";\nimport { TabsIndicator } from \"./tabs-indicator.js\";\nimport { TabsList } from \"./tabs-list.js\";\nimport { TabsPanel } from \"./tabs-panel.js\";\nimport { TabsRoot } from \"./tabs-root.js\";\nimport { TabsTab } from \"./tabs-tab.js\";\n\n/**\n * Tabs (WAI-ARIA APG Tabs pattern): `Root` (div) > `List` (tablist) > `Tab` (button) and an\n * optional `Indicator` (span), plus one `Panel` (tabpanel) per tab. Tabs and the Indicator render\n * the optimistic selection; Panels render the committed one, so a suspending panel keeps the\n * current panel on screen while the Root and the selected Tab show `data-pending`.\n */\nexport const Tabs = {\n Root: TabsRoot,\n List: TabsList,\n Tab: TabsTab,\n Panel: TabsPanel,\n Indicator: TabsIndicator,\n} as const;\n\nexport type {\n TabsRootProps,\n TabsRootState,\n TabsValueChangeDetails,\n TabsOrientation,\n TabsActivationMode,\n} from \"./tabs-root.js\";\nexport type { TabsListProps, TabsListState } from \"./tabs-list.js\";\nexport type { TabsTabProps, TabsTabState } from \"./tabs-tab.js\";\nexport type { TabsPanelProps, TabsPanelState } from \"./tabs-panel.js\";\nexport type { TabsIndicatorProps, TabsIndicatorState } from \"./tabs-indicator.js\";\n"],"mappings":";;;;;;;;;;;;AAeA,MAAa,OAAO;CAClB,MAAM;CACN,MAAM;CACN,KAAK;CACL,OAAO;CACP,WAAW;AACb"}
@@ -0,0 +1,7 @@
1
+ //#region src/tabs/tabs-context.d.ts
2
+ export type TabsOrientation = "horizontal" | "vertical";
3
+ export type TabsActivationMode = "automatic" | "manual";
4
+ /** Why a Tabs change was dispatched (design section 3.1). */
5
+ export type TabsChangeReason = "activate" | "platform";
6
+ //#endregion
7
+ //# sourceMappingURL=tabs-context.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs-context.d.mts","names":[],"sources":["../../src/tabs/tabs-context.ts"],"mappings":";YAGY;YACA;;YAGA"}
@@ -0,0 +1,13 @@
1
+ "use client";
2
+ import { createContext, use } from "react";
3
+ //#region src/tabs/tabs-context.ts
4
+ const TabsContext = createContext(null);
5
+ function useTabsContext(part) {
6
+ const context = use(TabsContext);
7
+ if (context === null) throw new Error(`${part} must be rendered inside a Tabs.Root.`);
8
+ return context;
9
+ }
10
+ //#endregion
11
+ export { TabsContext, useTabsContext };
12
+
13
+ //# sourceMappingURL=tabs-context.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs-context.mjs","names":[],"sources":["../../src/tabs/tabs-context.ts"],"sourcesContent":["\"use client\";\nimport { createContext, use } from \"react\";\n\nexport type TabsOrientation = \"horizontal\" | \"vertical\";\nexport type TabsActivationMode = \"automatic\" | \"manual\";\n\n/** Why a Tabs change was dispatched (design section 3.1). */\nexport type TabsChangeReason = \"activate\" | \"platform\";\n\nexport interface TabsContextValue {\n readonly rootId: string;\n /** Committed value: Panels render this. */\n readonly committed: string | null;\n /** Optimistic value: Tabs and the Indicator render this. */\n readonly optimistic: string | null;\n readonly pending: boolean;\n readonly orientation: TabsOrientation;\n readonly activationMode: TabsActivationMode;\n readonly loop: boolean;\n readonly disabled: boolean;\n /** Dispatches a change. No-op if `value` is the optimistic value. Never checks `disabled`. */\n readonly select: (value: string, event: Event | undefined, reason: TabsChangeReason) => void;\n}\n\nexport const TabsContext = createContext<TabsContextValue | null>(null);\n\nexport function useTabsContext(part: string): TabsContextValue {\n const context = use(TabsContext);\n if (context === null) {\n throw new Error(`${part} must be rendered inside a Tabs.Root.`);\n }\n return context;\n}\n"],"mappings":";;;AAwBA,MAAa,cAAc,cAAuC,IAAI;AAEtE,SAAgB,eAAe,MAAgC;CAC7D,MAAM,UAAU,IAAI,WAAW;CAC/B,IAAI,YAAY,MACd,MAAM,IAAI,MAAM,GAAG,KAAK,sCAAsC;CAEhE,OAAO;AACT"}
@@ -0,0 +1,39 @@
1
+ //#region src/tabs/tabs-ids.ts
2
+ /**
3
+ * Id derivation for Tabs (design section 6). Every part derives its ids from the Root's `useId`
4
+ * and the tab `value`, so there is no registration pass and no extra render.
5
+ *
6
+ * No `"use client"` directive: pure functions, importable from anywhere (conventions 7.1).
7
+ */
8
+ function escapeChar(char) {
9
+ return `_${(char.codePointAt(0) ?? 0).toString(16)}_`;
10
+ }
11
+ /**
12
+ * Makes `value` safe as both an HTML id fragment and a CSS dashed-ident suffix. Every character
13
+ * outside `[A-Za-z0-9-]` becomes `_<hex code point>_`. `_` itself is escaped too (`_5f_`), so the
14
+ * mapping is injective: `"a b"` and `"a_20_"` never share an id.
15
+ */
16
+ function slug(value) {
17
+ return value.replace(/[^A-Za-z0-9-]/gu, escapeChar);
18
+ }
19
+ /**
20
+ * The Root id from `useId` (`_r_1_` on React 19.3) is already a valid id and CSS ident; anything
21
+ * else (a custom `identifierPrefix`) is escaped defensively for the anchor name, keeping `_`.
22
+ */
23
+ function cssSafeRootId(rootId) {
24
+ return rootId.replace(/[^A-Za-z0-9_-]/gu, escapeChar);
25
+ }
26
+ function tabId(rootId, value) {
27
+ return `${rootId}-tab-${slug(value)}`;
28
+ }
29
+ function panelId(rootId, value) {
30
+ return `${rootId}-panel-${slug(value)}`;
31
+ }
32
+ /** `anchor-name` of a Tab, and `position-anchor` of the Indicator that points at it. */
33
+ function anchorName(rootId, value) {
34
+ return `--fsui-tab-${cssSafeRootId(rootId)}-tab-${slug(value)}`;
35
+ }
36
+ //#endregion
37
+ export { anchorName, panelId, slug, tabId };
38
+
39
+ //# sourceMappingURL=tabs-ids.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs-ids.mjs","names":[],"sources":["../../src/tabs/tabs-ids.ts"],"sourcesContent":["/**\n * Id derivation for Tabs (design section 6). Every part derives its ids from the Root's `useId`\n * and the tab `value`, so there is no registration pass and no extra render.\n *\n * No `\"use client\"` directive: pure functions, importable from anywhere (conventions 7.1).\n */\n\nfunction escapeChar(char: string): string {\n return `_${(char.codePointAt(0) ?? 0).toString(16)}_`;\n}\n\n/**\n * Makes `value` safe as both an HTML id fragment and a CSS dashed-ident suffix. Every character\n * outside `[A-Za-z0-9-]` becomes `_<hex code point>_`. `_` itself is escaped too (`_5f_`), so the\n * mapping is injective: `\"a b\"` and `\"a_20_\"` never share an id.\n */\nexport function slug(value: string): string {\n return value.replace(/[^A-Za-z0-9-]/gu, escapeChar);\n}\n\n/**\n * The Root id from `useId` (`_r_1_` on React 19.3) is already a valid id and CSS ident; anything\n * else (a custom `identifierPrefix`) is escaped defensively for the anchor name, keeping `_`.\n */\nfunction cssSafeRootId(rootId: string): string {\n return rootId.replace(/[^A-Za-z0-9_-]/gu, escapeChar);\n}\n\nexport function tabId(rootId: string, value: string): string {\n return `${rootId}-tab-${slug(value)}`;\n}\n\nexport function panelId(rootId: string, value: string): string {\n return `${rootId}-panel-${slug(value)}`;\n}\n\n/** `anchor-name` of a Tab, and `position-anchor` of the Indicator that points at it. */\nexport function anchorName(rootId: string, value: string): `--${string}` {\n return `--fsui-tab-${cssSafeRootId(rootId)}-tab-${slug(value)}`;\n}\n"],"mappings":";;;;;;;AAOA,SAAS,WAAW,MAAsB;CACxC,OAAO,KAAK,KAAK,YAAY,CAAC,KAAK,EAAA,CAAG,SAAS,EAAE,EAAE;AACrD;;;;;;AAOA,SAAgB,KAAK,OAAuB;CAC1C,OAAO,MAAM,QAAQ,mBAAmB,UAAU;AACpD;;;;;AAMA,SAAS,cAAc,QAAwB;CAC7C,OAAO,OAAO,QAAQ,oBAAoB,UAAU;AACtD;AAEA,SAAgB,MAAM,QAAgB,OAAuB;CAC3D,OAAO,GAAG,OAAO,OAAO,KAAK,KAAK;AACpC;AAEA,SAAgB,QAAQ,QAAgB,OAAuB;CAC7D,OAAO,GAAG,OAAO,SAAS,KAAK,KAAK;AACtC;;AAGA,SAAgB,WAAW,QAAgB,OAA8B;CACvE,OAAO,cAAc,cAAc,MAAM,EAAE,OAAO,KAAK,KAAK;AAC9D"}
@@ -0,0 +1,32 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { TabsOrientation } from "./tabs-context.mjs";
3
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
4
+ //#region src/tabs/tabs-indicator.d.ts
5
+ export interface TabsIndicatorState {
6
+ /** Optimistic selected value, or `null` (the indicator is then `hidden`). */
7
+ readonly value: string | null;
8
+ readonly pending: boolean;
9
+ readonly orientation: TabsOrientation;
10
+ }
11
+ export interface TabsIndicatorProps extends Omit<ComponentPropsWithRef<"span">, "hidden" | "className" | "style" | "children"> {
12
+ className?: StateValue<string | undefined, TabsIndicatorState>;
13
+ style?: StateValue<CSSProperties | undefined, TabsIndicatorState>;
14
+ children?: StateValue<ReactNode, TabsIndicatorState>;
15
+ render?: RenderProp<ComponentPropsWithRef<"span">, TabsIndicatorState>;
16
+ }
17
+ /**
18
+ * A decorative marker that follows the optimistic selection. Positioned by CSS Anchor
19
+ * Positioning: it sets `position-anchor` to the selected Tab's `anchor-name`; the consumer's CSS
20
+ * gives it `position: absolute` and `anchor()` insets, and a CSS `transition` on those insets
21
+ * slides it. It must come after the Tabs inside `Tabs.List` (an anchor must precede the
22
+ * positioned element in tree order). No `<ViewTransition>`: it changes only in optimistic (sync)
23
+ * commits, which never start a view transition (design 10.1).
24
+ *
25
+ * Owned keys (conventions 1.2): `aria-hidden`, `hidden`, `data-selected`, `data-pending`.
26
+ */
27
+ export declare function TabsIndicator({ className, style, children, render, ...rest }: TabsIndicatorProps): ReactNode;
28
+ export declare namespace TabsIndicator {
29
+ var displayName: string;
30
+ }
31
+ //#endregion
32
+ //# sourceMappingURL=tabs-indicator.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs-indicator.d.mts","names":[],"sources":["../../src/tabs/tabs-indicator.tsx"],"mappings":";;;;iBASiB;;WAEN;WACA;WACA,aAAa;;iBAGP,2BAA2B,KAC1C;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,+BAA+B;;;;;;;;;;;;wBAarC,gBAAgB,WAAW,OAAO,UAAU,WAAW,QAAQ,qBAAkB;yBAAjF"}
@@ -0,0 +1,49 @@
1
+ "use client";
2
+ import { dataAttr } from "../internal/data-attr.mjs";
3
+ import { resolveStateValue, useRender } from "../internal/use-render.mjs";
4
+ import { useTabsContext } from "./tabs-context.mjs";
5
+ import { anchorName } from "./tabs-ids.mjs";
6
+ //#region src/tabs/tabs-indicator.tsx
7
+ /**
8
+ * A decorative marker that follows the optimistic selection. Positioned by CSS Anchor
9
+ * Positioning: it sets `position-anchor` to the selected Tab's `anchor-name`; the consumer's CSS
10
+ * gives it `position: absolute` and `anchor()` insets, and a CSS `transition` on those insets
11
+ * slides it. It must come after the Tabs inside `Tabs.List` (an anchor must precede the
12
+ * positioned element in tree order). No `<ViewTransition>`: it changes only in optimistic (sync)
13
+ * commits, which never start a view transition (design 10.1).
14
+ *
15
+ * Owned keys (conventions 1.2): `aria-hidden`, `hidden`, `data-selected`, `data-pending`.
16
+ */
17
+ function TabsIndicator({ className, style, children, render, ...rest }) {
18
+ const ctx = useTabsContext("Tabs.Indicator");
19
+ const value = ctx.optimistic;
20
+ const state = {
21
+ value,
22
+ pending: ctx.pending,
23
+ orientation: ctx.orientation
24
+ };
25
+ const anchor = value === null ? void 0 : anchorName(ctx.rootId, value);
26
+ return useRender({
27
+ defaultElement: "span",
28
+ render,
29
+ state,
30
+ className,
31
+ style: (s) => ({
32
+ positionAnchor: anchor,
33
+ ...resolveStateValue(style, s)
34
+ }),
35
+ children,
36
+ props: rest,
37
+ libraryProps: {
38
+ "aria-hidden": true,
39
+ hidden: value === null,
40
+ "data-selected": dataAttr(value !== null),
41
+ "data-pending": dataAttr(ctx.pending)
42
+ }
43
+ });
44
+ }
45
+ TabsIndicator.displayName = "TabsIndicator";
46
+ //#endregion
47
+ export { TabsIndicator };
48
+
49
+ //# sourceMappingURL=tabs-indicator.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs-indicator.mjs","names":[],"sources":["../../src/tabs/tabs-indicator.tsx"],"sourcesContent":["\"use client\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { resolveStateValue, useRender } from \"../internal/use-render.js\";\nimport { useTabsContext } from \"./tabs-context.js\";\nimport type { TabsOrientation } from \"./tabs-context.js\";\nimport { anchorName } from \"./tabs-ids.js\";\n\nexport interface TabsIndicatorState {\n /** Optimistic selected value, or `null` (the indicator is then `hidden`). */\n readonly value: string | null;\n readonly pending: boolean;\n readonly orientation: TabsOrientation;\n}\n\nexport interface TabsIndicatorProps extends Omit<\n ComponentPropsWithRef<\"span\">,\n \"hidden\" | \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, TabsIndicatorState>;\n style?: StateValue<CSSProperties | undefined, TabsIndicatorState>;\n children?: StateValue<ReactNode, TabsIndicatorState>;\n render?: RenderProp<ComponentPropsWithRef<\"span\">, TabsIndicatorState>;\n}\n\n/**\n * A decorative marker that follows the optimistic selection. Positioned by CSS Anchor\n * Positioning: it sets `position-anchor` to the selected Tab's `anchor-name`; the consumer's CSS\n * gives it `position: absolute` and `anchor()` insets, and a CSS `transition` on those insets\n * slides it. It must come after the Tabs inside `Tabs.List` (an anchor must precede the\n * positioned element in tree order). No `<ViewTransition>`: it changes only in optimistic (sync)\n * commits, which never start a view transition (design 10.1).\n *\n * Owned keys (conventions 1.2): `aria-hidden`, `hidden`, `data-selected`, `data-pending`.\n */\nexport function TabsIndicator({ className, style, children, render, ...rest }: TabsIndicatorProps) {\n const ctx = useTabsContext(\"Tabs.Indicator\");\n const value = ctx.optimistic;\n const state: TabsIndicatorState = {\n value,\n pending: ctx.pending,\n orientation: ctx.orientation,\n };\n const anchor = value === null ? undefined : anchorName(ctx.rootId, value);\n\n return useRender<ComponentPropsWithRef<\"span\">, TabsIndicatorState>({\n defaultElement: \"span\",\n render,\n state,\n className,\n // `position-anchor` sits under the consumer's style: the consumer wins.\n style: (s) => ({ positionAnchor: anchor, ...resolveStateValue(style, s) }),\n children,\n props: rest,\n libraryProps: {\n \"aria-hidden\": true,\n hidden: value === null,\n \"data-selected\": dataAttr(value !== null),\n \"data-pending\": dataAttr(ctx.pending),\n },\n });\n}\n\nTabsIndicator.displayName = \"TabsIndicator\";\n"],"mappings":";;;;;;;;;;;;;;;;AAoCA,SAAgB,cAAc,EAAE,WAAW,OAAO,UAAU,QAAQ,GAAG,QAA4B;CACjG,MAAM,MAAM,eAAe,gBAAgB;CAC3C,MAAM,QAAQ,IAAI;CAClB,MAAM,QAA4B;EAChC;EACA,SAAS,IAAI;EACb,aAAa,IAAI;CACnB;CACA,MAAM,SAAS,UAAU,OAAO,KAAA,IAAY,WAAW,IAAI,QAAQ,KAAK;CAExE,OAAO,UAA6D;EAClE,gBAAgB;EAChB;EACA;EACA;EAEA,QAAQ,OAAO;GAAE,gBAAgB;GAAQ,GAAG,kBAAkB,OAAO,CAAC;EAAE;EACxE;EACA,OAAO;EACP,cAAc;GACZ,eAAe;GACf,QAAQ,UAAU;GAClB,iBAAiB,SAAS,UAAU,IAAI;GACxC,gBAAgB,SAAS,IAAI,OAAO;EACtC;CACF,CAAC;AACH;AAEA,cAAc,cAAc"}
@@ -0,0 +1,34 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { TabsOrientation } from "./tabs-context.mjs";
3
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
4
+ //#region src/tabs/tabs-list.d.ts
5
+ export interface TabsListState {
6
+ readonly orientation: TabsOrientation;
7
+ readonly disabled: boolean;
8
+ }
9
+ export interface TabsListProps extends Omit<ComponentPropsWithRef<"div">, "onKeyDown" | "onFocus" | "className" | "style" | "children"> {
10
+ /**
11
+ * Render `focusgroup="tablist …"` and, where the browser implements it, let the browser handle
12
+ * arrow keys, Home and End. Chromium 150+ only; elsewhere the JS path stays active.
13
+ * Default `false`.
14
+ */
15
+ experimental_focusgroup?: boolean;
16
+ className?: StateValue<string | undefined, TabsListState>;
17
+ style?: StateValue<CSSProperties | undefined, TabsListState>;
18
+ children?: StateValue<ReactNode, TabsListState>;
19
+ render?: RenderProp<ComponentPropsWithRef<"div">, TabsListState>;
20
+ }
21
+ /**
22
+ * The `role="tablist"` container. Owns arrow-key / Home / End navigation between Tabs: it only
23
+ * moves focus; automatic activation happens in the Tab's focus handler.
24
+ *
25
+ * Owned keys (a `render` element cannot override them, conventions 1.2): `role`,
26
+ * `aria-orientation`, `data-orientation`, `data-disabled`, `tabIndex`, `onKeyDown`, `onFocus`,
27
+ * and `focusgroup` when `experimental_focusgroup` is on.
28
+ */
29
+ export declare function TabsList({ experimental_focusgroup, className, style, children, render, ...rest }: TabsListProps): ReactNode;
30
+ export declare namespace TabsList {
31
+ var displayName: string;
32
+ }
33
+ //#endregion
34
+ //# sourceMappingURL=tabs-list.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs-list.d.mts","names":[],"sources":["../../src/tabs/tabs-list.tsx"],"mappings":";;;;iBAgBiB;WACN,aAAa;WACb;;iBAGM,sBAAsB,KACrC;;;;;;EAQA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,8BAA8B;;;;;;;;;;wBA4CpC,WACd,yBACA,WACA,OACA,UACA,WACG,QACF,gBAAa;yBAPA"}
@@ -0,0 +1,122 @@
1
+ "use client";
2
+ import { dataAttr } from "../internal/data-attr.mjs";
3
+ import { useRender } from "../internal/use-render.mjs";
4
+ import { useDevWarning } from "../internal/use-dev-warning.mjs";
5
+ import { useTabsContext } from "./tabs-context.mjs";
6
+ import { useEffect, useLayoutEffect, useRef, useState } from "react";
7
+ //#region src/tabs/tabs-list.tsx
8
+ function resolveKey(key, orientation, rtl) {
9
+ switch (key) {
10
+ case "Home": return "first";
11
+ case "End": return "last";
12
+ case "ArrowRight": return orientation === "horizontal" ? rtl ? "prev" : "next" : null;
13
+ case "ArrowLeft": return orientation === "horizontal" ? rtl ? "next" : "prev" : null;
14
+ case "ArrowDown": return orientation === "vertical" ? "next" : null;
15
+ case "ArrowUp": return orientation === "vertical" ? "prev" : null;
16
+ default: return null;
17
+ }
18
+ }
19
+ /** The tabs that belong to `list` (not to a nested tablist), in DOM order. Disabled included. */
20
+ function tabsOf(list) {
21
+ return Array.from(list.querySelectorAll("[role=\"tab\"]")).filter((tab) => tab.closest("[role=\"tablist\"]") === list);
22
+ }
23
+ function focusTab(tab) {
24
+ tab.focus({ focusVisible: true });
25
+ }
26
+ /**
27
+ * The `role="tablist"` container. Owns arrow-key / Home / End navigation between Tabs: it only
28
+ * moves focus; automatic activation happens in the Tab's focus handler.
29
+ *
30
+ * Owned keys (a `render` element cannot override them, conventions 1.2): `role`,
31
+ * `aria-orientation`, `data-orientation`, `data-disabled`, `tabIndex`, `onKeyDown`, `onFocus`,
32
+ * and `focusgroup` when `experimental_focusgroup` is on.
33
+ */
34
+ function TabsList({ experimental_focusgroup = false, className, style, children, render, ...rest }) {
35
+ const { orientation, loop, disabled, optimistic } = useTabsContext("Tabs.List");
36
+ const nativeFocusgroup = useRef(false);
37
+ useEffect(() => {
38
+ nativeFocusgroup.current = experimental_focusgroup && "focusgroup" in HTMLElement.prototype;
39
+ }, [experimental_focusgroup]);
40
+ const listRef = useRef(null);
41
+ const [unmatched, setUnmatched] = useState(false);
42
+ useLayoutEffect(() => {
43
+ const list = listRef.current;
44
+ const next = optimistic !== null && list !== null && !tabsOf(list).some((tab) => tab.getAttribute("tabindex") === "0");
45
+ if (next !== unmatched) setUnmatched(next);
46
+ }, [
47
+ optimistic,
48
+ children,
49
+ unmatched
50
+ ]);
51
+ useDevWarning(unmatched, `Tabs: the value ${JSON.stringify(optimistic)} matches no Tabs.Tab. No tab is selected; the tablist itself is the tab stop. Pass null to select nothing on purpose.`);
52
+ const listIsTabStop = optimistic === null || unmatched;
53
+ const state = {
54
+ orientation,
55
+ disabled
56
+ };
57
+ const onKeyDown = (event) => {
58
+ if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey) return;
59
+ const list = event.currentTarget;
60
+ const target = event.target;
61
+ if (target.getAttribute("role") !== "tab" || target.closest("[role=\"tablist\"]") !== list) return;
62
+ const rtl = orientation === "horizontal" && getComputedStyle(list).direction === "rtl";
63
+ const move = resolveKey(event.key, orientation, rtl);
64
+ if (move === null) return;
65
+ if (nativeFocusgroup.current) return;
66
+ event.preventDefault();
67
+ const tabs = tabsOf(list);
68
+ if (tabs.length === 0) return;
69
+ const index = tabs.indexOf(target);
70
+ const last = tabs.length - 1;
71
+ let nextIndex;
72
+ switch (move) {
73
+ case "first":
74
+ nextIndex = 0;
75
+ break;
76
+ case "last":
77
+ nextIndex = last;
78
+ break;
79
+ case "next":
80
+ nextIndex = index >= last ? loop ? 0 : last : index + 1;
81
+ break;
82
+ case "prev": nextIndex = index <= 0 ? loop ? last : 0 : index - 1;
83
+ }
84
+ const next = tabs[nextIndex];
85
+ if (next !== void 0 && next !== target) focusTab(next);
86
+ };
87
+ const onFocus = (event) => {
88
+ const list = event.currentTarget;
89
+ if (event.target !== list || !listIsTabStop) return;
90
+ const from = event.relatedTarget;
91
+ if (from instanceof Node && list.contains(from)) return;
92
+ const tabs = tabsOf(list);
93
+ const first = tabs.find((tab) => tab.getAttribute("aria-disabled") !== "true") ?? tabs[0];
94
+ if (first !== void 0) focusTab(first);
95
+ };
96
+ const focusgroup = experimental_focusgroup ? `tablist ${orientation === "vertical" ? "block" : "inline"} ${loop ? "wrap" : "nowrap"}` : void 0;
97
+ return useRender({
98
+ defaultElement: "div",
99
+ render,
100
+ state,
101
+ className,
102
+ style,
103
+ children,
104
+ props: rest,
105
+ libraryProps: {
106
+ role: "tablist",
107
+ "aria-orientation": orientation,
108
+ "data-orientation": orientation,
109
+ "data-disabled": dataAttr(disabled),
110
+ tabIndex: listIsTabStop ? 0 : void 0,
111
+ ...focusgroup !== void 0 && { focusgroup },
112
+ onKeyDown,
113
+ onFocus
114
+ },
115
+ ref: listRef
116
+ });
117
+ }
118
+ TabsList.displayName = "TabsList";
119
+ //#endregion
120
+ export { TabsList };
121
+
122
+ //# sourceMappingURL=tabs-list.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs-list.mjs","names":[],"sources":["../../src/tabs/tabs-list.tsx"],"sourcesContent":["\"use client\";\nimport { useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport type {\n ComponentPropsWithRef,\n CSSProperties,\n FocusEvent,\n KeyboardEvent,\n ReactNode,\n} from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { useTabsContext } from \"./tabs-context.js\";\nimport type { TabsOrientation } from \"./tabs-context.js\";\n\nexport interface TabsListState {\n readonly orientation: TabsOrientation;\n readonly disabled: boolean;\n}\n\nexport interface TabsListProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n \"onKeyDown\" | \"onFocus\" | \"className\" | \"style\" | \"children\"\n> {\n /**\n * Render `focusgroup=\"tablist …\"` and, where the browser implements it, let the browser handle\n * arrow keys, Home and End. Chromium 150+ only; elsewhere the JS path stays active.\n * Default `false`.\n */\n experimental_focusgroup?: boolean;\n className?: StateValue<string | undefined, TabsListState>;\n style?: StateValue<CSSProperties | undefined, TabsListState>;\n children?: StateValue<ReactNode, TabsListState>;\n render?: RenderProp<ComponentPropsWithRef<\"div\">, TabsListState>;\n}\n\ntype Move = \"next\" | \"prev\" | \"first\" | \"last\";\n\nfunction resolveKey(key: string, orientation: TabsOrientation, rtl: boolean): Move | null {\n switch (key) {\n case \"Home\":\n return \"first\";\n case \"End\":\n return \"last\";\n case \"ArrowRight\":\n return orientation === \"horizontal\" ? (rtl ? \"prev\" : \"next\") : null;\n case \"ArrowLeft\":\n return orientation === \"horizontal\" ? (rtl ? \"next\" : \"prev\") : null;\n case \"ArrowDown\":\n return orientation === \"vertical\" ? \"next\" : null;\n case \"ArrowUp\":\n return orientation === \"vertical\" ? \"prev\" : null;\n default:\n return null;\n }\n}\n\n/** The tabs that belong to `list` (not to a nested tablist), in DOM order. Disabled included. */\nfunction tabsOf(list: Element): HTMLElement[] {\n return Array.from(list.querySelectorAll<HTMLElement>('[role=\"tab\"]')).filter(\n (tab) => tab.closest('[role=\"tablist\"]') === list,\n );\n}\n\nfunction focusTab(tab: HTMLElement): void {\n // `focusVisible` shows the ring even where the engine's heuristic would not (survey: adopt).\n tab.focus({ focusVisible: true } as FocusOptions);\n}\n\n/**\n * The `role=\"tablist\"` container. Owns arrow-key / Home / End navigation between Tabs: it only\n * moves focus; automatic activation happens in the Tab's focus handler.\n *\n * Owned keys (a `render` element cannot override them, conventions 1.2): `role`,\n * `aria-orientation`, `data-orientation`, `data-disabled`, `tabIndex`, `onKeyDown`, `onFocus`,\n * and `focusgroup` when `experimental_focusgroup` is on.\n */\nexport function TabsList({\n experimental_focusgroup = false,\n className,\n style,\n children,\n render,\n ...rest\n}: TabsListProps) {\n const ctx = useTabsContext(\"Tabs.List\");\n const { orientation, loop, disabled, optimistic } = ctx;\n\n // Feature detection only when the flag is on, and only in an effect (conventions 8): markup\n // never differs between server and client.\n const nativeFocusgroup = useRef(false);\n useEffect(() => {\n nativeFocusgroup.current = experimental_focusgroup && \"focusgroup\" in HTMLElement.prototype;\n }, [experimental_focusgroup]);\n\n // A value that matches no Tab (a controlled `value` for a route without a tab, design 11.5)\n // leaves no tab with tabindex=0, so the List takes over as the tab stop exactly as for `null`.\n // Read from the DOM (Tabs register nowhere) whenever the value or the List's children change; a\n // Tab added or removed by a component below that re-renders on its own is picked up on the\n // List's next render. The state only changes when the answer does.\n const listRef = useRef<HTMLElement | null>(null);\n const [unmatched, setUnmatched] = useState(false);\n useLayoutEffect(() => {\n const list = listRef.current;\n const next =\n optimistic !== null &&\n list !== null &&\n !tabsOf(list).some((tab) => tab.getAttribute(\"tabindex\") === \"0\");\n if (next !== unmatched) setUnmatched(next);\n }, [optimistic, children, unmatched]);\n useDevWarning(\n unmatched,\n `Tabs: the value ${JSON.stringify(optimistic)} matches no Tabs.Tab. No tab is selected; the ` +\n \"tablist itself is the tab stop. Pass null to select nothing on purpose.\",\n );\n const listIsTabStop = optimistic === null || unmatched;\n\n const state: TabsListState = { orientation, disabled };\n\n const onKeyDown = (event: KeyboardEvent<HTMLElement>) => {\n if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey) return;\n const list = event.currentTarget;\n const target = event.target as Element;\n if (target.getAttribute(\"role\") !== \"tab\" || target.closest('[role=\"tablist\"]') !== list) {\n return;\n }\n const rtl = orientation === \"horizontal\" && getComputedStyle(list).direction === \"rtl\";\n const move = resolveKey(event.key, orientation, rtl);\n if (move === null) return;\n // The browser moves focus itself where `focusgroup` is implemented.\n if (nativeFocusgroup.current) return;\n\n event.preventDefault();\n const tabs = tabsOf(list);\n if (tabs.length === 0) return;\n const index = tabs.indexOf(target as HTMLElement);\n const last = tabs.length - 1;\n let nextIndex: number;\n switch (move) {\n case \"first\":\n nextIndex = 0;\n break;\n case \"last\":\n nextIndex = last;\n break;\n case \"next\":\n nextIndex = index >= last ? (loop ? 0 : last) : index + 1;\n break;\n case \"prev\":\n nextIndex = index <= 0 ? (loop ? last : 0) : index - 1;\n break;\n }\n const next = tabs[nextIndex];\n if (next !== undefined && next !== target) focusTab(next);\n };\n\n // With no selected tab (value null or matching no Tab) no Tab has tabindex=0, so the list\n // itself is the tab stop and hands focus to the first enabled tab (design section 8). Only when\n // focus arrives from outside the list: Shift+Tab from a tab inside would otherwise bounce\n // straight back. The redirect never selects: the Tab only dispatches on focus that moves\n // between tabs.\n const onFocus = (event: FocusEvent<HTMLElement>) => {\n const list = event.currentTarget;\n if (event.target !== list || !listIsTabStop) return;\n const from = event.relatedTarget;\n if (from instanceof Node && list.contains(from)) return;\n const tabs = tabsOf(list);\n const first = tabs.find((tab) => tab.getAttribute(\"aria-disabled\") !== \"true\") ?? tabs[0];\n if (first !== undefined) focusTab(first);\n };\n\n const focusgroup = experimental_focusgroup\n ? `tablist ${orientation === \"vertical\" ? \"block\" : \"inline\"} ${loop ? \"wrap\" : \"nowrap\"}`\n : undefined;\n\n return useRender<ComponentPropsWithRef<\"div\">, TabsListState>({\n defaultElement: \"div\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps: {\n role: \"tablist\",\n \"aria-orientation\": orientation,\n \"data-orientation\": orientation,\n \"data-disabled\": dataAttr(disabled),\n tabIndex: listIsTabStop ? 0 : undefined,\n // Owned only while the flag is on; otherwise the key is omitted, not `undefined`, so it is not\n // owned (conventions 1.2).\n ...(focusgroup !== undefined && { focusgroup }),\n onKeyDown,\n onFocus,\n },\n ref: listRef,\n });\n}\n\nTabsList.displayName = \"TabsList\";\n"],"mappings":";;;;;;;AAuCA,SAAS,WAAW,KAAa,aAA8B,KAA2B;CACxF,QAAQ,KAAR;EACE,KAAK,QACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,KAAK,cACH,OAAO,gBAAgB,eAAgB,MAAM,SAAS,SAAU;EAClE,KAAK,aACH,OAAO,gBAAgB,eAAgB,MAAM,SAAS,SAAU;EAClE,KAAK,aACH,OAAO,gBAAgB,aAAa,SAAS;EAC/C,KAAK,WACH,OAAO,gBAAgB,aAAa,SAAS;EAC/C,SACE,OAAO;CACX;AACF;;AAGA,SAAS,OAAO,MAA8B;CAC5C,OAAO,MAAM,KAAK,KAAK,iBAA8B,gBAAc,CAAC,CAAC,CAAC,QACnE,QAAQ,IAAI,QAAQ,oBAAkB,MAAM,IAC/C;AACF;AAEA,SAAS,SAAS,KAAwB;CAExC,IAAI,MAAM,EAAE,cAAc,KAAK,CAAiB;AAClD;;;;;;;;;AAUA,SAAgB,SAAS,EACvB,0BAA0B,OAC1B,WACA,OACA,UACA,QACA,GAAG,QACa;CAEhB,MAAM,EAAE,aAAa,MAAM,UAAU,eADzB,eAAe,WAC2B;CAItD,MAAM,mBAAmB,OAAO,KAAK;CACrC,gBAAgB;EACd,iBAAiB,UAAU,2BAA2B,gBAAgB,YAAY;CACpF,GAAG,CAAC,uBAAuB,CAAC;CAO5B,MAAM,UAAU,OAA2B,IAAI;CAC/C,MAAM,CAAC,WAAW,gBAAgB,SAAS,KAAK;CAChD,sBAAsB;EACpB,MAAM,OAAO,QAAQ;EACrB,MAAM,OACJ,eAAe,QACf,SAAS,QACT,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,QAAQ,IAAI,aAAa,UAAU,MAAM,GAAG;EAClE,IAAI,SAAS,WAAW,aAAa,IAAI;CAC3C,GAAG;EAAC;EAAY;EAAU;CAAS,CAAC;CACpC,cACE,WACA,mBAAmB,KAAK,UAAU,UAAU,EAAE,sHAEhD;CACA,MAAM,gBAAgB,eAAe,QAAQ;CAE7C,MAAM,QAAuB;EAAE;EAAa;CAAS;CAErD,MAAM,aAAa,UAAsC;EACvD,IAAI,MAAM,oBAAoB,MAAM,UAAU,MAAM,WAAW,MAAM,SAAS;EAC9E,MAAM,OAAO,MAAM;EACnB,MAAM,SAAS,MAAM;EACrB,IAAI,OAAO,aAAa,MAAM,MAAM,SAAS,OAAO,QAAQ,oBAAkB,MAAM,MAClF;EAEF,MAAM,MAAM,gBAAgB,gBAAgB,iBAAiB,IAAI,CAAC,CAAC,cAAc;EACjF,MAAM,OAAO,WAAW,MAAM,KAAK,aAAa,GAAG;EACnD,IAAI,SAAS,MAAM;EAEnB,IAAI,iBAAiB,SAAS;EAE9B,MAAM,eAAe;EACrB,MAAM,OAAO,OAAO,IAAI;EACxB,IAAI,KAAK,WAAW,GAAG;EACvB,MAAM,QAAQ,KAAK,QAAQ,MAAqB;EAChD,MAAM,OAAO,KAAK,SAAS;EAC3B,IAAI;EACJ,QAAQ,MAAR;GACE,KAAK;IACH,YAAY;IACZ;GACF,KAAK;IACH,YAAY;IACZ;GACF,KAAK;IACH,YAAY,SAAS,OAAQ,OAAO,IAAI,OAAQ,QAAQ;IACxD;GACF,KAAK,QACH,YAAY,SAAS,IAAK,OAAO,OAAO,IAAK,QAAQ;EAEzD;EACA,MAAM,OAAO,KAAK;EAClB,IAAI,SAAS,KAAA,KAAa,SAAS,QAAQ,SAAS,IAAI;CAC1D;CAOA,MAAM,WAAW,UAAmC;EAClD,MAAM,OAAO,MAAM;EACnB,IAAI,MAAM,WAAW,QAAQ,CAAC,eAAe;EAC7C,MAAM,OAAO,MAAM;EACnB,IAAI,gBAAgB,QAAQ,KAAK,SAAS,IAAI,GAAG;EACjD,MAAM,OAAO,OAAO,IAAI;EACxB,MAAM,QAAQ,KAAK,MAAM,QAAQ,IAAI,aAAa,eAAe,MAAM,MAAM,KAAK,KAAK;EACvF,IAAI,UAAU,KAAA,GAAW,SAAS,KAAK;CACzC;CAEA,MAAM,aAAa,0BACf,WAAW,gBAAgB,aAAa,UAAU,SAAS,GAAG,OAAO,SAAS,aAC9E,KAAA;CAEJ,OAAO,UAAuD;EAC5D,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP,cAAc;GACZ,MAAM;GACN,oBAAoB;GACpB,oBAAoB;GACpB,iBAAiB,SAAS,QAAQ;GAClC,UAAU,gBAAgB,IAAI,KAAA;GAG9B,GAAI,eAAe,KAAA,KAAa,EAAE,WAAW;GAC7C;GACA;EACF;EACA,KAAK;CACP,CAAC;AACH;AAEA,SAAS,cAAc"}