@arun-dev/headless 4.5.1 → 4.7.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.
@@ -0,0 +1,255 @@
1
+ import {
2
+ byDocumentOrder,
3
+ rovingIndex
4
+ } from "../chunk-2S4XG4VD.js";
5
+ import {
6
+ anchorNameFor,
7
+ anchoredDataAttributes,
8
+ anchoredPopupStyle
9
+ } from "../chunk-HCOCPQXS.js";
10
+ import {
11
+ useControlled
12
+ } from "../chunk-UEIB3I3T.js";
13
+ import {
14
+ __export,
15
+ useRender
16
+ } from "../chunk-OQTWWZ4B.js";
17
+
18
+ // src/menu/index.parts.ts
19
+ var index_parts_exports = {};
20
+ __export(index_parts_exports, {
21
+ Item: () => MenuItem,
22
+ Popup: () => MenuPopup,
23
+ Root: () => MenuRoot,
24
+ Trigger: () => MenuTrigger
25
+ });
26
+
27
+ // src/menu/MenuRoot.tsx
28
+ import { useCallback, useId, useMemo, useRef, useState } from "react";
29
+
30
+ // src/menu/MenuRootContext.ts
31
+ import { createContext, useContext } from "react";
32
+ var MenuRootContext = createContext(null);
33
+ function useMenuRootContext(part) {
34
+ const context = useContext(MenuRootContext);
35
+ if (context === null) {
36
+ throw new Error(`<Menu.${part}> must be rendered inside <Menu.Root>.`);
37
+ }
38
+ return context;
39
+ }
40
+
41
+ // src/menu/MenuRoot.tsx
42
+ import { jsx } from "react/jsx-runtime";
43
+ function MenuRoot({
44
+ open: openProp,
45
+ defaultOpen,
46
+ onOpenChange,
47
+ focusableWhenDisabled = false,
48
+ children
49
+ }) {
50
+ const [open, setOpenState] = useControlled({
51
+ controlled: openProp,
52
+ default: defaultOpen ?? false,
53
+ name: "Menu.Root",
54
+ state: "open"
55
+ });
56
+ const setOpen = useCallback(
57
+ (next) => {
58
+ if (next === open) return;
59
+ setOpenState(next);
60
+ onOpenChange?.(next);
61
+ },
62
+ [open, setOpenState, onOpenChange]
63
+ );
64
+ const [items, setItems] = useState([]);
65
+ const register = useCallback((entry) => {
66
+ setItems((current) => [...current, entry].sort(byDocumentOrder));
67
+ return () => setItems((current) => current.filter((i) => i !== entry));
68
+ }, []);
69
+ const navigableItems = useMemo(
70
+ () => focusableWhenDisabled ? items : items.filter((i) => !i.disabled),
71
+ [items, focusableWhenDisabled]
72
+ );
73
+ const id = useId();
74
+ const triggerRef = useRef(null);
75
+ const focusOnOpenRef = useRef("first");
76
+ const context = useMemo(
77
+ () => ({
78
+ open,
79
+ setOpen,
80
+ navigableItems,
81
+ focusableWhenDisabled,
82
+ register,
83
+ focusOnOpenRef,
84
+ triggerId: `${id}-trigger`,
85
+ popupId: `${id}-popup`,
86
+ anchorName: anchorNameFor(id),
87
+ triggerRef
88
+ }),
89
+ [open, setOpen, navigableItems, focusableWhenDisabled, register, id]
90
+ );
91
+ return /* @__PURE__ */ jsx(MenuRootContext.Provider, { value: context, children });
92
+ }
93
+
94
+ // src/menu/menuDataAttributes.ts
95
+ function menuDataAttributes({ open }) {
96
+ return {
97
+ "data-open": open ? "" : void 0,
98
+ "data-closed": open ? void 0 : ""
99
+ };
100
+ }
101
+ function menuItemDataAttributes(disabled) {
102
+ return { "data-disabled": disabled ? "" : void 0 };
103
+ }
104
+
105
+ // src/menu/MenuTrigger.tsx
106
+ function MenuTrigger({ className, children, render, ...rest }) {
107
+ const { open, setOpen, focusOnOpenRef, triggerId, popupId, anchorName, triggerRef } = useMenuRootContext("Trigger");
108
+ return useRender({
109
+ render,
110
+ defaultTagName: "button",
111
+ props: {
112
+ type: "button",
113
+ id: triggerId,
114
+ "aria-haspopup": "menu",
115
+ "aria-expanded": open,
116
+ "aria-controls": popupId,
117
+ ...menuDataAttributes({ open }),
118
+ style: { anchorName },
119
+ className,
120
+ children,
121
+ ref: triggerRef,
122
+ onClick() {
123
+ focusOnOpenRef.current = "first";
124
+ setOpen(!open);
125
+ },
126
+ onKeyDown(event) {
127
+ if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
128
+ event.preventDefault();
129
+ focusOnOpenRef.current = event.key === "ArrowUp" ? "last" : "first";
130
+ setOpen(true);
131
+ }
132
+ },
133
+ consumerProps: rest
134
+ });
135
+ }
136
+
137
+ // src/menu/MenuPopup.tsx
138
+ import { useEffect, useReducer, useRef as useRef2 } from "react";
139
+ function MenuPopup({
140
+ side = "bottom",
141
+ align = "start",
142
+ className,
143
+ children,
144
+ render,
145
+ ...rest
146
+ }) {
147
+ const {
148
+ open,
149
+ setOpen,
150
+ navigableItems,
151
+ focusOnOpenRef,
152
+ triggerId,
153
+ popupId,
154
+ anchorName,
155
+ triggerRef
156
+ } = useMenuRootContext("Popup");
157
+ const elementRef = useRef2(null);
158
+ const focusPendingRef = useRef2(false);
159
+ const [, resync] = useReducer((n) => n + 1, 0);
160
+ useEffect(() => {
161
+ const popup = elementRef.current;
162
+ if (!popup || typeof popup.showPopover !== "function") return;
163
+ const shown = popup.matches(":popover-open");
164
+ if (open && !shown) {
165
+ popup.showPopover({ source: triggerRef.current ?? void 0 });
166
+ if (!popup.matches(":popover-open")) setOpen(false);
167
+ else focusPendingRef.current = true;
168
+ } else if (!open && shown) {
169
+ focusPendingRef.current = false;
170
+ popup.hidePopover();
171
+ }
172
+ if (focusPendingRef.current && navigableItems.length > 0) {
173
+ focusPendingRef.current = false;
174
+ const target = focusOnOpenRef.current === "last" ? navigableItems.at(-1) : navigableItems[0];
175
+ target?.ref.current?.focus();
176
+ }
177
+ });
178
+ return useRender({
179
+ render,
180
+ defaultTagName: "div",
181
+ props: {
182
+ id: popupId,
183
+ popover: "auto",
184
+ role: "menu",
185
+ "aria-labelledby": triggerId,
186
+ ...anchoredDataAttributes(side, align),
187
+ ...menuDataAttributes({ open }),
188
+ style: anchoredPopupStyle(anchorName, side, align),
189
+ className,
190
+ children,
191
+ ref: elementRef,
192
+ onKeyDown(event) {
193
+ if (event.key === "Tab") {
194
+ setOpen(false);
195
+ return;
196
+ }
197
+ const current = navigableItems.findIndex((i) => i.ref.current === event.target);
198
+ if (current < 0 && event.target !== event.currentTarget) return;
199
+ const next = rovingIndex(event.key, {
200
+ count: navigableItems.length,
201
+ current,
202
+ orientation: "vertical",
203
+ rtl: false
204
+ });
205
+ if (next === null) return;
206
+ event.preventDefault();
207
+ navigableItems[next]?.ref.current?.focus();
208
+ },
209
+ onToggle(event) {
210
+ if (event.target !== event.currentTarget) return;
211
+ if (event.nativeEvent.newState !== "closed") return;
212
+ setOpen(false);
213
+ resync();
214
+ }
215
+ },
216
+ consumerProps: rest
217
+ });
218
+ }
219
+
220
+ // src/menu/MenuItem.tsx
221
+ import { useLayoutEffect, useRef as useRef3 } from "react";
222
+ function MenuItem({
223
+ disabled = false,
224
+ onClick,
225
+ className,
226
+ children,
227
+ render,
228
+ ...rest
229
+ }) {
230
+ const { setOpen, focusableWhenDisabled, register } = useMenuRootContext("Item");
231
+ const elementRef = useRef3(null);
232
+ useLayoutEffect(() => register({ disabled, ref: elementRef }), [register, disabled]);
233
+ return useRender({
234
+ render,
235
+ defaultTagName: "button",
236
+ props: {
237
+ type: "button",
238
+ role: "menuitem",
239
+ tabIndex: -1,
240
+ disabled: disabled && !focusableWhenDisabled || void 0,
241
+ "aria-disabled": disabled && focusableWhenDisabled || void 0,
242
+ ...menuItemDataAttributes(disabled),
243
+ className,
244
+ children,
245
+ ref: elementRef,
246
+ onClick() {
247
+ if (!disabled) setOpen(false);
248
+ }
249
+ },
250
+ consumerProps: disabled ? rest : { ...rest, onClick }
251
+ });
252
+ }
253
+ export {
254
+ index_parts_exports as Menu
255
+ };
@@ -0,0 +1,408 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/tabs/index.ts
21
+ var tabs_exports = {};
22
+ __export(tabs_exports, {
23
+ Tabs: () => index_parts_exports
24
+ });
25
+ module.exports = __toCommonJS(tabs_exports);
26
+
27
+ // src/tabs/index.parts.ts
28
+ var index_parts_exports = {};
29
+ __export(index_parts_exports, {
30
+ List: () => TabsList,
31
+ Panel: () => TabsPanel,
32
+ Root: () => TabsRoot,
33
+ Tab: () => TabsTab
34
+ });
35
+
36
+ // src/tabs/TabsRoot.tsx
37
+ var import_react4 = require("react");
38
+
39
+ // src/core/rovingFocus.ts
40
+ function byDocumentOrder(a, b) {
41
+ const x = a.ref.current;
42
+ const y = b.ref.current;
43
+ if (!x || !y) return 0;
44
+ return x.compareDocumentPosition(y) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
45
+ }
46
+ function rovingIndex(key, {
47
+ count,
48
+ current,
49
+ orientation,
50
+ rtl
51
+ }) {
52
+ if (count === 0) return null;
53
+ const [prevKey, nextKey] = orientation === "vertical" ? ["ArrowUp", "ArrowDown"] : rtl ? ["ArrowRight", "ArrowLeft"] : ["ArrowLeft", "ArrowRight"];
54
+ switch (key) {
55
+ case nextKey:
56
+ return current < 0 ? 0 : (current + 1) % count;
57
+ case prevKey:
58
+ return current < 0 ? count - 1 : (current - 1 + count) % count;
59
+ case "Home":
60
+ return 0;
61
+ case "End":
62
+ return count - 1;
63
+ default:
64
+ return null;
65
+ }
66
+ }
67
+
68
+ // src/core/useControlled.ts
69
+ var import_react = require("react");
70
+ function useControlled({
71
+ controlled,
72
+ default: defaultValue,
73
+ name = "Component",
74
+ state = "value"
75
+ }) {
76
+ const { current: isControlled } = (0, import_react.useRef)(controlled !== void 0);
77
+ const [uncontrolled, setUncontrolled] = (0, import_react.useState)(defaultValue);
78
+ const defaultRef = (0, import_react.useRef)(defaultValue);
79
+ const value = isControlled ? controlled : uncontrolled;
80
+ if (process.env.NODE_ENV !== "production") {
81
+ if (isControlled !== (controlled !== void 0)) {
82
+ console.error(
83
+ `${name}: cannot switch between controlled and uncontrolled \`${state}\`. Decide which one this component is for the whole of its life.`
84
+ );
85
+ }
86
+ if (!isControlled && defaultRef.current !== defaultValue) {
87
+ console.error(
88
+ `${name}: cannot change the default \`${state}\` after mount. It is only read once, so later changes are silently ignored.`
89
+ );
90
+ }
91
+ }
92
+ const setValue = (0, import_react.useCallback)(
93
+ (next) => {
94
+ if (!isControlled) setUncontrolled(next);
95
+ },
96
+ [isControlled]
97
+ );
98
+ return [value, setValue];
99
+ }
100
+
101
+ // src/core/useRender.ts
102
+ var import_react2 = require("react");
103
+
104
+ // src/core/mergeProps.ts
105
+ function isEventHandler(key) {
106
+ return key.length > 2 && key.startsWith("on") && key[2] === key[2]?.toUpperCase();
107
+ }
108
+ function mergeRefs(...refs) {
109
+ return (node) => {
110
+ const cleanups = refs.map((ref) => {
111
+ if (typeof ref === "function") {
112
+ const result = ref(node);
113
+ return typeof result === "function" ? result : () => ref(null);
114
+ }
115
+ if (ref && typeof ref === "object") {
116
+ ref.current = node;
117
+ return () => {
118
+ ref.current = null;
119
+ };
120
+ }
121
+ return void 0;
122
+ });
123
+ return () => {
124
+ cleanups.forEach((cleanup) => {
125
+ if (typeof cleanup === "function") cleanup();
126
+ });
127
+ };
128
+ };
129
+ }
130
+ function isSyntheticEvent(event) {
131
+ return typeof event === "object" && event !== null && "nativeEvent" in event;
132
+ }
133
+ function makeEventPreventable(event) {
134
+ if (Object.prototype.hasOwnProperty.call(event, "preventComponentHandler")) return;
135
+ const target = event;
136
+ target.preventComponentHandler = () => {
137
+ target.componentHandlerPrevented = true;
138
+ };
139
+ }
140
+ function isComponentHandlerPrevented(event) {
141
+ return typeof event === "object" && event !== null && event.componentHandlerPrevented === true;
142
+ }
143
+ function chainHandlers(earlier, value) {
144
+ return (...args) => {
145
+ const event = args[0];
146
+ if (isSyntheticEvent(event)) makeEventPreventable(event);
147
+ value(...args);
148
+ if (isComponentHandlerPrevented(event)) return;
149
+ earlier(...args);
150
+ };
151
+ }
152
+ function mergeProps(...objects) {
153
+ const merged = {};
154
+ for (const props of objects) {
155
+ if (!props) continue;
156
+ for (const key of Object.keys(props)) {
157
+ const value = props[key];
158
+ if (value === void 0) continue;
159
+ if (isEventHandler(key) && typeof value === "function") {
160
+ const earlier = merged[key];
161
+ merged[key] = typeof earlier === "function" ? chainHandlers(earlier, value) : value;
162
+ } else if (key === "className") {
163
+ merged.className = merged.className ? `${String(merged.className)} ${String(value)}` : value;
164
+ } else if (key === "style") {
165
+ merged.style = { ...merged.style, ...value };
166
+ } else if (key === "ref") {
167
+ merged.ref = merged.ref ? mergeRefs(merged.ref, value) : value;
168
+ } else {
169
+ merged[key] = value;
170
+ }
171
+ }
172
+ }
173
+ return merged;
174
+ }
175
+
176
+ // src/core/useRender.ts
177
+ function useRender({
178
+ render,
179
+ defaultTagName,
180
+ props,
181
+ consumerProps
182
+ }) {
183
+ const merged = mergeProps(props, consumerProps);
184
+ if ((0, import_react2.isValidElement)(render)) {
185
+ return (0, import_react2.cloneElement)(render, mergeProps(merged, render.props));
186
+ }
187
+ return (0, import_react2.createElement)(defaultTagName, merged);
188
+ }
189
+
190
+ // src/tabs/TabsRootContext.ts
191
+ var import_react3 = require("react");
192
+ var TabsRootContext = (0, import_react3.createContext)(null);
193
+ function useTabsRootContext(part) {
194
+ const context = (0, import_react3.useContext)(TabsRootContext);
195
+ if (context === null) {
196
+ throw new Error(`<Tabs.${part}> must be rendered inside <Tabs.Root>.`);
197
+ }
198
+ return context;
199
+ }
200
+
201
+ // src/tabs/tabsDataAttributes.ts
202
+ function orientationDataAttributes(orientation) {
203
+ return { "data-orientation": orientation };
204
+ }
205
+ function selectedDataAttributes(selected, disabled = false) {
206
+ return {
207
+ "data-selected": selected ? "" : void 0,
208
+ "data-unselected": selected ? void 0 : "",
209
+ "data-disabled": disabled ? "" : void 0
210
+ };
211
+ }
212
+
213
+ // src/tabs/TabsRoot.tsx
214
+ var import_jsx_runtime = require("react/jsx-runtime");
215
+ function idPart(value) {
216
+ return value.replace(/\s+/g, "_");
217
+ }
218
+ function TabsRoot({
219
+ value: valueProp,
220
+ defaultValue,
221
+ onValueChange,
222
+ orientation = "horizontal",
223
+ activationMode = "automatic",
224
+ focusableWhenDisabled: focusableWhenDisabledProp = false,
225
+ className,
226
+ children,
227
+ render,
228
+ ...rest
229
+ }) {
230
+ const [value, setValueState] = useControlled({
231
+ controlled: valueProp,
232
+ default: defaultValue,
233
+ name: "Tabs.Root",
234
+ state: "value"
235
+ });
236
+ const setValue = (0, import_react4.useCallback)(
237
+ (next) => {
238
+ if (next === value) return;
239
+ setValueState(next);
240
+ onValueChange?.(next);
241
+ },
242
+ [value, setValueState, onValueChange]
243
+ );
244
+ const [tabs, setTabs] = (0, import_react4.useState)([]);
245
+ const register = (0, import_react4.useCallback)((entry) => {
246
+ setTabs(
247
+ (current) => [...current.filter((t) => t.value !== entry.value), entry].sort(byDocumentOrder)
248
+ );
249
+ return () => setTabs((current) => current.filter((t) => t !== entry));
250
+ }, []);
251
+ const focusableWhenDisabled = focusableWhenDisabledProp && activationMode === "manual";
252
+ const navigableTabs = (0, import_react4.useMemo)(
253
+ () => focusableWhenDisabled ? tabs : tabs.filter((t) => !t.disabled),
254
+ [tabs, focusableWhenDisabled]
255
+ );
256
+ const tabStopValue = tabs.length === 0 ? value : (navigableTabs.find((t) => t.value === value) ?? navigableTabs[0])?.value;
257
+ const baseId = (0, import_react4.useId)();
258
+ const context = (0, import_react4.useMemo)(
259
+ () => ({
260
+ value,
261
+ setValue,
262
+ orientation,
263
+ activationMode,
264
+ navigableTabs,
265
+ focusableWhenDisabled,
266
+ tabStopValue,
267
+ register,
268
+ tabId: (v) => `${baseId}-tab-${idPart(v)}`,
269
+ panelId: (v) => `${baseId}-panel-${idPart(v)}`
270
+ }),
271
+ [
272
+ value,
273
+ setValue,
274
+ orientation,
275
+ activationMode,
276
+ navigableTabs,
277
+ focusableWhenDisabled,
278
+ tabStopValue,
279
+ register,
280
+ baseId
281
+ ]
282
+ );
283
+ const element = useRender({
284
+ render,
285
+ defaultTagName: "div",
286
+ props: { ...orientationDataAttributes(orientation), className, children },
287
+ consumerProps: rest
288
+ });
289
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TabsRootContext.Provider, { value: context, children: element });
290
+ }
291
+
292
+ // src/tabs/TabsList.tsx
293
+ var import_react5 = require("react");
294
+ function TabsList({ className, children, render, ...rest }) {
295
+ const { orientation, navigableTabs } = useTabsRootContext("List");
296
+ const elementRef = (0, import_react5.useRef)(null);
297
+ return useRender({
298
+ render,
299
+ defaultTagName: "div",
300
+ props: {
301
+ role: "tablist",
302
+ // Horizontal is the ARIA default for a tablist, so it is only stated when vertical.
303
+ "aria-orientation": orientation === "vertical" ? "vertical" : void 0,
304
+ ...orientationDataAttributes(orientation),
305
+ className,
306
+ children,
307
+ ref: elementRef,
308
+ onKeyDown(event) {
309
+ const current = navigableTabs.findIndex((t) => t.ref.current === event.target);
310
+ if (current < 0) return;
311
+ const list = elementRef.current;
312
+ const rtl = list ? getComputedStyle(list).direction === "rtl" : false;
313
+ const next = rovingIndex(event.key, {
314
+ count: navigableTabs.length,
315
+ current,
316
+ orientation,
317
+ rtl
318
+ });
319
+ if (next === null) return;
320
+ event.preventDefault();
321
+ navigableTabs[next]?.ref.current?.focus();
322
+ }
323
+ },
324
+ consumerProps: rest
325
+ });
326
+ }
327
+
328
+ // src/tabs/TabsTab.tsx
329
+ var import_react6 = require("react");
330
+ function TabsTab({
331
+ value,
332
+ disabled = false,
333
+ onClick,
334
+ className,
335
+ children,
336
+ render,
337
+ ...rest
338
+ }) {
339
+ const {
340
+ value: selectedValue,
341
+ setValue,
342
+ activationMode,
343
+ focusableWhenDisabled,
344
+ tabStopValue,
345
+ register,
346
+ tabId,
347
+ panelId
348
+ } = useTabsRootContext("Tab");
349
+ const elementRef = (0, import_react6.useRef)(null);
350
+ const selected = value === selectedValue;
351
+ (0, import_react6.useLayoutEffect)(
352
+ () => register({ value, disabled, ref: elementRef }),
353
+ [register, value, disabled]
354
+ );
355
+ return useRender({
356
+ render,
357
+ defaultTagName: "button",
358
+ props: {
359
+ type: "button",
360
+ role: "tab",
361
+ id: tabId(value),
362
+ "aria-selected": selected,
363
+ "aria-controls": panelId(value),
364
+ tabIndex: value === tabStopValue ? 0 : -1,
365
+ // The platform keeps a disabled button out of focus and activation. Focusable when
366
+ // disabled, it is aria-disabled instead, and activation is blocked here.
367
+ disabled: disabled && !focusableWhenDisabled || void 0,
368
+ "aria-disabled": disabled && focusableWhenDisabled || void 0,
369
+ ...selectedDataAttributes(selected, disabled),
370
+ className,
371
+ children,
372
+ ref: elementRef,
373
+ onClick() {
374
+ if (!disabled) setValue(value);
375
+ },
376
+ onFocus() {
377
+ if (activationMode === "automatic" && !disabled) setValue(value);
378
+ }
379
+ },
380
+ // A native `disabled` would block your onClick; aria-disabled does not, so it is left off.
381
+ consumerProps: disabled ? rest : { ...rest, onClick }
382
+ });
383
+ }
384
+
385
+ // src/tabs/TabsPanel.tsx
386
+ function TabsPanel({ value, className, children, render, ...rest }) {
387
+ const { value: selectedValue, tabId, panelId } = useTabsRootContext("Panel");
388
+ const selected = value === selectedValue;
389
+ return useRender({
390
+ render,
391
+ defaultTagName: "div",
392
+ props: {
393
+ role: "tabpanel",
394
+ id: panelId(value),
395
+ "aria-labelledby": tabId(value),
396
+ tabIndex: 0,
397
+ hidden: !selected || void 0,
398
+ ...selectedDataAttributes(selected),
399
+ className,
400
+ children
401
+ },
402
+ consumerProps: rest
403
+ });
404
+ }
405
+ // Annotate the CommonJS export names for ESM import in node:
406
+ 0 && (module.exports = {
407
+ Tabs
408
+ });