@funstack/ui 0.1.0-alpha.3 → 0.1.0-alpha.5

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 (230) hide show
  1. package/dist/accordion/accordion-root.mjs +2 -1
  2. package/dist/accordion/accordion-root.mjs.map +1 -1
  3. package/dist/combobox/combobox-clear.mjs +2 -1
  4. package/dist/combobox/combobox-clear.mjs.map +1 -1
  5. package/dist/combobox/combobox-list.mjs +2 -1
  6. package/dist/combobox/combobox-list.mjs.map +1 -1
  7. package/dist/combobox/combobox-popup.d.mts.map +1 -1
  8. package/dist/combobox/combobox-popup.mjs +7 -2
  9. package/dist/combobox/combobox-popup.mjs.map +1 -1
  10. package/dist/combobox/combobox-root.mjs +1 -1
  11. package/dist/combobox/combobox-trigger.mjs +2 -1
  12. package/dist/combobox/combobox-trigger.mjs.map +1 -1
  13. package/dist/dialog/dialog-close.mjs +2 -1
  14. package/dist/dialog/dialog-close.mjs.map +1 -1
  15. package/dist/dialog/dialog-form.d.mts +14 -8
  16. package/dist/dialog/dialog-form.d.mts.map +1 -1
  17. package/dist/dialog/dialog-form.mjs +36 -5
  18. package/dist/dialog/dialog-form.mjs.map +1 -1
  19. package/dist/dialog/dialog-popup.d.mts.map +1 -1
  20. package/dist/dialog/dialog-popup.mjs +13 -5
  21. package/dist/dialog/dialog-popup.mjs.map +1 -1
  22. package/dist/dialog/dialog-root.mjs +1 -1
  23. package/dist/dialog/dialog-trigger.mjs +2 -1
  24. package/dist/dialog/dialog-trigger.mjs.map +1 -1
  25. package/dist/field/field-root.mjs +5 -3
  26. package/dist/field/field-root.mjs.map +1 -1
  27. package/dist/form/form-root.mjs +2 -1
  28. package/dist/form/form-root.mjs.map +1 -1
  29. package/dist/form/form-submit.mjs +2 -1
  30. package/dist/form/form-submit.mjs.map +1 -1
  31. package/dist/index.d.mts +27 -1
  32. package/dist/index.mjs +6 -1
  33. package/dist/internal/announce.mjs +53 -0
  34. package/dist/internal/announce.mjs.map +1 -0
  35. package/dist/internal/decimal.mjs +27 -0
  36. package/dist/internal/decimal.mjs.map +1 -0
  37. package/dist/internal/dev-warn.mjs +17 -0
  38. package/dist/internal/dev-warn.mjs.map +1 -0
  39. package/dist/internal/field-context.mjs.map +1 -1
  40. package/dist/internal/range-value.mjs +51 -0
  41. package/dist/internal/range-value.mjs.map +1 -0
  42. package/dist/internal/render-element.mjs +54 -0
  43. package/dist/internal/render-element.mjs.map +1 -0
  44. package/dist/internal/toolbar-context.mjs +17 -0
  45. package/dist/internal/toolbar-context.mjs.map +1 -0
  46. package/dist/internal/use-exit-animation.mjs +26 -6
  47. package/dist/internal/use-exit-animation.mjs.map +1 -1
  48. package/dist/internal/use-field-control.mjs +5 -3
  49. package/dist/internal/use-field-control.mjs.map +1 -1
  50. package/dist/internal/use-popover-popup.mjs +3 -2
  51. package/dist/internal/use-popover-popup.mjs.map +1 -1
  52. package/dist/internal/use-popover-trigger.mjs +1 -1
  53. package/dist/internal/use-render.mjs +11 -34
  54. package/dist/internal/use-render.mjs.map +1 -1
  55. package/dist/internal/use-roving-focus.mjs +301 -0
  56. package/dist/internal/use-roving-focus.mjs.map +1 -0
  57. package/dist/menu/menu-checkbox-item.mjs +2 -1
  58. package/dist/menu/menu-checkbox-item.mjs.map +1 -1
  59. package/dist/menu/menu-item.mjs +2 -1
  60. package/dist/menu/menu-item.mjs.map +1 -1
  61. package/dist/menu/menu-popup.d.mts.map +1 -1
  62. package/dist/menu/menu-popup.mjs +7 -2
  63. package/dist/menu/menu-popup.mjs.map +1 -1
  64. package/dist/menu/menu-radio-item.mjs +2 -1
  65. package/dist/menu/menu-radio-item.mjs.map +1 -1
  66. package/dist/menu/menu-root.mjs +1 -1
  67. package/dist/menu/menu-trigger.d.mts +2 -1
  68. package/dist/menu/menu-trigger.d.mts.map +1 -1
  69. package/dist/menu/menu-trigger.mjs +15 -4
  70. package/dist/menu/menu-trigger.mjs.map +1 -1
  71. package/dist/meter/index.d.mts +24 -0
  72. package/dist/meter/index.d.mts.map +1 -0
  73. package/dist/meter/index.mjs +23 -0
  74. package/dist/meter/index.mjs.map +1 -0
  75. package/dist/meter/meter-context.mjs +13 -0
  76. package/dist/meter/meter-context.mjs.map +1 -0
  77. package/dist/meter/meter-indicator.d.mts +19 -0
  78. package/dist/meter/meter-indicator.d.mts.map +1 -0
  79. package/dist/meter/meter-indicator.mjs +25 -0
  80. package/dist/meter/meter-indicator.mjs.map +1 -0
  81. package/dist/meter/meter-label.d.mts +19 -0
  82. package/dist/meter/meter-label.d.mts.map +1 -0
  83. package/dist/meter/meter-label.mjs +28 -0
  84. package/dist/meter/meter-label.mjs.map +1 -0
  85. package/dist/meter/meter-region.d.mts +4 -0
  86. package/dist/meter/meter-region.d.mts.map +1 -0
  87. package/dist/meter/meter-region.mjs +39 -0
  88. package/dist/meter/meter-region.mjs.map +1 -0
  89. package/dist/meter/meter-root.d.mts +71 -0
  90. package/dist/meter/meter-root.d.mts.map +1 -0
  91. package/dist/meter/meter-root.mjs +83 -0
  92. package/dist/meter/meter-root.mjs.map +1 -0
  93. package/dist/meter/meter-track.d.mts +19 -0
  94. package/dist/meter/meter-track.d.mts.map +1 -0
  95. package/dist/meter/meter-track.mjs +25 -0
  96. package/dist/meter/meter-track.mjs.map +1 -0
  97. package/dist/meter/meter-value.d.mts +20 -0
  98. package/dist/meter/meter-value.d.mts.map +1 -0
  99. package/dist/meter/meter-value.mjs +29 -0
  100. package/dist/meter/meter-value.mjs.map +1 -0
  101. package/dist/number-field/index.d.mts +26 -0
  102. package/dist/number-field/index.d.mts.map +1 -0
  103. package/dist/number-field/index.mjs +26 -0
  104. package/dist/number-field/index.mjs.map +1 -0
  105. package/dist/number-field/number-field-arithmetic.mjs +78 -0
  106. package/dist/number-field/number-field-arithmetic.mjs.map +1 -0
  107. package/dist/number-field/number-field-context.d.mts +2 -0
  108. package/dist/number-field/number-field-context.mjs +13 -0
  109. package/dist/number-field/number-field-context.mjs.map +1 -0
  110. package/dist/number-field/number-field-decrement.d.mts +16 -0
  111. package/dist/number-field/number-field-decrement.d.mts.map +1 -0
  112. package/dist/number-field/number-field-decrement.mjs +19 -0
  113. package/dist/number-field/number-field-decrement.mjs.map +1 -0
  114. package/dist/number-field/number-field-format.mjs +279 -0
  115. package/dist/number-field/number-field-format.mjs.map +1 -0
  116. package/dist/number-field/number-field-increment.d.mts +32 -0
  117. package/dist/number-field/number-field-increment.d.mts.map +1 -0
  118. package/dist/number-field/number-field-increment.mjs +68 -0
  119. package/dist/number-field/number-field-increment.mjs.map +1 -0
  120. package/dist/number-field/number-field-input.d.mts +29 -0
  121. package/dist/number-field/number-field-input.d.mts.map +1 -0
  122. package/dist/number-field/number-field-input.mjs +163 -0
  123. package/dist/number-field/number-field-input.mjs.map +1 -0
  124. package/dist/number-field/number-field-label.d.mts +23 -0
  125. package/dist/number-field/number-field-label.d.mts.map +1 -0
  126. package/dist/number-field/number-field-label.mjs +47 -0
  127. package/dist/number-field/number-field-label.mjs.map +1 -0
  128. package/dist/number-field/number-field-root.d.mts +108 -0
  129. package/dist/number-field/number-field-root.d.mts.map +1 -0
  130. package/dist/number-field/number-field-root.mjs +523 -0
  131. package/dist/number-field/number-field-root.mjs.map +1 -0
  132. package/dist/number-field/use-step-button.mjs +107 -0
  133. package/dist/number-field/use-step-button.mjs.map +1 -0
  134. package/dist/popover/popover-close.mjs +2 -1
  135. package/dist/popover/popover-close.mjs.map +1 -1
  136. package/dist/popover/popover-popup.d.mts.map +1 -1
  137. package/dist/popover/popover-popup.mjs +7 -2
  138. package/dist/popover/popover-popup.mjs.map +1 -1
  139. package/dist/popover/popover-root.mjs +1 -1
  140. package/dist/progress/index.d.mts +24 -0
  141. package/dist/progress/index.d.mts.map +1 -0
  142. package/dist/progress/index.mjs +24 -0
  143. package/dist/progress/index.mjs.map +1 -0
  144. package/dist/progress/progress-context.mjs +13 -0
  145. package/dist/progress/progress-context.mjs.map +1 -0
  146. package/dist/progress/progress-indicator.d.mts +19 -0
  147. package/dist/progress/progress-indicator.d.mts.map +1 -0
  148. package/dist/progress/progress-indicator.mjs +29 -0
  149. package/dist/progress/progress-indicator.mjs.map +1 -0
  150. package/dist/progress/progress-label.d.mts +19 -0
  151. package/dist/progress/progress-label.d.mts.map +1 -0
  152. package/dist/progress/progress-label.mjs +30 -0
  153. package/dist/progress/progress-label.mjs.map +1 -0
  154. package/dist/progress/progress-root.d.mts +68 -0
  155. package/dist/progress/progress-root.d.mts.map +1 -0
  156. package/dist/progress/progress-root.mjs +80 -0
  157. package/dist/progress/progress-root.mjs.map +1 -0
  158. package/dist/progress/progress-track.d.mts +19 -0
  159. package/dist/progress/progress-track.d.mts.map +1 -0
  160. package/dist/progress/progress-track.mjs +29 -0
  161. package/dist/progress/progress-track.mjs.map +1 -0
  162. package/dist/progress/progress-value.d.mts +20 -0
  163. package/dist/progress/progress-value.d.mts.map +1 -0
  164. package/dist/progress/progress-value.mjs +33 -0
  165. package/dist/progress/progress-value.mjs.map +1 -0
  166. package/dist/slider/slider-arithmetic.mjs +1 -17
  167. package/dist/slider/slider-arithmetic.mjs.map +1 -1
  168. package/dist/slider/slider-root.mjs +3 -2
  169. package/dist/slider/slider-root.mjs.map +1 -1
  170. package/dist/slider/slider-thumb.mjs +2 -1
  171. package/dist/slider/slider-thumb.mjs.map +1 -1
  172. package/dist/slider/slider-track.mjs +2 -1
  173. package/dist/slider/slider-track.mjs.map +1 -1
  174. package/dist/tabs/tabs-indicator.mjs +2 -1
  175. package/dist/tabs/tabs-indicator.mjs.map +1 -1
  176. package/dist/tabs/tabs-tab.mjs +2 -1
  177. package/dist/tabs/tabs-tab.mjs.map +1 -1
  178. package/dist/toast/toast-action.mjs +2 -1
  179. package/dist/toast/toast-action.mjs.map +1 -1
  180. package/dist/toast/toast-close.mjs +2 -1
  181. package/dist/toast/toast-close.mjs.map +1 -1
  182. package/dist/toast/toast-root.mjs +3 -2
  183. package/dist/toast/toast-root.mjs.map +1 -1
  184. package/dist/toast/toast-viewport.d.mts.map +1 -1
  185. package/dist/toast/toast-viewport.mjs +30 -7
  186. package/dist/toast/toast-viewport.mjs.map +1 -1
  187. package/dist/toast/toast.d.mts.map +1 -1
  188. package/dist/toast/toast.mjs +5 -1
  189. package/dist/toast/toast.mjs.map +1 -1
  190. package/dist/toggle/toggle.mjs +2 -1
  191. package/dist/toggle/toggle.mjs.map +1 -1
  192. package/dist/toggle-group/index.d.mts +16 -0
  193. package/dist/toggle-group/index.d.mts.map +1 -0
  194. package/dist/toggle-group/index.mjs +15 -0
  195. package/dist/toggle-group/index.mjs.map +1 -0
  196. package/dist/toggle-group/toggle-group-context.d.mts +4 -0
  197. package/dist/toggle-group/toggle-group-context.d.mts.map +1 -0
  198. package/dist/toggle-group/toggle-group-context.mjs +13 -0
  199. package/dist/toggle-group/toggle-group-context.mjs.map +1 -0
  200. package/dist/toggle-group/toggle-group-item.d.mts +47 -0
  201. package/dist/toggle-group/toggle-group-item.d.mts.map +1 -0
  202. package/dist/toggle-group/toggle-group-item.mjs +63 -0
  203. package/dist/toggle-group/toggle-group-item.mjs.map +1 -0
  204. package/dist/toggle-group/toggle-group-root.d.mts +95 -0
  205. package/dist/toggle-group/toggle-group-root.d.mts.map +1 -0
  206. package/dist/toggle-group/toggle-group-root.mjs +211 -0
  207. package/dist/toggle-group/toggle-group-root.mjs.map +1 -0
  208. package/dist/toolbar/index.d.mts +18 -0
  209. package/dist/toolbar/index.d.mts.map +1 -0
  210. package/dist/toolbar/index.mjs +18 -0
  211. package/dist/toolbar/index.mjs.map +1 -0
  212. package/dist/toolbar/toolbar-button.d.mts +42 -0
  213. package/dist/toolbar/toolbar-button.d.mts.map +1 -0
  214. package/dist/toolbar/toolbar-button.mjs +50 -0
  215. package/dist/toolbar/toolbar-button.mjs.map +1 -0
  216. package/dist/toolbar/toolbar-root.d.mts +38 -0
  217. package/dist/toolbar/toolbar-root.d.mts.map +1 -0
  218. package/dist/toolbar/toolbar-root.mjs +66 -0
  219. package/dist/toolbar/toolbar-root.mjs.map +1 -0
  220. package/dist/toolbar/toolbar-separator.d.mts +25 -0
  221. package/dist/toolbar/toolbar-separator.d.mts.map +1 -0
  222. package/dist/toolbar/toolbar-separator.mjs +31 -0
  223. package/dist/toolbar/toolbar-separator.mjs.map +1 -0
  224. package/dist/tooltip/tooltip-popup.mjs +2 -1
  225. package/dist/tooltip/tooltip-popup.mjs.map +1 -1
  226. package/dist/tooltip/tooltip-root.mjs +18 -2
  227. package/dist/tooltip/tooltip-root.mjs.map +1 -1
  228. package/dist/tooltip/tooltip-trigger.mjs +2 -1
  229. package/dist/tooltip/tooltip-trigger.mjs.map +1 -1
  230. package/package.json +6 -1
