@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.
- package/dist/chunk-2S4XG4VD.js +33 -0
- package/dist/menu/index.cjs +438 -0
- package/dist/menu/index.d.cts +115 -0
- package/dist/menu/index.d.ts +115 -0
- package/dist/menu/index.js +255 -0
- package/dist/tabs/index.cjs +408 -0
- package/dist/tabs/index.d.cts +127 -0
- package/dist/tabs/index.d.ts +127 -0
- package/dist/tabs/index.js +242 -0
- package/package.json +21 -1
|
@@ -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
|
+
});
|