@@ -0,0 +1,211 @@
1
+ "use client";
2
+ import { createActionDetails } from "../internal/action-details.mjs";
3
+ import { dataAttr } from "../internal/data-attr.mjs";
4
+ import { transitionTypes } from "../internal/transition-types.mjs";
5
+ import { useLatest } from "../internal/use-latest.mjs";
6
+ import { useControllableAction } from "../internal/use-controllable-action.mjs";
7
+ import { useRender } from "../internal/use-render.mjs";
8
+ import { ToggleGroupContext } from "./toggle-group-context.mjs";
9
+ import { useToolbarContext } from "../internal/toolbar-context.mjs";
10
+ import { useDevWarning } from "../internal/use-dev-warning.mjs";
11
+ import { useRovingFocus } from "../internal/use-roving-focus.mjs";
12
+ import { useCallback, useEffectEvent, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
13
+ import { jsx } from "react/jsx-runtime";
14
+ //#region src/toggle-group/toggle-group-root.tsx
15
+ const isDev = process.env.NODE_ENV !== "production";
16
+ const EMPTY = /* @__PURE__ */ new Set();
17
+ function toSet(value) {
18
+ if (value === null) return EMPTY;
19
+ return typeof value === "string" ? /* @__PURE__ */ new Set([value]) : new Set(value);
20
+ }
21
+ /** The optimistic reducer (design 5.2): apply a patch, so stacked dispatches re-base. */
22
+ function reduce(set, patch) {
23
+ const next = new Set(set);
24
+ for (const v of patch.removed) next.delete(v);
25
+ for (const v of patch.added) next.add(v);
26
+ return next;
27
+ }
28
+ /** `values` sorted by `order`; values not in `order` keep their iteration order, last. */
29
+ function sortByOrder(values, order) {
30
+ const wanted = new Set(values);
31
+ const sorted = [];
32
+ for (const v of order) if (wanted.delete(v)) sorted.push(v);
33
+ for (const v of wanted) sorted.push(v);
34
+ return sorted;
35
+ }
36
+ function arraysEqual(a, b) {
37
+ return a.length === b.length && a.every((v, i) => v === b[i]);
38
+ }
39
+ function byDocumentOrder(a, b) {
40
+ if (a === b) return 0;
41
+ return a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
42
+ }
43
+ /** Standalone entry (design 6.2): the first pressed item, else the first item (radio-like). */
44
+ function pressedFirst(items) {
45
+ return items.find((el) => el.getAttribute("aria-pressed") === "true") ?? items[0];
46
+ }
47
+ /** Engine capabilities never change while the page lives. */
48
+ const subscribeNever = () => () => {};
49
+ const NO_LAYOUT = {
50
+ order: [],
51
+ disabled: []
52
+ };
53
+ /**
54
+ * `<div role="group">` that owns the pressed set, the transition and, when standalone, the roving
55
+ * focus over its Items (design/components/toggle-group.md 16.2). Inside a `Toolbar.Root` it runs
56
+ * no engine of its own: its Items are members of the Toolbar's sequence.
57
+ *
58
+ * Owned keys (conventions 1.2): `role`, `data-orientation`, `data-pending`, `data-disabled`;
59
+ * standalone also `onKeyDown`, `onFocus`, `onBlur`, `onPointerDown` and, with the flag,
60
+ * `focusgroup`.
61
+ */
62
+ function ToggleGroupRoot(props) {
63
+ const { exclusive = false, value, defaultValue, valueChangeAction, deselectable = true, orientation: orientationProp, loop: loopProp, disabled = false, experimental_focusgroup: focusgroupProp, className, style, children, render, ...rest } = props;
64
+ const toolbar = useToolbarContext();
65
+ const inToolbar = toolbar !== null;
66
+ useDevWarning(inToolbar && orientationProp !== void 0, "ToggleGroup.Root: `orientation` is ignored inside a Toolbar.Root; the group follows the Toolbar's orientation.");
67
+ useDevWarning(inToolbar && loopProp !== void 0, "ToggleGroup.Root: `loop` is ignored inside a Toolbar.Root; the Toolbar's `loop` applies.");
68
+ useDevWarning(inToolbar && focusgroupProp === true, "ToggleGroup.Root: `experimental_focusgroup` is ignored inside a Toolbar.Root. A nested focusgroup would split the Toolbar's single Tab stop; set the flag on the Toolbar.Root.");
69
+ const orientation = toolbar?.orientation ?? orientationProp ?? "horizontal";
70
+ const loop = loopProp ?? true;
71
+ const flag = !inToolbar && focusgroupProp === true;
72
+ const [mountExclusive] = useState(exclusive);
73
+ useDevWarning(mountExclusive !== exclusive, "ToggleGroup.Root: `exclusive` changed after mount. Switching between exclusive and multiple mode is not supported; the group keeps the mode it mounted with.");
74
+ const controlledKey = value === void 0 ? void 0 : JSON.stringify(value);
75
+ const controlledSet = useMemo(() => controlledKey === void 0 ? void 0 : toSet(JSON.parse(controlledKey)), [controlledKey]);
76
+ const defaultKey = defaultValue === void 0 ? void 0 : JSON.stringify(defaultValue);
77
+ const defaultSet = useMemo(() => defaultKey === void 0 ? void 0 : toSet(JSON.parse(defaultKey)), [defaultKey]);
78
+ const registry = useRef(/* @__PURE__ */ new Map());
79
+ const register = useCallback((element, itemValue) => {
80
+ registry.current.set(element, itemValue);
81
+ return () => {
82
+ registry.current.delete(element);
83
+ };
84
+ }, []);
85
+ const sortedItems = () => [...registry.current].sort(([a], [b]) => byDocumentOrder(a, b));
86
+ /** Sorts by the Items' current document order. Reads the DOM: handlers and effects only. */
87
+ const sortByDocument = (values) => sortByOrder(values, sortedItems().map(([, v]) => v));
88
+ const { optimistic, committed, pending, dispatch } = useControllableAction({
89
+ controlled: controlledSet,
90
+ defaultValue: defaultSet,
91
+ initialValue: EMPTY,
92
+ action: valueChangeAction && ((next, details) => valueChangeAction(mountExclusive ? [...next][0] ?? null : sortByDocument(next), details)),
93
+ transitionTypes: (_next, _prev, details) => [
94
+ ...transitionTypes("toggle-group"),
95
+ ...details.added.length > 0 ? ["fsui-toggle-group-press"] : [],
96
+ ...details.removed.length > 0 ? ["fsui-toggle-group-release"] : []
97
+ ],
98
+ reducer: (prev, _next, details) => reduce(prev, details),
99
+ warn: {
100
+ component: "ToggleGroup.Root",
101
+ prop: "value"
102
+ }
103
+ });
104
+ const latest = useLatest({
105
+ optimistic,
106
+ exclusive: mountExclusive,
107
+ deselectable
108
+ });
109
+ const toggle = useCallback((itemValue, event) => {
110
+ const { optimistic, exclusive, deselectable } = latest.current;
111
+ let patch;
112
+ if (optimistic.has(itemValue)) {
113
+ if (exclusive && !deselectable) return;
114
+ patch = {
115
+ added: [],
116
+ removed: [itemValue]
117
+ };
118
+ } else patch = {
119
+ added: [itemValue],
120
+ removed: exclusive ? [...optimistic] : []
121
+ };
122
+ dispatch(reduce(optimistic, patch), createActionDetails(event, patch));
123
+ }, [dispatch, latest]);
124
+ const [layout, setLayout] = useState(NO_LAYOUT);
125
+ const renderedValue = sortByOrder(optimistic, layout.order);
126
+ const warnedDuplicate = useRef(false);
127
+ const syncLayout = useEffectEvent(() => {
128
+ const items = sortedItems();
129
+ const order = items.map(([, v]) => v);
130
+ const disabledValues = items.filter(([el]) => el.matches(":disabled")).map(([, v]) => v);
131
+ const valueChanged = !arraysEqual(sortByOrder(optimistic, order), renderedValue);
132
+ const startChanged = flag && (!arraysEqual(order, layout.order) || !arraysEqual(disabledValues, layout.disabled));
133
+ if (valueChanged || startChanged) setLayout({
134
+ order,
135
+ disabled: disabledValues
136
+ });
137
+ if (!isDev || warnedDuplicate.current) return;
138
+ const duplicate = order.find((v, i) => order.indexOf(v) !== i);
139
+ if (duplicate !== void 0) {
140
+ warnedDuplicate.current = true;
141
+ console.error(`ToggleGroup.Item: two Items share the value "${duplicate}" in one ToggleGroup.Root. Item values must be unique; both follow the same pressed state.`);
142
+ }
143
+ });
144
+ useLayoutEffect(() => {
145
+ syncLayout();
146
+ });
147
+ const native = useSyncExternalStore(subscribeNever, () => flag && "focusGroup" in HTMLElement.prototype, () => false);
148
+ const focusgroupStartValue = flag && !disabled ? renderedValue.find((v) => (layout.order.length === 0 || layout.order.includes(v)) && !layout.disabled.includes(v)) ?? null : null;
149
+ const rootRef = useRef(null);
150
+ const roving = useRovingFocus(rootRef, {
151
+ enabled: !inToolbar,
152
+ orientation,
153
+ loop,
154
+ native,
155
+ entry: pressedFirst,
156
+ memory: false
157
+ });
158
+ const context = useMemo(() => ({
159
+ optimistic,
160
+ committed,
161
+ pending,
162
+ disabled,
163
+ orientation,
164
+ focusgroupStartValue,
165
+ toggle,
166
+ register
167
+ }), [
168
+ optimistic,
169
+ committed,
170
+ pending,
171
+ disabled,
172
+ orientation,
173
+ focusgroupStartValue,
174
+ toggle,
175
+ register
176
+ ]);
177
+ const state = {
178
+ value: renderedValue,
179
+ pending,
180
+ disabled,
181
+ orientation
182
+ };
183
+ const focusgroup = flag ? `toolbar ${orientation === "vertical" ? "block" : "inline"} ${loop ? "wrap" : "nowrap"} nomemory` : void 0;
184
+ const element = useRender({
185
+ defaultElement: "div",
186
+ render,
187
+ state,
188
+ className,
189
+ style,
190
+ children,
191
+ props: rest,
192
+ libraryProps: {
193
+ role: "group",
194
+ "data-orientation": orientation,
195
+ "data-pending": dataAttr(pending),
196
+ "data-disabled": dataAttr(disabled),
197
+ ...!inToolbar && roving,
198
+ ...focusgroup !== void 0 && { focusgroup }
199
+ },
200
+ ref: rootRef
201
+ });
202
+ return /* @__PURE__ */ jsx(ToggleGroupContext, {
203
+ value: context,
204
+ children: element
205
+ });
206
+ }
207
+ ToggleGroupRoot.displayName = "ToggleGroupRoot";
208
+ //#endregion
209
+ export { ToggleGroupRoot };
210
+
211
+ //# sourceMappingURL=toggle-group-root.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggle-group-root.mjs","names":[],"sources":["../../src/toggle-group/toggle-group-root.tsx"],"sourcesContent":["\"use client\";\nimport {\n useCallback,\n useEffectEvent,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport { createActionDetails } from \"../internal/action-details.js\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport { useToolbarContext } from \"../internal/toolbar-context.js\";\nimport { transitionTypes } from \"../internal/transition-types.js\";\nimport type { ActionDetails, ChangeAction, RenderProp, StateValue } from \"../internal/types.js\";\nimport { useControllableAction } from \"../internal/use-controllable-action.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { useLatest } from \"../internal/use-latest.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { useRovingFocus } from \"../internal/use-roving-focus.js\";\nimport { ToggleGroupContext } from \"./toggle-group-context.js\";\nimport type { ToggleGroupContextValue, ToggleGroupOrientation } from \"./toggle-group-context.js\";\n\nexport type { ToggleGroupOrientation } from \"./toggle-group-context.js\";\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\n/** Details passed as the second argument of `valueChangeAction`, in both modes. */\nexport interface ToggleGroupValueChangeDetails extends ActionDetails {\n /** Values this dispatch pressed (document order of their Items). Exclusive mode: `[next]` or `[]`. */\n readonly added: readonly string[];\n /** Values this dispatch released. Exclusive mode: `[previous]` or `[]`. */\n readonly removed: readonly string[];\n}\n\nexport interface ToggleGroupRootState {\n /**\n * Optimistic pressed values in document order of the Items (exclusive mode: `[value]` or `[]`,\n * so one state shape serves both modes). The server render and the first client render use the\n * value's own order. Content rendered from it must not suspend.\n */\n readonly value: readonly string[];\n /** An action dispatched through this group is in flight. */\n readonly pending: boolean;\n readonly disabled: boolean;\n /** The effective orientation: the Toolbar's when inside one. */\n readonly orientation: ToggleGroupOrientation;\n}\n\ninterface ToggleGroupRootBaseProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n | \"role\"\n | \"onKeyDown\"\n | \"onFocus\"\n | \"onBlur\"\n | \"onPointerDown\"\n | \"defaultValue\"\n | \"className\"\n | \"style\"\n | \"children\"\n> {\n /** Axis of the arrow keys and `data-orientation`. Default `\"horizontal\"`. Ignored inside a Toolbar (dev warning). */\n orientation?: ToggleGroupOrientation;\n /** Whether arrows wrap from the last Item to the first. Default `true`. Ignored inside a Toolbar (dev warning). */\n loop?: boolean;\n /** Disables every Item (native `disabled`). */\n disabled?: boolean;\n /**\n * Render `focusgroup=\"toolbar …\"` and, where the browser implements it, let the browser move\n * focus between the Items. Chromium 150+ only; elsewhere the JS path stays active. Ignored\n * inside a Toolbar (a nested focusgroup would split the Toolbar's sequence; dev warning).\n * Default `false`.\n */\n experimental_focusgroup?: boolean;\n className?: StateValue<string | undefined, ToggleGroupRootState>;\n style?: StateValue<CSSProperties | undefined, ToggleGroupRootState>;\n children?: StateValue<ReactNode, ToggleGroupRootState>;\n render?: RenderProp<ComponentPropsWithRef<\"div\">, ToggleGroupRootState>;\n}\n\nexport interface ToggleGroupMultipleProps extends ToggleGroupRootBaseProps {\n exclusive?: false;\n /** Controlled pressed values. `[]` presses nothing; `undefined` means uncontrolled. Order is not significant; duplicates ignored. */\n value?: string[];\n /** Initial pressed values in uncontrolled mode. Default `[]`. */\n defaultValue?: string[];\n /**\n * Runs inside the Root's transition whenever the set of pressed Items changes. Sync or async (a\n * Server Function may be passed directly); rejections reach the nearest Error Boundary and roll\n * the change back. `next` is the whole set as a fresh array in document order;\n * `details.added` / `details.removed` say what changed. A `setState` after your own `await` is\n * not a Transition update; wrap it in `startTransition`. Prefer a functional update keyed by\n * `added` / `removed` when controlled.\n */\n valueChangeAction?: ChangeAction<string[], ToggleGroupValueChangeDetails>;\n /** Exclusive mode only: a type error here (TypeScript's union excess-property check would let it through otherwise). */\n deselectable?: never;\n}\n\nexport interface ToggleGroupExclusiveProps extends ToggleGroupRootBaseProps {\n /** At most one Item is pressed; `value` is `string | null`. */\n exclusive: true;\n /** Controlled pressed value. `null` presses nothing; `undefined` means uncontrolled. */\n value?: string | null;\n /** Initial pressed value in uncontrolled mode. Default `null`. */\n defaultValue?: string | null;\n /**\n * Whether pressing the pressed Item releases it (`next === null`). Default `true`. With\n * `false` that press is a no-op and dispatches nothing; `value` stays `string | null` because\n * the group may start with nothing pressed, but the action is then never called with `null`.\n */\n deselectable?: boolean;\n /** See {@link ToggleGroupMultipleProps.valueChangeAction}; `next` is the pressed value or `null`. */\n valueChangeAction?: ChangeAction<string | null, ToggleGroupValueChangeDetails>;\n}\n\n/** Props of `ToggleGroup.Root`, discriminated on `exclusive`. */\nexport type ToggleGroupRootProps = ToggleGroupMultipleProps | ToggleGroupExclusiveProps;\n\ntype GroupValue = string[] | string | null;\ntype ValueSet = ReadonlySet<string>;\n\n/** Both variants as one shape, for the implementation. */\ninterface InternalProps extends ToggleGroupRootBaseProps {\n exclusive?: boolean;\n value?: GroupValue;\n defaultValue?: GroupValue;\n deselectable?: boolean;\n valueChangeAction?: ChangeAction<GroupValue, ToggleGroupValueChangeDetails>;\n}\n\nconst EMPTY: ValueSet = new Set();\n\nfunction toSet(value: GroupValue): ValueSet {\n if (value === null) return EMPTY;\n return typeof value === \"string\" ? new Set([value]) : new Set(value);\n}\n\ninterface Patch {\n readonly added: readonly string[];\n readonly removed: readonly string[];\n}\n\n/** The optimistic reducer (design 5.2): apply a patch, so stacked dispatches re-base. */\nfunction reduce(set: ValueSet, patch: Patch): ValueSet {\n const next = new Set(set);\n for (const v of patch.removed) next.delete(v);\n for (const v of patch.added) next.add(v);\n return next;\n}\n\n/** `values` sorted by `order`; values not in `order` keep their iteration order, last. */\nfunction sortByOrder(values: Iterable<string>, order: readonly string[]): string[] {\n const wanted = new Set(values);\n const sorted: string[] = [];\n for (const v of order) if (wanted.delete(v)) sorted.push(v);\n for (const v of wanted) sorted.push(v);\n return sorted;\n}\n\nfunction arraysEqual(a: readonly string[], b: readonly string[]): boolean {\n return a.length === b.length && a.every((v, i) => v === b[i]);\n}\n\nfunction byDocumentOrder(a: Node, b: Node): number {\n if (a === b) return 0;\n return a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;\n}\n\n/** Standalone entry (design 6.2): the first pressed item, else the first item (radio-like). */\nfunction pressedFirst(items: readonly HTMLElement[]): HTMLElement | undefined {\n return items.find((el) => el.getAttribute(\"aria-pressed\") === \"true\") ?? items[0];\n}\n\n/** Engine capabilities never change while the page lives. */\nconst subscribeNever = () => () => {};\n\n/** The Items' document order and their disabled values, as of the Root's last commit. */\ninterface ItemLayout {\n readonly order: readonly string[];\n readonly disabled: readonly string[];\n}\n\nconst NO_LAYOUT: ItemLayout = { order: [], disabled: [] };\n\n/**\n * `<div role=\"group\">` that owns the pressed set, the transition and, when standalone, the roving\n * focus over its Items (design/components/toggle-group.md 16.2). Inside a `Toolbar.Root` it runs\n * no engine of its own: its Items are members of the Toolbar's sequence.\n *\n * Owned keys (conventions 1.2): `role`, `data-orientation`, `data-pending`, `data-disabled`;\n * standalone also `onKeyDown`, `onFocus`, `onBlur`, `onPointerDown` and, with the flag,\n * `focusgroup`.\n */\nexport function ToggleGroupRoot(props: ToggleGroupRootProps) {\n const {\n exclusive = false,\n value,\n defaultValue,\n valueChangeAction,\n deselectable = true,\n orientation: orientationProp,\n loop: loopProp,\n disabled = false,\n experimental_focusgroup: focusgroupProp,\n className,\n style,\n children,\n render,\n ...rest\n } = props as InternalProps;\n\n const toolbar = useToolbarContext();\n const inToolbar = toolbar !== null;\n useDevWarning(\n inToolbar && orientationProp !== undefined,\n \"ToggleGroup.Root: `orientation` is ignored inside a Toolbar.Root; the group follows the \" +\n \"Toolbar's orientation.\",\n );\n useDevWarning(\n inToolbar && loopProp !== undefined,\n \"ToggleGroup.Root: `loop` is ignored inside a Toolbar.Root; the Toolbar's `loop` applies.\",\n );\n useDevWarning(\n inToolbar && focusgroupProp === true,\n \"ToggleGroup.Root: `experimental_focusgroup` is ignored inside a Toolbar.Root. A nested \" +\n \"focusgroup would split the Toolbar's single Tab stop; set the flag on the Toolbar.Root.\",\n );\n const orientation: ToggleGroupOrientation =\n toolbar?.orientation ?? orientationProp ?? \"horizontal\";\n const loop = loopProp ?? true;\n const flag = !inToolbar && focusgroupProp === true;\n\n // The mode is fixed at mount (design 3.1).\n const [mountExclusive] = useState(exclusive);\n useDevWarning(\n mountExclusive !== exclusive,\n \"ToggleGroup.Root: `exclusive` changed after mount. Switching between exclusive and \" +\n \"multiple mode is not supported; the group keeps the mode it mounted with.\",\n );\n\n // Committed set keyed on the contents, so an inline `value={[\"a\"]}` does not re-base every render.\n const controlledKey = value === undefined ? undefined : JSON.stringify(value);\n const controlledSet = useMemo(\n () =>\n controlledKey === undefined ? undefined : toSet(JSON.parse(controlledKey) as GroupValue),\n [controlledKey],\n );\n const defaultKey = defaultValue === undefined ? undefined : JSON.stringify(defaultValue);\n const defaultSet = useMemo(\n () => (defaultKey === undefined ? undefined : toSet(JSON.parse(defaultKey) as GroupValue)),\n [defaultKey],\n );\n\n // Item registration (design 16.2): element → value, written from the Items' callback refs.\n const registry = useRef(new Map<HTMLElement, string>());\n const register = useCallback((element: HTMLElement, itemValue: string) => {\n registry.current.set(element, itemValue);\n return () => {\n registry.current.delete(element);\n };\n }, []);\n const sortedItems = (): [HTMLElement, string][] =>\n [...registry.current].sort(([a], [b]) => byDocumentOrder(a, b));\n /** Sorts by the Items' current document order. Reads the DOM: handlers and effects only. */\n const sortByDocument = (values: Iterable<string>): string[] =>\n sortByOrder(\n values,\n sortedItems().map(([, v]) => v),\n );\n\n const { optimistic, committed, pending, dispatch } = useControllableAction<\n ValueSet,\n ToggleGroupValueChangeDetails\n >({\n controlled: controlledSet,\n defaultValue: defaultSet,\n initialValue: EMPTY,\n action:\n valueChangeAction &&\n ((next, details) =>\n valueChangeAction(mountExclusive ? ([...next][0] ?? null) : sortByDocument(next), details)),\n transitionTypes: (_next, _prev, details) => [\n ...transitionTypes(\"toggle-group\"),\n ...(details.added.length > 0 ? [\"fsui-toggle-group-press\"] : []),\n ...(details.removed.length > 0 ? [\"fsui-toggle-group-release\"] : []),\n ],\n reducer: (prev, _next, details) => reduce(prev, details),\n warn: { component: \"ToggleGroup.Root\", prop: \"value\" },\n });\n\n // Read by the stable `toggle` (#48): a handler only runs against the committed UI.\n const latest = useLatest({ optimistic, exclusive: mountExclusive, deselectable });\n const toggle = useCallback(\n (itemValue: string, event: Event) => {\n const { optimistic, exclusive, deselectable } = latest.current;\n let patch: Patch;\n if (optimistic.has(itemValue)) {\n // Exclusive and not deselectable: pressing the pressed Item is a no-op (design 5.2).\n if (exclusive && !deselectable) return;\n patch = { added: [], removed: [itemValue] };\n } else {\n patch = { added: [itemValue], removed: exclusive ? [...optimistic] : [] };\n }\n dispatch(reduce(optimistic, patch), createActionDetails(event, patch));\n },\n [dispatch, latest],\n );\n\n // `state.value` is in document order. Render cannot read the DOM, so the Items' order (and,\n // for `focusgroupstart`, which values have a disabled Item) is kept in state, refreshed after a\n // commit of the Root only when what renders from it would change (CheckboxGroup's approach).\n const [layout, setLayout] = useState<ItemLayout>(NO_LAYOUT);\n const renderedValue = sortByOrder(optimistic, layout.order);\n const warnedDuplicate = useRef(false);\n const syncLayout = useEffectEvent(() => {\n const items = sortedItems();\n const order = items.map(([, v]) => v);\n const disabledValues = items.filter(([el]) => el.matches(\":disabled\")).map(([, v]) => v);\n const valueChanged = !arraysEqual(sortByOrder(optimistic, order), renderedValue);\n const startChanged =\n flag && (!arraysEqual(order, layout.order) || !arraysEqual(disabledValues, layout.disabled));\n // A DOM read (the Items' order) that render cannot do; the comparison ends the chain.\n if (valueChanged || startChanged) setLayout({ order, disabled: disabledValues });\n\n if (!isDev || warnedDuplicate.current) return;\n const duplicate = order.find((v, i) => order.indexOf(v) !== i);\n if (duplicate !== undefined) {\n warnedDuplicate.current = true;\n console.error(\n `ToggleGroup.Item: two Items share the value \"${duplicate}\" in one ToggleGroup.Root. ` +\n \"Item values must be unique; both follow the same pressed state.\",\n );\n }\n });\n useLayoutEffect(() => {\n // oxlint-disable-next-line react/set-state-in-effect -- a DOM read render cannot do (as CheckboxGroup.Root)\n syncLayout();\n });\n\n // Feature detection only when the flag is on, never during SSR or hydration (conventions 8).\n const native = useSyncExternalStore(\n subscribeNever,\n () => flag && \"focusGroup\" in HTMLElement.prototype,\n () => false,\n );\n\n const focusgroupStartValue =\n flag && !disabled\n ? (renderedValue.find(\n (v) =>\n (layout.order.length === 0 || layout.order.includes(v)) && !layout.disabled.includes(v),\n ) ?? null)\n : null;\n\n const rootRef = useRef<HTMLElement | null>(null);\n const roving = useRovingFocus(rootRef, {\n enabled: !inToolbar,\n orientation,\n loop,\n native,\n entry: pressedFirst,\n memory: false,\n });\n\n const context = useMemo<ToggleGroupContextValue>(\n () => ({\n optimistic,\n committed,\n pending,\n disabled,\n orientation,\n focusgroupStartValue,\n toggle,\n register,\n }),\n [optimistic, committed, pending, disabled, orientation, focusgroupStartValue, toggle, register],\n );\n\n const state: ToggleGroupRootState = { value: renderedValue, pending, disabled, orientation };\n\n const focusgroup = flag\n ? `toolbar ${orientation === \"vertical\" ? \"block\" : \"inline\"} ${loop ? \"wrap\" : \"nowrap\"} nomemory`\n : undefined;\n\n const element = useRender<ComponentPropsWithRef<\"div\">, ToggleGroupRootState>({\n defaultElement: \"div\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps: {\n role: \"group\",\n \"data-orientation\": orientation,\n \"data-pending\": dataAttr(pending),\n \"data-disabled\": dataAttr(disabled),\n // Inside a Toolbar the group has no engine: the keys are not owned there.\n ...(!inToolbar && roving),\n ...(focusgroup !== undefined && { focusgroup }),\n },\n ref: rootRef,\n });\n\n return <ToggleGroupContext value={context}>{element}</ToggleGroupContext>;\n}\n\nToggleGroupRoot.displayName = \"ToggleGroupRoot\";\n"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import "../internal/platform-attributes.mjs";
2
+ import { ToolbarButton, ToolbarButtonActionDetails, ToolbarButtonProps, ToolbarButtonState } from "./toolbar-button.mjs";
3
+ import { ToolbarOrientation, ToolbarRoot, ToolbarRootProps, ToolbarRootState } from "./toolbar-root.mjs";
4
+ import { ToolbarSeparator, ToolbarSeparatorProps, ToolbarSeparatorState } from "./toolbar-separator.mjs";
5
+ //#region src/toolbar/index.d.ts
6
+ /**
7
+ * Toolbar (WAI-ARIA APG Toolbar pattern): `Root` (div role="toolbar") with one Tab stop and
8
+ * arrow keys over its focusable descendants, `Button` (an action button, focusable when
9
+ * disabled) and `Separator` (hr). Any focusable element is an item: no wrapper parts.
10
+ */
11
+ export declare const Toolbar: {
12
+ readonly Root: typeof ToolbarRoot;
13
+ readonly Button: typeof ToolbarButton;
14
+ readonly Separator: typeof ToolbarSeparator;
15
+ };
16
+ //#endregion
17
+ export type { ToolbarButtonActionDetails, ToolbarButtonProps, ToolbarButtonState, ToolbarOrientation, ToolbarRootProps, ToolbarRootState, ToolbarSeparatorProps, ToolbarSeparatorState };
18
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/toolbar/index.ts"],"mappings":";;;;;;;;;;qBAWa;WACX,aAAI;WACJ,eAAM;WACN,kBAAS"}
@@ -0,0 +1,18 @@
1
+ import { ToolbarButton } from "./toolbar-button.mjs";
2
+ import { ToolbarRoot } from "./toolbar-root.mjs";
3
+ import { ToolbarSeparator } from "./toolbar-separator.mjs";
4
+ //#region src/toolbar/index.ts
5
+ /**
6
+ * Toolbar (WAI-ARIA APG Toolbar pattern): `Root` (div role="toolbar") with one Tab stop and
7
+ * arrow keys over its focusable descendants, `Button` (an action button, focusable when
8
+ * disabled) and `Separator` (hr). Any focusable element is an item: no wrapper parts.
9
+ */
10
+ const Toolbar = {
11
+ Root: ToolbarRoot,
12
+ Button: ToolbarButton,
13
+ Separator: ToolbarSeparator
14
+ };
15
+ //#endregion
16
+ export { Toolbar };
17
+
18
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/toolbar/index.ts"],"sourcesContent":["// Ships the platform attribute typings with this subpath (conventions 7.4). Type-only.\nimport \"../internal/platform-attributes.js\";\nimport { ToolbarButton } from \"./toolbar-button.js\";\nimport { ToolbarRoot } from \"./toolbar-root.js\";\nimport { ToolbarSeparator } from \"./toolbar-separator.js\";\n\n/**\n * Toolbar (WAI-ARIA APG Toolbar pattern): `Root` (div role=\"toolbar\") with one Tab stop and\n * arrow keys over its focusable descendants, `Button` (an action button, focusable when\n * disabled) and `Separator` (hr). Any focusable element is an item: no wrapper parts.\n */\nexport const Toolbar = {\n Root: ToolbarRoot,\n Button: ToolbarButton,\n Separator: ToolbarSeparator,\n} as const;\n\nexport type { ToolbarRootProps, ToolbarRootState, ToolbarOrientation } from \"./toolbar-root.js\";\nexport type {\n ToolbarButtonProps,\n ToolbarButtonState,\n ToolbarButtonActionDetails,\n} from \"./toolbar-button.js\";\nexport type { ToolbarSeparatorProps, ToolbarSeparatorState } from \"./toolbar-separator.js\";\n"],"mappings":";;;;;;;;;AAWA,MAAa,UAAU;CACrB,MAAM;CACN,QAAQ;CACR,WAAW;AACb"}
@@ -0,0 +1,42 @@
1
+ import { Action, ActionDetails, RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { CSSProperties, ComponentPropsWithRef, ReactNode, ViewTransitionClass } from "react";
3
+ //#region src/toolbar/toolbar-button.d.ts
4
+ /** Details passed to `Toolbar.Button`'s `action`. */
5
+ export interface ToolbarButtonActionDetails extends ActionDetails {}
6
+ export interface ToolbarButtonState {
7
+ /** This button's action is in flight. */
8
+ readonly pending: boolean;
9
+ readonly disabled: boolean;
10
+ }
11
+ export interface ToolbarButtonProps extends Omit<ComponentPropsWithRef<"button">, "onClick" | "type" | "tabIndex" | "aria-disabled" | "className" | "style" | "children"> {
12
+ /** Runs inside this button's own transition on click, Enter or Space. Sync or async; rejections reach the nearest Error Boundary. */
13
+ action?: Action<[details: ToolbarButtonActionDetails]>;
14
+ /**
15
+ * `aria-disabled="true"`: the button stays focusable and in the arrow-key sequence so
16
+ * assistive technology still finds it (APG Toolbar example), and never dispatches.
17
+ */
18
+ disabled?: boolean;
19
+ /**
20
+ * Forwarded as `default` to the button's `<ViewTransition>`: animates the commit that ends the
21
+ * action (content keyed on `pending`), not a click. Static; any value makes a mount inside a
22
+ * transition start a document view transition (issue #35).
23
+ */
24
+ transition?: ViewTransitionClass;
25
+ className?: StateValue<string | undefined, ToolbarButtonState>;
26
+ style?: StateValue<CSSProperties | undefined, ToolbarButtonState>;
27
+ children?: StateValue<ReactNode, ToolbarButtonState>;
28
+ render?: RenderProp<ComponentPropsWithRef<"button">, ToolbarButtonState>;
29
+ }
30
+ /**
31
+ * A toolbar command: `<button type="button">` that runs `action` in its own transition
32
+ * (`data-pending` while it runs). Disabled is `aria-disabled`, so it stays focusable.
33
+ *
34
+ * Owned keys (conventions 1.2): `type` (on a `<button>` only), `aria-disabled`, `data-pending`,
35
+ * `data-disabled`, `onClick`.
36
+ */
37
+ export declare function ToolbarButton({ action, disabled, transition, className, style, children, render, ...rest }: ToolbarButtonProps): ReactNode;
38
+ export declare namespace ToolbarButton {
39
+ var displayName: string;
40
+ }
41
+ //#endregion
42
+ //# sourceMappingURL=toolbar-button.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolbar-button.d.mts","names":[],"sources":["../../src/toolbar/toolbar-button.tsx"],"mappings":";;;;iBAgBiB,mCAAmC;iBAEnC;;WAEN;WACA;;iBAGM,2BAA2B,KAC1C;;EAIA,SAAS,QAAQ,SAAS;;;;;EAK1B;;;;;;EAMA,aAAa;EACb,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,iCAAiC;;;;;;;;;wBAUvC,gBACd,QACA,UACA,YACA,WACA,OACA,UACA,WACG,QACF,qBAAkB;yBATL"}
@@ -0,0 +1,50 @@
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 { rendersButton } from "../internal/render-element.mjs";
6
+ import { useRender } from "../internal/use-render.mjs";
7
+ import { useActionTrigger } from "../internal/use-action-trigger.mjs";
8
+ //#region src/toolbar/toolbar-button.tsx
9
+ /**
10
+ * A toolbar command: `<button type="button">` that runs `action` in its own transition
11
+ * (`data-pending` while it runs). Disabled is `aria-disabled`, so it stays focusable.
12
+ *
13
+ * Owned keys (conventions 1.2): `type` (on a `<button>` only), `aria-disabled`, `data-pending`,
14
+ * `data-disabled`, `onClick`.
15
+ */
16
+ function ToolbarButton({ action, disabled = false, transition, className, style, children, render, ...rest }) {
17
+ const { pending, run } = useActionTrigger();
18
+ const element = useRender({
19
+ defaultElement: "button",
20
+ render,
21
+ state: {
22
+ pending,
23
+ disabled
24
+ },
25
+ className,
26
+ style,
27
+ children,
28
+ props: rest,
29
+ libraryProps: {
30
+ ...rendersButton(render) && { type: "button" },
31
+ "aria-disabled": disabled ? true : void 0,
32
+ "data-pending": dataAttr(pending),
33
+ "data-disabled": dataAttr(disabled),
34
+ onClick: (event) => {
35
+ if (disabled) {
36
+ event.preventDefault();
37
+ return;
38
+ }
39
+ const details = createActionDetails(event.nativeEvent, {});
40
+ run(() => action?.(details));
41
+ }
42
+ }
43
+ });
44
+ return useTransitionBoundary("Toolbar.Button", transition, element);
45
+ }
46
+ ToolbarButton.displayName = "ToolbarButton";
47
+ //#endregion
48
+ export { ToolbarButton };
49
+
50
+ //# sourceMappingURL=toolbar-button.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolbar-button.mjs","names":[],"sources":["../../src/toolbar/toolbar-button.tsx"],"sourcesContent":["\"use client\";\nimport type {\n ComponentPropsWithRef,\n CSSProperties,\n MouseEvent,\n ReactNode,\n ViewTransitionClass,\n} from \"react\";\nimport { createActionDetails } from \"../internal/action-details.js\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport { useTransitionBoundary } from \"../internal/transition-boundary.js\";\nimport type { Action, ActionDetails, RenderProp, StateValue } from \"../internal/types.js\";\nimport { useActionTrigger } from \"../internal/use-action-trigger.js\";\nimport { rendersButton, useRender } from \"../internal/use-render.js\";\n\n/** Details passed to `Toolbar.Button`'s `action`. */\nexport interface ToolbarButtonActionDetails extends ActionDetails {}\n\nexport interface ToolbarButtonState {\n /** This button's action is in flight. */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\nexport interface ToolbarButtonProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n \"onClick\" | \"type\" | \"tabIndex\" | \"aria-disabled\" | \"className\" | \"style\" | \"children\"\n> {\n /** Runs inside this button's own transition on click, Enter or Space. Sync or async; rejections reach the nearest Error Boundary. */\n action?: Action<[details: ToolbarButtonActionDetails]>;\n /**\n * `aria-disabled=\"true\"`: the button stays focusable and in the arrow-key sequence so\n * assistive technology still finds it (APG Toolbar example), and never dispatches.\n */\n disabled?: boolean;\n /**\n * Forwarded as `default` to the button's `<ViewTransition>`: animates the commit that ends the\n * action (content keyed on `pending`), not a click. Static; any value makes a mount inside a\n * transition start a document view transition (issue #35).\n */\n transition?: ViewTransitionClass;\n className?: StateValue<string | undefined, ToolbarButtonState>;\n style?: StateValue<CSSProperties | undefined, ToolbarButtonState>;\n children?: StateValue<ReactNode, ToolbarButtonState>;\n render?: RenderProp<ComponentPropsWithRef<\"button\">, ToolbarButtonState>;\n}\n\n/**\n * A toolbar command: `<button type=\"button\">` that runs `action` in its own transition\n * (`data-pending` while it runs). Disabled is `aria-disabled`, so it stays focusable.\n *\n * Owned keys (conventions 1.2): `type` (on a `<button>` only), `aria-disabled`, `data-pending`,\n * `data-disabled`, `onClick`.\n */\nexport function ToolbarButton({\n action,\n disabled = false,\n transition,\n className,\n style,\n children,\n render,\n ...rest\n}: ToolbarButtonProps) {\n const { pending, run } = useActionTrigger();\n const state: ToolbarButtonState = { pending, disabled };\n\n const element = useRender<ComponentPropsWithRef<\"button\">, ToolbarButtonState>({\n defaultElement: \"button\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps: {\n ...(rendersButton(render) && { type: \"button\" as const }),\n \"aria-disabled\": disabled ? true : undefined,\n \"data-pending\": dataAttr(pending),\n \"data-disabled\": dataAttr(disabled),\n onClick: (event: MouseEvent<HTMLButtonElement>) => {\n if (disabled) {\n // A disabled link must not navigate either.\n event.preventDefault();\n return;\n }\n const details: ToolbarButtonActionDetails = createActionDetails(event.nativeEvent, {});\n run(() => action?.(details));\n },\n },\n });\n\n return useTransitionBoundary(\"Toolbar.Button\", transition, element);\n}\n\nToolbarButton.displayName = \"ToolbarButton\";\n"],"mappings":";;;;;;;;;;;;;;;AAsDA,SAAgB,cAAc,EAC5B,QACA,WAAW,OACX,YACA,WACA,OACA,UACA,QACA,GAAG,QACkB;CACrB,MAAM,EAAE,SAAS,QAAQ,iBAAiB;CAG1C,MAAM,UAAU,UAA+D;EAC7E,gBAAgB;EAChB;EACA,OAAA;GALkC;GAAS;EAKvC;EACJ;EACA;EACA;EACA,OAAO;EACP,cAAc;GACZ,GAAI,cAAc,MAAM,KAAK,EAAE,MAAM,SAAkB;GACvD,iBAAiB,WAAW,OAAO,KAAA;GACnC,gBAAgB,SAAS,OAAO;GAChC,iBAAiB,SAAS,QAAQ;GAClC,UAAU,UAAyC;IACjD,IAAI,UAAU;KAEZ,MAAM,eAAe;KACrB;IACF;IACA,MAAM,UAAsC,oBAAoB,MAAM,aAAa,CAAC,CAAC;IACrF,UAAU,SAAS,OAAO,CAAC;GAC7B;EACF;CACF,CAAC;CAED,OAAO,sBAAsB,kBAAkB,YAAY,OAAO;AACpE;AAEA,cAAc,cAAc"}
@@ -0,0 +1,38 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
3
+ //#region src/toolbar/toolbar-root.d.ts
4
+ export type ToolbarOrientation = "horizontal" | "vertical";
5
+ export interface ToolbarRootState {
6
+ readonly orientation: ToolbarOrientation;
7
+ }
8
+ export interface ToolbarRootProps extends Omit<ComponentPropsWithRef<"div">, "role" | "aria-orientation" | "onKeyDown" | "onFocus" | "onBlur" | "onPointerDown" | "className" | "style" | "children"> {
9
+ /** Axis of the arrow keys; also `aria-orientation`. Default `"horizontal"`. */
10
+ orientation?: ToolbarOrientation;
11
+ /** Whether arrows wrap from the last item to the first. Default `true`. */
12
+ loop?: boolean;
13
+ /**
14
+ * Render `focusgroup="toolbar …"` and, where the browser implements it, let the browser handle
15
+ * arrow keys, Home and End and the single Tab stop. Chromium 150+ only; elsewhere the JS path
16
+ * stays active. Default `false`.
17
+ */
18
+ experimental_focusgroup?: boolean;
19
+ className?: StateValue<string | undefined, ToolbarRootState>;
20
+ style?: StateValue<CSSProperties | undefined, ToolbarRootState>;
21
+ children?: StateValue<ReactNode, ToolbarRootState>;
22
+ render?: RenderProp<ComponentPropsWithRef<"div">, ToolbarRootState>;
23
+ }
24
+ /**
25
+ * `<div role="toolbar" aria-orientation>` (WAI-ARIA APG Toolbar). Its items are its focusable
26
+ * descendants, found in the DOM: one Tab stop (the last focused item), arrow keys, Home and End
27
+ * between them (design/components/toggle-group.md section 6). Name it with `aria-label` or
28
+ * `aria-labelledby`.
29
+ *
30
+ * Owned keys (conventions 1.2): `role`, `aria-orientation`, `data-orientation`, `onKeyDown`,
31
+ * `onFocus`, `onBlur`, `onPointerDown`, and `focusgroup` while the flag is on.
32
+ */
33
+ export declare function ToolbarRoot({ orientation, loop, experimental_focusgroup, className, style, children, render, ...rest }: ToolbarRootProps): import("react").JSX.Element;
34
+ export declare namespace ToolbarRoot {
35
+ var displayName: string;
36
+ }
37
+ //#endregion
38
+ //# sourceMappingURL=toolbar-root.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolbar-root.d.mts","names":[],"sources":["../../src/toolbar/toolbar-root.tsx"],"mappings":";;;YASY;iBAEK;WACN,aAAa;;iBAGP,yBAAyB,KACxC;;EAYA,cAAc;;EAEd;;;;;;EAMA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,8BAA8B;;;;;;;;;;;wBAwBpC,cACd,aACA,MACA,yBACA,WACA,OACA,UACA,WACG,QACF,mCAAgB,IAAA;yBATH"}
@@ -0,0 +1,66 @@
1
+ "use client";
2
+ import { useRender } from "../internal/use-render.mjs";
3
+ import { ToolbarContext } from "../internal/toolbar-context.mjs";
4
+ import { useRovingFocus } from "../internal/use-roving-focus.mjs";
5
+ import { useMemo, useRef, useSyncExternalStore } from "react";
6
+ import { jsx } from "react/jsx-runtime";
7
+ //#region src/toolbar/toolbar-root.tsx
8
+ /**
9
+ * First entry: the first item. Natively `disabled` controls are not items; `aria-disabled` ones
10
+ * are, and are entered like any other, as Chromium's native `focusgroup` does (the APG allows
11
+ * focusable disabled items in a toolbar).
12
+ */
13
+ function firstItem(items) {
14
+ return items[0];
15
+ }
16
+ /** Engine capabilities never change while the page lives. */
17
+ const subscribeNever = () => () => {};
18
+ /**
19
+ * `<div role="toolbar" aria-orientation>` (WAI-ARIA APG Toolbar). Its items are its focusable
20
+ * descendants, found in the DOM: one Tab stop (the last focused item), arrow keys, Home and End
21
+ * between them (design/components/toggle-group.md section 6). Name it with `aria-label` or
22
+ * `aria-labelledby`.
23
+ *
24
+ * Owned keys (conventions 1.2): `role`, `aria-orientation`, `data-orientation`, `onKeyDown`,
25
+ * `onFocus`, `onBlur`, `onPointerDown`, and `focusgroup` while the flag is on.
26
+ */
27
+ function ToolbarRoot({ orientation = "horizontal", loop = true, experimental_focusgroup = false, className, style, children, render, ...rest }) {
28
+ const native = useSyncExternalStore(subscribeNever, () => experimental_focusgroup && "focusGroup" in HTMLElement.prototype, () => false);
29
+ const rootRef = useRef(null);
30
+ const roving = useRovingFocus(rootRef, {
31
+ orientation,
32
+ loop,
33
+ native,
34
+ entry: firstItem,
35
+ memory: true
36
+ });
37
+ const context = useMemo(() => ({ orientation }), [orientation]);
38
+ const state = { orientation };
39
+ const focusgroup = experimental_focusgroup ? `toolbar ${orientation === "vertical" ? "block" : "inline"} ${loop ? "wrap" : "nowrap"}` : void 0;
40
+ const element = useRender({
41
+ defaultElement: "div",
42
+ render,
43
+ state,
44
+ className,
45
+ style,
46
+ children,
47
+ props: rest,
48
+ libraryProps: {
49
+ role: "toolbar",
50
+ "aria-orientation": orientation,
51
+ "data-orientation": orientation,
52
+ ...roving,
53
+ ...focusgroup !== void 0 && { focusgroup }
54
+ },
55
+ ref: rootRef
56
+ });
57
+ return /* @__PURE__ */ jsx(ToolbarContext, {
58
+ value: context,
59
+ children: element
60
+ });
61
+ }
62
+ ToolbarRoot.displayName = "ToolbarRoot";
63
+ //#endregion
64
+ export { ToolbarRoot };
65
+
66
+ //# sourceMappingURL=toolbar-root.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolbar-root.mjs","names":[],"sources":["../../src/toolbar/toolbar-root.tsx"],"sourcesContent":["\"use client\";\nimport { useMemo, useRef, useSyncExternalStore } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport { ToolbarContext } from \"../internal/toolbar-context.js\";\nimport type { ToolbarContextValue } from \"../internal/toolbar-context.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { useRovingFocus } from \"../internal/use-roving-focus.js\";\n\nexport type ToolbarOrientation = \"horizontal\" | \"vertical\";\n\nexport interface ToolbarRootState {\n readonly orientation: ToolbarOrientation;\n}\n\nexport interface ToolbarRootProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n | \"role\"\n | \"aria-orientation\"\n | \"onKeyDown\"\n | \"onFocus\"\n | \"onBlur\"\n | \"onPointerDown\"\n | \"className\"\n | \"style\"\n | \"children\"\n> {\n /** Axis of the arrow keys; also `aria-orientation`. Default `\"horizontal\"`. */\n orientation?: ToolbarOrientation;\n /** Whether arrows wrap from the last item to the first. Default `true`. */\n loop?: boolean;\n /**\n * Render `focusgroup=\"toolbar …\"` and, where the browser implements it, let the browser handle\n * arrow keys, Home and End and the single Tab stop. Chromium 150+ only; elsewhere the JS path\n * stays active. Default `false`.\n */\n experimental_focusgroup?: boolean;\n className?: StateValue<string | undefined, ToolbarRootState>;\n style?: StateValue<CSSProperties | undefined, ToolbarRootState>;\n children?: StateValue<ReactNode, ToolbarRootState>;\n render?: RenderProp<ComponentPropsWithRef<\"div\">, ToolbarRootState>;\n}\n\n/**\n * First entry: the first item. Natively `disabled` controls are not items; `aria-disabled` ones\n * are, and are entered like any other, as Chromium's native `focusgroup` does (the APG allows\n * focusable disabled items in a toolbar).\n */\nfunction firstItem(items: readonly HTMLElement[]): HTMLElement | undefined {\n return items[0];\n}\n\n/** Engine capabilities never change while the page lives. */\nconst subscribeNever = () => () => {};\n\n/**\n * `<div role=\"toolbar\" aria-orientation>` (WAI-ARIA APG Toolbar). Its items are its focusable\n * descendants, found in the DOM: one Tab stop (the last focused item), arrow keys, Home and End\n * between them (design/components/toggle-group.md section 6). Name it with `aria-label` or\n * `aria-labelledby`.\n *\n * Owned keys (conventions 1.2): `role`, `aria-orientation`, `data-orientation`, `onKeyDown`,\n * `onFocus`, `onBlur`, `onPointerDown`, and `focusgroup` while the flag is on.\n */\nexport function ToolbarRoot({\n orientation = \"horizontal\",\n loop = true,\n experimental_focusgroup = false,\n className,\n style,\n children,\n render,\n ...rest\n}: ToolbarRootProps) {\n // Conventions 8: the server snapshot is `false`, so hydration markup matches the server.\n const native = useSyncExternalStore(\n subscribeNever,\n () => experimental_focusgroup && \"focusGroup\" in HTMLElement.prototype,\n () => false,\n );\n const rootRef = useRef<HTMLElement | null>(null);\n const roving = useRovingFocus(rootRef, {\n orientation,\n loop,\n native,\n entry: firstItem,\n memory: true,\n });\n\n const context = useMemo<ToolbarContextValue>(() => ({ orientation }), [orientation]);\n const state: ToolbarRootState = { orientation };\n\n const focusgroup = experimental_focusgroup\n ? `toolbar ${orientation === \"vertical\" ? \"block\" : \"inline\"} ${loop ? \"wrap\" : \"nowrap\"}`\n : undefined;\n\n const element = useRender<ComponentPropsWithRef<\"div\">, ToolbarRootState>({\n defaultElement: \"div\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps: {\n role: \"toolbar\",\n \"aria-orientation\": orientation,\n \"data-orientation\": orientation,\n ...roving,\n ...(focusgroup !== undefined && { focusgroup }),\n },\n ref: rootRef,\n });\n\n return <ToolbarContext value={context}>{element}</ToolbarContext>;\n}\n\nToolbarRoot.displayName = \"ToolbarRoot\";\n"],"mappings":";;;;;;;;;;;;AAgDA,SAAS,UAAU,OAAwD;CACzE,OAAO,MAAM;AACf;;AAGA,MAAM,6BAA6B,CAAC;;;;;;;;;;AAWpC,SAAgB,YAAY,EAC1B,cAAc,cACd,OAAO,MACP,0BAA0B,OAC1B,WACA,OACA,UACA,QACA,GAAG,QACgB;CAEnB,MAAM,SAAS,qBACb,sBACM,2BAA2B,gBAAgB,YAAY,iBACvD,KACR;CACA,MAAM,UAAU,OAA2B,IAAI;CAC/C,MAAM,SAAS,eAAe,SAAS;EACrC;EACA;EACA;EACA,OAAO;EACP,QAAQ;CACV,CAAC;CAED,MAAM,UAAU,eAAoC,EAAE,YAAY,IAAI,CAAC,WAAW,CAAC;CACnF,MAAM,QAA0B,EAAE,YAAY;CAE9C,MAAM,aAAa,0BACf,WAAW,gBAAgB,aAAa,UAAU,SAAS,GAAG,OAAO,SAAS,aAC9E,KAAA;CAEJ,MAAM,UAAU,UAA0D;EACxE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP,cAAc;GACZ,MAAM;GACN,oBAAoB;GACpB,oBAAoB;GACpB,GAAG;GACH,GAAI,eAAe,KAAA,KAAa,EAAE,WAAW;EAC/C;EACA,KAAK;CACP,CAAC;CAED,OAAO,oBAAC,gBAAD;EAAgB,OAAO;EAAU,UAAA;CAAwB,CAAA;AAClE;AAEA,YAAY,cAAc"}
@@ -0,0 +1,25 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { ToolbarOrientation } from "./toolbar-root.mjs";
3
+ import { CSSProperties, ComponentPropsWithRef } from "react";
4
+ //#region src/toolbar/toolbar-separator.d.ts
5
+ export interface ToolbarSeparatorState {
6
+ /** The separator's own orientation: perpendicular to the Toolbar's. */
7
+ readonly orientation: ToolbarOrientation;
8
+ }
9
+ export interface ToolbarSeparatorProps extends Omit<ComponentPropsWithRef<"hr">, "aria-orientation" | "className" | "style" | "children"> {
10
+ className?: StateValue<string | undefined, ToolbarSeparatorState>;
11
+ style?: StateValue<CSSProperties | undefined, ToolbarSeparatorState>;
12
+ render?: RenderProp<ComponentPropsWithRef<"hr">, ToolbarSeparatorState>;
13
+ }
14
+ /**
15
+ * `<hr aria-orientation>` between toolbar items: vertical in a horizontal Toolbar, horizontal in
16
+ * a vertical one. Not focusable, not an item.
17
+ *
18
+ * Owned keys (conventions 1.2): `aria-orientation`, `data-orientation`.
19
+ */
20
+ export declare function ToolbarSeparator({ className, style, render, ...rest }: ToolbarSeparatorProps): import("react").ReactNode;
21
+ export declare namespace ToolbarSeparator {
22
+ var displayName: string;
23
+ }
24
+ //#endregion
25
+ //# sourceMappingURL=toolbar-separator.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolbar-separator.d.mts","names":[],"sources":["../../src/toolbar/toolbar-separator.tsx"],"mappings":";;;;iBAOiB;;WAEN,aAAa;;iBAGP,8BAA8B,KAC7C;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,SAAS,WAAW,6BAA6B;;;;;;;;wBASnC,mBAAmB,WAAW,OAAO,WAAW,QAAQ,wCAAqB;yBAA7E"}
@@ -0,0 +1,31 @@
1
+ "use client";
2
+ import { useRender } from "../internal/use-render.mjs";
3
+ import { useToolbarContext } from "../internal/toolbar-context.mjs";
4
+ //#region src/toolbar/toolbar-separator.tsx
5
+ /**
6
+ * `<hr aria-orientation>` between toolbar items: vertical in a horizontal Toolbar, horizontal in
7
+ * a vertical one. Not focusable, not an item.
8
+ *
9
+ * Owned keys (conventions 1.2): `aria-orientation`, `data-orientation`.
10
+ */
11
+ function ToolbarSeparator({ className, style, render, ...rest }) {
12
+ const orientation = useToolbarContext()?.orientation === "vertical" ? "horizontal" : "vertical";
13
+ return useRender({
14
+ defaultElement: "hr",
15
+ render,
16
+ state: { orientation },
17
+ className,
18
+ style,
19
+ children: void 0,
20
+ props: rest,
21
+ libraryProps: {
22
+ "aria-orientation": orientation,
23
+ "data-orientation": orientation
24
+ }
25
+ });
26
+ }
27
+ ToolbarSeparator.displayName = "ToolbarSeparator";
28
+ //#endregion
29
+ export { ToolbarSeparator };
30
+
31
+ //# sourceMappingURL=toolbar-separator.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolbar-separator.mjs","names":[],"sources":["../../src/toolbar/toolbar-separator.tsx"],"sourcesContent":["\"use client\";\nimport type { ComponentPropsWithRef, CSSProperties } from \"react\";\nimport { useToolbarContext } from \"../internal/toolbar-context.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport type { ToolbarOrientation } from \"./toolbar-root.js\";\n\nexport interface ToolbarSeparatorState {\n /** The separator's own orientation: perpendicular to the Toolbar's. */\n readonly orientation: ToolbarOrientation;\n}\n\nexport interface ToolbarSeparatorProps extends Omit<\n ComponentPropsWithRef<\"hr\">,\n \"aria-orientation\" | \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, ToolbarSeparatorState>;\n style?: StateValue<CSSProperties | undefined, ToolbarSeparatorState>;\n render?: RenderProp<ComponentPropsWithRef<\"hr\">, ToolbarSeparatorState>;\n}\n\n/**\n * `<hr aria-orientation>` between toolbar items: vertical in a horizontal Toolbar, horizontal in\n * a vertical one. Not focusable, not an item.\n *\n * Owned keys (conventions 1.2): `aria-orientation`, `data-orientation`.\n */\nexport function ToolbarSeparator({ className, style, render, ...rest }: ToolbarSeparatorProps) {\n const toolbar = useToolbarContext();\n const orientation: ToolbarOrientation =\n toolbar?.orientation === \"vertical\" ? \"horizontal\" : \"vertical\";\n const state: ToolbarSeparatorState = { orientation };\n\n return useRender<ComponentPropsWithRef<\"hr\">, ToolbarSeparatorState>({\n defaultElement: \"hr\",\n render,\n state,\n className,\n style,\n children: undefined,\n props: rest,\n libraryProps: {\n \"aria-orientation\": orientation,\n \"data-orientation\": orientation,\n },\n });\n}\n\nToolbarSeparator.displayName = \"ToolbarSeparator\";\n"],"mappings":";;;;;;;;;;AA2BA,SAAgB,iBAAiB,EAAE,WAAW,OAAO,QAAQ,GAAG,QAA+B;CAE7F,MAAM,cADU,kBAER,CAAC,EAAE,gBAAgB,aAAa,eAAe;CAGvD,OAAO,UAA8D;EACnE,gBAAgB;EAChB;EACA,OAAA,EALqC,YAKjC;EACJ;EACA;EACA,UAAU,KAAA;EACV,OAAO;EACP,cAAc;GACZ,oBAAoB;GACpB,oBAAoB;EACtB;CACF,CAAC;AACH;AAEA,iBAAiB,cAAc"}
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { dataAttr } from "../internal/data-attr.mjs";
3
- import { resolveStateValue, useRender } from "../internal/use-render.mjs";
3
+ import { resolveStateValue } from "../internal/render-element.mjs";
4
+ import { useRender } from "../internal/use-render.mjs";
4
5
  import { POSITION_TRY_FALLBACKS, positionArea } from "../internal/anchor-position.mjs";
5
6
  import { useTooltipContext } from "./tooltip-context.mjs";
6
7
  //#region src/tooltip/tooltip-popup.tsx