@structyl/core 1.0.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/index.cjs ADDED
@@ -0,0 +1,1516 @@
1
+ 'use strict';
2
+
3
+ var React17 = require('react');
4
+ var ReactDOM = require('react-dom');
5
+ var react = require('@floating-ui/react');
6
+
7
+ function _interopNamespace(e) {
8
+ if (e && e.__esModule) return e;
9
+ var n = Object.create(null);
10
+ if (e) {
11
+ Object.keys(e).forEach(function (k) {
12
+ if (k !== 'default') {
13
+ var d = Object.getOwnPropertyDescriptor(e, k);
14
+ Object.defineProperty(n, k, d.get ? d : {
15
+ enumerable: true,
16
+ get: function () { return e[k]; }
17
+ });
18
+ }
19
+ });
20
+ }
21
+ n.default = e;
22
+ return Object.freeze(n);
23
+ }
24
+
25
+ var React17__namespace = /*#__PURE__*/_interopNamespace(React17);
26
+ var ReactDOM__namespace = /*#__PURE__*/_interopNamespace(ReactDOM);
27
+
28
+ var __defProp = Object.defineProperty;
29
+ var __export = (target, all) => {
30
+ for (var name in all)
31
+ __defProp(target, name, { get: all[name], enumerable: true });
32
+ };
33
+ var Slot = React17__namespace.forwardRef((props, forwardedRef) => {
34
+ const { children, ...slotProps } = props;
35
+ const childrenArray = React17__namespace.Children.toArray(children);
36
+ const slottable = childrenArray.find(isSlottable);
37
+ if (slottable) {
38
+ const newElement = slottable.props.children;
39
+ const newChildren = childrenArray.map((child) => {
40
+ if (child === slottable) {
41
+ if (React17__namespace.Children.count(newElement) > 1) return React17__namespace.Children.only(null);
42
+ return React17__namespace.isValidElement(newElement) ? newElement.props.children : null;
43
+ }
44
+ return child;
45
+ });
46
+ return /* @__PURE__ */ React17__namespace.createElement(SlotClone, { ...slotProps, ref: forwardedRef }, React17__namespace.isValidElement(newElement) ? React17__namespace.cloneElement(newElement, void 0, newChildren) : null);
47
+ }
48
+ const singleChild = childrenArray.length === 1 ? childrenArray[0] : children;
49
+ return /* @__PURE__ */ React17__namespace.createElement(SlotClone, { ...slotProps, ref: forwardedRef }, singleChild);
50
+ });
51
+ Slot.displayName = "Slot";
52
+ var SlotClone = React17__namespace.forwardRef((props, forwardedRef) => {
53
+ const { children, ...slotProps } = props;
54
+ if (React17__namespace.isValidElement(children)) {
55
+ const childRef = getElementRef(children);
56
+ const ref = forwardedRef ? composeRefs(forwardedRef, childRef) : childRef;
57
+ return React17__namespace.cloneElement(
58
+ children,
59
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
+ { ...mergeProps(slotProps, children.props), ref }
61
+ );
62
+ }
63
+ return React17__namespace.Children.count(children) > 1 ? React17__namespace.Children.only(null) : null;
64
+ });
65
+ SlotClone.displayName = "SlotClone";
66
+ var Slottable = ({ children }) => /* @__PURE__ */ React17__namespace.createElement(React17__namespace.Fragment, null, children);
67
+ function isSlottable(child) {
68
+ return React17__namespace.isValidElement(child) && child.type === Slottable;
69
+ }
70
+ function mergeProps(slotProps, childProps) {
71
+ const overrideProps = { ...childProps };
72
+ for (const propName in childProps) {
73
+ const slotPropValue = slotProps[propName];
74
+ const childPropValue = childProps[propName];
75
+ const isHandler = /^on[A-Z]/.test(propName);
76
+ if (isHandler) {
77
+ if (slotPropValue && childPropValue) {
78
+ overrideProps[propName] = (...args) => {
79
+ childPropValue(...args);
80
+ slotPropValue(...args);
81
+ };
82
+ } else if (slotPropValue) {
83
+ overrideProps[propName] = slotPropValue;
84
+ }
85
+ } else if (propName === "style") {
86
+ overrideProps[propName] = { ...slotPropValue, ...childPropValue };
87
+ } else if (propName === "className") {
88
+ overrideProps[propName] = [slotPropValue, childPropValue].filter(Boolean).join(" ");
89
+ }
90
+ }
91
+ return { ...slotProps, ...overrideProps };
92
+ }
93
+ function composeRefs(...refs) {
94
+ return (node) => refs.forEach((ref) => {
95
+ if (typeof ref === "function") ref(node);
96
+ else if (ref != null) ref.current = node;
97
+ });
98
+ }
99
+ function getElementRef(element) {
100
+ let getter = Object.getOwnPropertyDescriptor(element.props, "ref")?.get;
101
+ let mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
102
+ if (mayWarn) {
103
+ return element.ref;
104
+ }
105
+ getter = Object.getOwnPropertyDescriptor(element, "ref")?.get;
106
+ mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
107
+ if (mayWarn) {
108
+ return element.props.ref;
109
+ }
110
+ return element.props.ref || element.ref;
111
+ }
112
+
113
+ // src/primitive.tsx
114
+ var NODES = [
115
+ "a",
116
+ "button",
117
+ "div",
118
+ "form",
119
+ "h2",
120
+ "h3",
121
+ "img",
122
+ "input",
123
+ "label",
124
+ "li",
125
+ "nav",
126
+ "ol",
127
+ "p",
128
+ "span",
129
+ "svg",
130
+ "ul"
131
+ ];
132
+ var Primitive = NODES.reduce((primitive, node) => {
133
+ const Node = React17__namespace.forwardRef(
134
+ (props, forwardedRef) => {
135
+ const { asChild, ...rest } = props;
136
+ const Comp = asChild ? Slot : node;
137
+ return /* @__PURE__ */ React17__namespace.createElement(Comp, { ...rest, ref: forwardedRef });
138
+ }
139
+ );
140
+ Node.displayName = `Primitive.${node}`;
141
+ return { ...primitive, [node]: Node };
142
+ }, {});
143
+ function createContext2(rootComponentName, defaultContext) {
144
+ const Context = React17__namespace.createContext(defaultContext);
145
+ function Provider(props) {
146
+ const { children, ...context } = props;
147
+ const value = React17__namespace.useMemo(() => context, Object.values(context));
148
+ return /* @__PURE__ */ React17__namespace.createElement(Context.Provider, { value }, children);
149
+ }
150
+ Provider.displayName = `${rootComponentName}Provider`;
151
+ function useContextHook(consumerName) {
152
+ const ctx = React17__namespace.useContext(Context);
153
+ if (ctx !== void 0) return ctx;
154
+ if (defaultContext !== void 0) return defaultContext;
155
+ throw new Error(`\`${consumerName}\` must be used within \`${rootComponentName}\``);
156
+ }
157
+ return [Provider, useContextHook];
158
+ }
159
+ function createContextScope(scopeName, createContextScopeDeps = []) {
160
+ let defaultContexts = [];
161
+ function createScopedContext(rootComponentName, defaultContext) {
162
+ const BaseContext = React17__namespace.createContext(defaultContext);
163
+ const index = defaultContexts.push(BaseContext) - 1;
164
+ function Provider(props) {
165
+ const { scope, children, ...context } = props;
166
+ const Context = scope?.[scopeName]?.[index] ?? BaseContext;
167
+ const value = React17__namespace.useMemo(() => context, Object.values(context));
168
+ return /* @__PURE__ */ React17__namespace.createElement(Context.Provider, { value }, children);
169
+ }
170
+ Provider.displayName = `${rootComponentName}Provider`;
171
+ function useScopedContext(consumerName, scope) {
172
+ const Context = scope?.[scopeName]?.[index] ?? BaseContext;
173
+ const ctx = React17__namespace.useContext(Context);
174
+ if (ctx !== void 0) return ctx;
175
+ if (defaultContext !== void 0) return defaultContext;
176
+ throw new Error(`\`${consumerName}\` must be used within \`${rootComponentName}\``);
177
+ }
178
+ return [Provider, useScopedContext];
179
+ }
180
+ const createScope = () => {
181
+ const scopeContexts = defaultContexts.map(
182
+ () => React17__namespace.createContext(void 0)
183
+ );
184
+ return function useScopeHook(parentScope) {
185
+ return {
186
+ [`__scope${scopeName}`]: { ...parentScope, [scopeName]: scopeContexts }
187
+ };
188
+ };
189
+ };
190
+ createScope.scopeName = scopeName;
191
+ return [
192
+ createScopedContext,
193
+ composeContextScopes(createScope, ...createContextScopeDeps)
194
+ ];
195
+ }
196
+ function composeContextScopes(...scopes) {
197
+ const composed = () => {
198
+ const scopeHooks = scopes.map((scope) => scope());
199
+ return function useComposedScopeHook(parentScope) {
200
+ return scopeHooks.reduce(
201
+ (nextScope, hook) => ({ ...nextScope, ...hook(parentScope) }),
202
+ {}
203
+ );
204
+ };
205
+ };
206
+ composed.scopeName = scopes.map((s) => s.scopeName).join(",");
207
+ return composed;
208
+ }
209
+ var Portal = React17__namespace.forwardRef(
210
+ (props, forwardedRef) => {
211
+ const { container: containerProp, ...portalProps } = props;
212
+ const [mounted, setMounted] = React17__namespace.useState(false);
213
+ React17__namespace.useEffect(() => setMounted(true), []);
214
+ const container = containerProp ?? (typeof document !== "undefined" ? document.body : null);
215
+ if (!mounted || !container) return null;
216
+ return ReactDOM__namespace.createPortal(
217
+ /* @__PURE__ */ React17__namespace.createElement(Primitive.div, { ...portalProps, ref: forwardedRef }),
218
+ container
219
+ );
220
+ }
221
+ );
222
+ Portal.displayName = "Portal";
223
+ var VISUALLY_HIDDEN_STYLES = {
224
+ position: "absolute",
225
+ border: 0,
226
+ width: 1,
227
+ height: 1,
228
+ padding: 0,
229
+ margin: -1,
230
+ overflow: "hidden",
231
+ clip: "rect(0, 0, 0, 0)",
232
+ whiteSpace: "nowrap",
233
+ wordWrap: "normal"
234
+ };
235
+ var VisuallyHidden = React17__namespace.forwardRef(
236
+ (props, forwardedRef) => /* @__PURE__ */ React17__namespace.createElement(
237
+ Primitive.span,
238
+ {
239
+ ...props,
240
+ ref: forwardedRef,
241
+ style: { ...VISUALLY_HIDDEN_STYLES, ...props.style }
242
+ }
243
+ )
244
+ );
245
+ VisuallyHidden.displayName = "VisuallyHidden";
246
+ var DirectionContext = React17__namespace.createContext(void 0);
247
+ var DirectionProvider = ({ dir, children }) => /* @__PURE__ */ React17__namespace.createElement(DirectionContext.Provider, { value: dir }, children);
248
+ function useDirection(localDir) {
249
+ const globalDir = React17__namespace.useContext(DirectionContext);
250
+ return localDir ?? globalDir ?? "ltr";
251
+ }
252
+ function setRef(ref, value) {
253
+ if (typeof ref === "function") ref(value);
254
+ else if (ref != null && typeof ref === "object")
255
+ ref.current = value;
256
+ }
257
+ function composeRefs2(...refs) {
258
+ return (node) => refs.forEach((ref) => setRef(ref, node));
259
+ }
260
+ function useComposedRefs(...refs) {
261
+ return React17__namespace.useCallback(composeRefs2(...refs), refs);
262
+ }
263
+ function useCallbackRef(callback) {
264
+ const callbackRef = React17__namespace.useRef(callback);
265
+ React17__namespace.useEffect(() => {
266
+ callbackRef.current = callback;
267
+ });
268
+ return React17__namespace.useMemo(
269
+ () => ((...args) => callbackRef.current?.(...args)),
270
+ []
271
+ );
272
+ }
273
+ function useControllableState({
274
+ prop,
275
+ defaultProp,
276
+ onChange = () => {
277
+ }
278
+ }) {
279
+ const [uncontrolled, setUncontrolled] = React17__namespace.useState(defaultProp);
280
+ const isControlled = prop !== void 0;
281
+ const value = isControlled ? prop : uncontrolled;
282
+ const handleChange = useCallbackRef(onChange);
283
+ const setValue = React17__namespace.useCallback(
284
+ (nextValue) => {
285
+ if (isControlled) {
286
+ const setter = nextValue;
287
+ const newValue = typeof setter === "function" ? setter(prop) : setter;
288
+ if (newValue !== prop) handleChange(newValue);
289
+ } else {
290
+ setUncontrolled((prev) => {
291
+ const setter = nextValue;
292
+ const newValue = typeof setter === "function" ? setter(prev) : setter;
293
+ if (newValue !== prev) handleChange(newValue);
294
+ return newValue;
295
+ });
296
+ }
297
+ },
298
+ [isControlled, prop, handleChange]
299
+ );
300
+ return [value, setValue];
301
+ }
302
+ var idCounter = 0;
303
+ function useReactId() {
304
+ return React17__namespace.useId?.() ?? "";
305
+ }
306
+ function useId2(prefix) {
307
+ const reactId = useReactId();
308
+ const [id, setId] = React17__namespace.useState(reactId);
309
+ React17__namespace.useEffect(() => {
310
+ if (!reactId) setId(`yl-${++idCounter}`);
311
+ }, [reactId]);
312
+ return id || reactId;
313
+ }
314
+ function composeEventHandlers(originalEventHandler, ourEventHandler, { checkForDefaultPrevented = true } = {}) {
315
+ return function handleEvent(event) {
316
+ originalEventHandler?.(event);
317
+ if (checkForDefaultPrevented === false || !event.defaultPrevented) {
318
+ ourEventHandler?.(event);
319
+ }
320
+ };
321
+ }
322
+
323
+ // src/presence.tsx
324
+ var machine = {
325
+ mounted: {
326
+ UNMOUNT: "unmounted",
327
+ ANIMATION_OUT: "unmountSuspended"
328
+ },
329
+ unmountSuspended: {
330
+ MOUNT: "mounted",
331
+ ANIMATION_END: "unmounted"
332
+ },
333
+ unmounted: {
334
+ MOUNT: "mounted"
335
+ }
336
+ };
337
+ function usePresence(present) {
338
+ const [node, setNode] = React17__namespace.useState(null);
339
+ const stylesRef = React17__namespace.useRef(null);
340
+ const prevPresentRef = React17__namespace.useRef(present);
341
+ const prevAnimationNameRef = React17__namespace.useRef("none");
342
+ const initialState = present ? "mounted" : "unmounted";
343
+ const [state, setState] = React17__namespace.useState(initialState);
344
+ const send = React17__namespace.useCallback((event) => {
345
+ setState((prev) => {
346
+ const next = machine[prev][event];
347
+ return next ?? prev;
348
+ });
349
+ }, []);
350
+ React17__namespace.useEffect(() => {
351
+ const currentAnimationName = getAnimationName(stylesRef.current);
352
+ prevAnimationNameRef.current = state === "mounted" ? currentAnimationName : "none";
353
+ }, [state]);
354
+ React17__namespace.useEffect(() => {
355
+ const styles = stylesRef.current;
356
+ const wasPresent = prevPresentRef.current;
357
+ const hasPresentChanged = wasPresent !== present;
358
+ if (hasPresentChanged) {
359
+ const prevAnimationName = prevAnimationNameRef.current;
360
+ const currentAnimationName = getAnimationName(styles);
361
+ if (present) {
362
+ send("MOUNT");
363
+ } else if (currentAnimationName === "none" || styles?.display === "none") {
364
+ send("UNMOUNT");
365
+ } else {
366
+ const isAnimating = prevAnimationName !== currentAnimationName;
367
+ if (wasPresent && isAnimating) {
368
+ send("ANIMATION_OUT");
369
+ } else {
370
+ send("UNMOUNT");
371
+ }
372
+ }
373
+ prevPresentRef.current = present;
374
+ }
375
+ }, [present, send]);
376
+ React17__namespace.useEffect(() => {
377
+ if (!node) {
378
+ send("ANIMATION_END");
379
+ return;
380
+ }
381
+ const handleAnimationEnd = (event) => {
382
+ const currentAnimationName = getAnimationName(stylesRef.current);
383
+ const animationName = "animationName" in event ? event.animationName : "";
384
+ const isCurrent = currentAnimationName.includes(animationName) || animationName === "";
385
+ if (event.target === node && isCurrent) {
386
+ send("ANIMATION_END");
387
+ }
388
+ };
389
+ const handleAnimationStart = (event) => {
390
+ if (event.target === node) {
391
+ prevAnimationNameRef.current = getAnimationName(stylesRef.current);
392
+ }
393
+ };
394
+ node.addEventListener("animationstart", handleAnimationStart);
395
+ node.addEventListener("animationcancel", handleAnimationEnd);
396
+ node.addEventListener("animationend", handleAnimationEnd);
397
+ node.addEventListener("transitioncancel", handleAnimationEnd);
398
+ node.addEventListener("transitionend", handleAnimationEnd);
399
+ return () => {
400
+ node.removeEventListener("animationstart", handleAnimationStart);
401
+ node.removeEventListener("animationcancel", handleAnimationEnd);
402
+ node.removeEventListener("animationend", handleAnimationEnd);
403
+ node.removeEventListener("transitioncancel", handleAnimationEnd);
404
+ node.removeEventListener("transitionend", handleAnimationEnd);
405
+ };
406
+ }, [node, send]);
407
+ return {
408
+ isPresent: state !== "unmounted",
409
+ ref: React17__namespace.useCallback((el) => {
410
+ stylesRef.current = el ? getComputedStyle(el) : null;
411
+ setNode(el);
412
+ }, [])
413
+ };
414
+ }
415
+ function getAnimationName(styles) {
416
+ return styles?.animationName || "none";
417
+ }
418
+ var Presence = (props) => {
419
+ const { present, children, forceMount } = props;
420
+ const presence = usePresence(present);
421
+ const isFunctionChild = typeof children === "function";
422
+ const child = isFunctionChild ? children({ present: presence.isPresent }) : React17__namespace.Children.only(children);
423
+ const childRef = getElementRef2(child);
424
+ const ref = useComposedRefs(presence.ref, childRef);
425
+ const shouldRender = forceMount || isFunctionChild || presence.isPresent;
426
+ if (!shouldRender) return null;
427
+ return React17__namespace.cloneElement(child, { ref });
428
+ };
429
+ Presence.displayName = "Presence";
430
+ function getElementRef2(element) {
431
+ const props = element.props;
432
+ const ref19 = props?.ref;
433
+ const ref18 = element.ref;
434
+ return ref19 ?? ref18;
435
+ }
436
+ var AUTOFOCUS_ON_MOUNT = "focusScope.autoFocusOnMount";
437
+ var AUTOFOCUS_ON_UNMOUNT = "focusScope.autoFocusOnUnmount";
438
+ var EVENT_OPTIONS = { bubbles: false, cancelable: true };
439
+ var FocusScope = React17__namespace.forwardRef(
440
+ (props, forwardedRef) => {
441
+ const {
442
+ loop = false,
443
+ trapped = false,
444
+ onMountAutoFocus: onMountAutoFocusProp,
445
+ onUnmountAutoFocus: onUnmountAutoFocusProp,
446
+ ...scopeProps
447
+ } = props;
448
+ const [container, setContainer] = React17__namespace.useState(null);
449
+ const onMountAutoFocus = useCallbackRef(onMountAutoFocusProp);
450
+ const onUnmountAutoFocus = useCallbackRef(onUnmountAutoFocusProp);
451
+ const lastFocusedElementRef = React17__namespace.useRef(null);
452
+ const composedRefs = useComposedRefs(forwardedRef, setContainer);
453
+ const scope = React17__namespace.useRef({
454
+ paused: false,
455
+ pause() {
456
+ this.paused = true;
457
+ },
458
+ resume() {
459
+ this.paused = false;
460
+ }
461
+ }).current;
462
+ React17__namespace.useEffect(() => {
463
+ if (!trapped) return;
464
+ const handleFocusIn = (event) => {
465
+ if (focusScopesStack.active !== scope) return;
466
+ const target = event.target;
467
+ if (container?.contains(target)) {
468
+ lastFocusedElementRef.current = target;
469
+ } else {
470
+ focus(lastFocusedElementRef.current, { select: true });
471
+ }
472
+ };
473
+ const handleFocusOut = (event) => {
474
+ if (focusScopesStack.active !== scope) return;
475
+ const relatedTarget = event.relatedTarget;
476
+ if (relatedTarget === null) return;
477
+ if (!container?.contains(relatedTarget)) {
478
+ focus(lastFocusedElementRef.current, { select: true });
479
+ }
480
+ };
481
+ const handleMutations = (mutations) => {
482
+ const focusedEl = document.activeElement;
483
+ if (focusedEl !== document.body) return;
484
+ for (const mutation of mutations) {
485
+ if (mutation.removedNodes.length > 0) {
486
+ focus(container, { select: true });
487
+ }
488
+ }
489
+ };
490
+ document.addEventListener("focusin", handleFocusIn);
491
+ document.addEventListener("focusout", handleFocusOut);
492
+ const mutationObserver = new MutationObserver(handleMutations);
493
+ if (container) {
494
+ mutationObserver.observe(container, { childList: true, subtree: true });
495
+ }
496
+ return () => {
497
+ document.removeEventListener("focusin", handleFocusIn);
498
+ document.removeEventListener("focusout", handleFocusOut);
499
+ mutationObserver.disconnect();
500
+ };
501
+ }, [trapped, container, scope]);
502
+ React17__namespace.useEffect(() => {
503
+ if (!container) return;
504
+ focusScopesStack.add(scope);
505
+ const previouslyFocusedElement = document.activeElement;
506
+ const hasFocusedCandidate = container.contains(previouslyFocusedElement);
507
+ if (!hasFocusedCandidate) {
508
+ const mountEvent = new CustomEvent(AUTOFOCUS_ON_MOUNT, EVENT_OPTIONS);
509
+ container.addEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus);
510
+ container.dispatchEvent(mountEvent);
511
+ if (!mountEvent.defaultPrevented) {
512
+ focusFirst(removeLinks(getTabbableCandidates(container)), { select: true });
513
+ if (document.activeElement === previouslyFocusedElement) {
514
+ focus(container);
515
+ }
516
+ }
517
+ }
518
+ return () => {
519
+ container.removeEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus);
520
+ setTimeout(() => {
521
+ const unmountEvent = new CustomEvent(AUTOFOCUS_ON_UNMOUNT, EVENT_OPTIONS);
522
+ container.addEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus);
523
+ container.dispatchEvent(unmountEvent);
524
+ if (!unmountEvent.defaultPrevented) {
525
+ focus(previouslyFocusedElement ?? document.body, { select: true });
526
+ }
527
+ container.removeEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus);
528
+ focusScopesStack.remove(scope);
529
+ }, 0);
530
+ };
531
+ }, [container, onMountAutoFocus, onUnmountAutoFocus, scope]);
532
+ const handleKeyDown = React17__namespace.useCallback(
533
+ (event) => {
534
+ if (!loop && !trapped) return;
535
+ if (scope.paused) return;
536
+ const isTabKey = event.key === "Tab" && !event.altKey && !event.ctrlKey && !event.metaKey;
537
+ const focusedElement = document.activeElement;
538
+ if (!isTabKey || !focusedElement) return;
539
+ const containerEl = event.currentTarget;
540
+ const [first, last] = getTabbableEdges(containerEl);
541
+ const hasTabbable = first && last;
542
+ if (!hasTabbable) {
543
+ if (focusedElement === containerEl) event.preventDefault();
544
+ } else {
545
+ if (!event.shiftKey && focusedElement === last) {
546
+ event.preventDefault();
547
+ if (loop) focus(first, { select: true });
548
+ } else if (event.shiftKey && focusedElement === first) {
549
+ event.preventDefault();
550
+ if (loop) focus(last, { select: true });
551
+ }
552
+ }
553
+ },
554
+ [loop, trapped, scope.paused]
555
+ );
556
+ return /* @__PURE__ */ React17__namespace.createElement(
557
+ Primitive.div,
558
+ {
559
+ tabIndex: -1,
560
+ ...scopeProps,
561
+ ref: composedRefs,
562
+ onKeyDown: handleKeyDown
563
+ }
564
+ );
565
+ }
566
+ );
567
+ FocusScope.displayName = "FocusScope";
568
+ function focus(element, { select = false } = {}) {
569
+ if (element && element.focus) {
570
+ const previouslyFocusedElement = document.activeElement;
571
+ element.focus({ preventScroll: true });
572
+ if (element !== previouslyFocusedElement && isSelectableInput(element) && select) {
573
+ element.select();
574
+ }
575
+ }
576
+ }
577
+ function focusFirst(candidates, { select = false } = {}) {
578
+ const previouslyFocusedElement = document.activeElement;
579
+ for (const candidate of candidates) {
580
+ focus(candidate, { select });
581
+ if (document.activeElement !== previouslyFocusedElement) return;
582
+ }
583
+ }
584
+ function getTabbableEdges(container) {
585
+ const candidates = getTabbableCandidates(container);
586
+ const first = findVisible(candidates, container);
587
+ const last = findVisible(candidates.reverse(), container);
588
+ return [first, last];
589
+ }
590
+ function getTabbableCandidates(container) {
591
+ const nodes = [];
592
+ const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {
593
+ acceptNode: (node) => {
594
+ const el = node;
595
+ const isHiddenInput = el.tagName === "INPUT" && el.type === "hidden";
596
+ if (el.hasAttribute("disabled") || el.hasAttribute("hidden") || isHiddenInput) {
597
+ return NodeFilter.FILTER_SKIP;
598
+ }
599
+ return el.tabIndex >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
600
+ }
601
+ });
602
+ while (walker.nextNode()) nodes.push(walker.currentNode);
603
+ return nodes;
604
+ }
605
+ function findVisible(elements, container) {
606
+ for (const element of elements) {
607
+ if (!isHidden(element, { upTo: container })) return element;
608
+ }
609
+ return void 0;
610
+ }
611
+ function isHidden(node, { upTo } = {}) {
612
+ if (getComputedStyle(node).visibility === "hidden") return true;
613
+ let current = node;
614
+ while (current) {
615
+ if (upTo !== void 0 && current === upTo) return false;
616
+ if (getComputedStyle(current).display === "none") return true;
617
+ current = current.parentElement;
618
+ }
619
+ return false;
620
+ }
621
+ function isSelectableInput(element) {
622
+ return element instanceof HTMLInputElement && "select" in element;
623
+ }
624
+ function removeLinks(items) {
625
+ return items.filter((item) => item.tagName !== "A");
626
+ }
627
+ var focusScopesStack = createFocusScopesStack();
628
+ function createFocusScopesStack() {
629
+ let stack = [];
630
+ return {
631
+ add(focusScope) {
632
+ const active = stack[0];
633
+ if (focusScope !== active) active?.pause();
634
+ stack = arrayRemove(stack, focusScope);
635
+ stack.unshift(focusScope);
636
+ },
637
+ remove(focusScope) {
638
+ stack = arrayRemove(stack, focusScope);
639
+ stack[0]?.resume();
640
+ },
641
+ get active() {
642
+ return stack[0];
643
+ }
644
+ };
645
+ }
646
+ function arrayRemove(arr, item) {
647
+ const newArr = [...arr];
648
+ const idx = newArr.indexOf(item);
649
+ if (idx !== -1) newArr.splice(idx, 1);
650
+ return newArr;
651
+ }
652
+ var count = 0;
653
+ function useFocusGuards() {
654
+ React17__namespace.useEffect(() => {
655
+ if (typeof document === "undefined") return;
656
+ const edgeGuards = document.querySelectorAll("[data-structyl-focus-guard]");
657
+ document.body.insertAdjacentElement("afterbegin", edgeGuards[0] ?? createFocusGuard());
658
+ document.body.insertAdjacentElement("beforeend", edgeGuards[1] ?? createFocusGuard());
659
+ count++;
660
+ return () => {
661
+ if (count === 1) {
662
+ document.querySelectorAll("[data-structyl-focus-guard]").forEach((node) => node.remove());
663
+ }
664
+ count--;
665
+ };
666
+ }, []);
667
+ }
668
+ function createFocusGuard() {
669
+ const element = document.createElement("span");
670
+ element.setAttribute("data-structyl-focus-guard", "");
671
+ element.tabIndex = 0;
672
+ element.style.cssText = "outline: none; opacity: 0; position: fixed; pointer-events: none";
673
+ return element;
674
+ }
675
+ var FocusGuards = ({ children }) => {
676
+ useFocusGuards();
677
+ return /* @__PURE__ */ React17__namespace.createElement(React17__namespace.Fragment, null, children);
678
+ };
679
+ FocusGuards.displayName = "FocusGuards";
680
+ var CONTEXT_UPDATE = "dismissableLayer.update";
681
+ var POINTER_DOWN_OUTSIDE = "dismissableLayer.pointerDownOutside";
682
+ var FOCUS_OUTSIDE = "dismissableLayer.focusOutside";
683
+ var originalBodyPointerEvents;
684
+ var DismissableLayerContext = React17__namespace.createContext({
685
+ layers: /* @__PURE__ */ new Set(),
686
+ layersWithOutsidePointerEventsDisabled: /* @__PURE__ */ new Set(),
687
+ branches: /* @__PURE__ */ new Set()
688
+ });
689
+ var DismissableLayer = React17__namespace.forwardRef(
690
+ (props, forwardedRef) => {
691
+ const {
692
+ disableOutsidePointerEvents = false,
693
+ onEscapeKeyDown,
694
+ onPointerDownOutside,
695
+ onFocusOutside,
696
+ onInteractOutside,
697
+ onDismiss,
698
+ ...layerProps
699
+ } = props;
700
+ const context = React17__namespace.useContext(DismissableLayerContext);
701
+ const [node, setNode] = React17__namespace.useState(null);
702
+ const [, forceUpdate] = React17__namespace.useState({});
703
+ const ownerDocument = node?.ownerDocument ?? (typeof document !== "undefined" ? document : null);
704
+ const layers = Array.from(context.layers);
705
+ const [highestLayerWithOutsidePointerEventsDisabled] = [...context.layersWithOutsidePointerEventsDisabled].slice(-1);
706
+ const highestLayerWithOutsidePointerEventsDisabledIndex = highestLayerWithOutsidePointerEventsDisabled ? layers.indexOf(highestLayerWithOutsidePointerEventsDisabled) : -1;
707
+ const index = node ? layers.indexOf(node) : -1;
708
+ const isBodyPointerEventsDisabled = context.layersWithOutsidePointerEventsDisabled.size > 0;
709
+ const isPointerEventsEnabled = index >= highestLayerWithOutsidePointerEventsDisabledIndex;
710
+ const pointerDownOutside = usePointerDownOutside((event) => {
711
+ const target = event.target;
712
+ const isPointerDownOnBranch = [...context.branches].some(
713
+ (branch) => branch.contains(target)
714
+ );
715
+ if (!isPointerEventsEnabled || isPointerDownOnBranch) return;
716
+ onPointerDownOutside?.(event);
717
+ onInteractOutside?.(event);
718
+ if (!event.defaultPrevented) onDismiss?.();
719
+ }, ownerDocument);
720
+ const focusOutside = useFocusOutside((event) => {
721
+ const target = event.target;
722
+ const isFocusOnBranch = [...context.branches].some(
723
+ (branch) => branch.contains(target)
724
+ );
725
+ if (isFocusOnBranch) return;
726
+ onFocusOutside?.(event);
727
+ onInteractOutside?.(event);
728
+ if (!event.defaultPrevented) onDismiss?.();
729
+ }, ownerDocument);
730
+ useEscapeKeydown((event) => {
731
+ const layers2 = Array.from(context.layers);
732
+ const index2 = node ? layers2.indexOf(node) : -1;
733
+ const isHighest = node != null && index2 === layers2.length - 1;
734
+ if (!isHighest) return;
735
+ onEscapeKeyDown?.(event);
736
+ if (!event.defaultPrevented && onDismiss) {
737
+ event.preventDefault();
738
+ onDismiss();
739
+ }
740
+ }, ownerDocument);
741
+ React17__namespace.useLayoutEffect(() => {
742
+ if (!node) return;
743
+ if (disableOutsidePointerEvents) {
744
+ if (context.layersWithOutsidePointerEventsDisabled.size === 0 && ownerDocument) {
745
+ originalBodyPointerEvents = ownerDocument.body.style.pointerEvents;
746
+ ownerDocument.body.style.pointerEvents = "none";
747
+ }
748
+ context.layersWithOutsidePointerEventsDisabled.add(node);
749
+ }
750
+ context.layers.add(node);
751
+ updatePointerEventsOnNode(node, context);
752
+ dispatchUpdate();
753
+ forceUpdate({});
754
+ return () => {
755
+ if (disableOutsidePointerEvents && context.layersWithOutsidePointerEventsDisabled.size === 1 && ownerDocument) {
756
+ ownerDocument.body.style.pointerEvents = originalBodyPointerEvents;
757
+ }
758
+ };
759
+ }, [node, ownerDocument, disableOutsidePointerEvents, context]);
760
+ React17__namespace.useLayoutEffect(() => {
761
+ return () => {
762
+ if (!node) return;
763
+ context.layers.delete(node);
764
+ context.layersWithOutsidePointerEventsDisabled.delete(node);
765
+ dispatchUpdate();
766
+ };
767
+ }, [node, context]);
768
+ React17__namespace.useEffect(() => {
769
+ const handleUpdate = () => {
770
+ if (node) updatePointerEventsOnNode(node, context);
771
+ forceUpdate({});
772
+ };
773
+ document.addEventListener(CONTEXT_UPDATE, handleUpdate);
774
+ return () => document.removeEventListener(CONTEXT_UPDATE, handleUpdate);
775
+ }, [node, context]);
776
+ const composedRefs = useComposedRefs(forwardedRef, setNode);
777
+ return /* @__PURE__ */ React17__namespace.createElement(
778
+ Primitive.div,
779
+ {
780
+ ...layerProps,
781
+ ref: composedRefs,
782
+ style: {
783
+ pointerEvents: isBodyPointerEventsDisabled ? isPointerEventsEnabled ? "auto" : "none" : void 0,
784
+ ...layerProps.style
785
+ },
786
+ onFocusCapture: composeEventHandlers(layerProps.onFocusCapture, focusOutside.onFocusCapture),
787
+ onBlurCapture: composeEventHandlers(layerProps.onBlurCapture, focusOutside.onBlurCapture),
788
+ onPointerDownCapture: composeEventHandlers(
789
+ layerProps.onPointerDownCapture,
790
+ pointerDownOutside.onPointerDownCapture
791
+ )
792
+ }
793
+ );
794
+ }
795
+ );
796
+ DismissableLayer.displayName = "DismissableLayer";
797
+ var DismissableLayerBranch = React17__namespace.forwardRef(
798
+ (props, forwardedRef) => {
799
+ const context = React17__namespace.useContext(DismissableLayerContext);
800
+ const ref = React17__namespace.useRef(null);
801
+ const composedRefs = useComposedRefs(forwardedRef, ref);
802
+ React17__namespace.useEffect(() => {
803
+ const node = ref.current;
804
+ if (!node) return;
805
+ context.branches.add(node);
806
+ return () => {
807
+ context.branches.delete(node);
808
+ };
809
+ }, [context]);
810
+ return /* @__PURE__ */ React17__namespace.createElement(Primitive.div, { ...props, ref: composedRefs });
811
+ }
812
+ );
813
+ DismissableLayerBranch.displayName = "DismissableLayerBranch";
814
+ function updatePointerEventsOnNode(node, context) {
815
+ const layers = Array.from(context.layers);
816
+ const [highest] = [...context.layersWithOutsidePointerEventsDisabled].slice(-1);
817
+ const highestIndex = highest ? layers.indexOf(highest) : -1;
818
+ const nodeIndex = layers.indexOf(node);
819
+ const isBodyDisabled = context.layersWithOutsidePointerEventsDisabled.size > 0;
820
+ const isEnabled = nodeIndex >= highestIndex;
821
+ if (isBodyDisabled) {
822
+ node.style.pointerEvents = isEnabled ? "auto" : "none";
823
+ } else {
824
+ node.style.removeProperty("pointer-events");
825
+ }
826
+ }
827
+ function dispatchUpdate() {
828
+ if (typeof document === "undefined") return;
829
+ const event = new CustomEvent(CONTEXT_UPDATE);
830
+ document.dispatchEvent(event);
831
+ }
832
+ function usePointerDownOutside(onPointerDownOutside, ownerDocument = typeof document !== "undefined" ? document : null) {
833
+ const handler = useCallbackRef(onPointerDownOutside);
834
+ const isPointerInsideReactTreeRef = React17__namespace.useRef(false);
835
+ const handleClickRef = React17__namespace.useRef(() => {
836
+ });
837
+ React17__namespace.useEffect(() => {
838
+ if (!ownerDocument) return;
839
+ const handlePointerDown = (event) => {
840
+ if (event.target && !isPointerInsideReactTreeRef.current) {
841
+ const eventDetail = { originalEvent: event };
842
+ const handleAndDispatchPointerDownOutsideEvent = () => {
843
+ const customEvent = new CustomEvent(POINTER_DOWN_OUTSIDE, {
844
+ bubbles: false,
845
+ cancelable: true,
846
+ detail: eventDetail
847
+ });
848
+ const targetEl = event.target;
849
+ targetEl.addEventListener(POINTER_DOWN_OUTSIDE, handler, { once: true });
850
+ targetEl.dispatchEvent(customEvent);
851
+ };
852
+ if (event.pointerType === "touch") {
853
+ ownerDocument.removeEventListener("click", handleClickRef.current);
854
+ handleClickRef.current = handleAndDispatchPointerDownOutsideEvent;
855
+ ownerDocument.addEventListener("click", handleClickRef.current, { once: true });
856
+ } else {
857
+ handleAndDispatchPointerDownOutsideEvent();
858
+ }
859
+ } else {
860
+ ownerDocument.removeEventListener("click", handleClickRef.current);
861
+ }
862
+ isPointerInsideReactTreeRef.current = false;
863
+ };
864
+ const timerId = window.setTimeout(() => {
865
+ ownerDocument.addEventListener("pointerdown", handlePointerDown);
866
+ }, 0);
867
+ return () => {
868
+ window.clearTimeout(timerId);
869
+ ownerDocument.removeEventListener("pointerdown", handlePointerDown);
870
+ ownerDocument.removeEventListener("click", handleClickRef.current);
871
+ };
872
+ }, [ownerDocument, handler]);
873
+ return {
874
+ onPointerDownCapture: () => {
875
+ isPointerInsideReactTreeRef.current = true;
876
+ }
877
+ };
878
+ }
879
+ function useFocusOutside(onFocusOutside, ownerDocument = typeof document !== "undefined" ? document : null) {
880
+ const handler = useCallbackRef(onFocusOutside);
881
+ const isFocusInsideReactTreeRef = React17__namespace.useRef(false);
882
+ React17__namespace.useEffect(() => {
883
+ if (!ownerDocument) return;
884
+ const handleFocus = (event) => {
885
+ if (event.target && !isFocusInsideReactTreeRef.current) {
886
+ const eventDetail = { originalEvent: event };
887
+ const customEvent = new CustomEvent(FOCUS_OUTSIDE, {
888
+ bubbles: false,
889
+ cancelable: true,
890
+ detail: eventDetail
891
+ });
892
+ const targetEl = event.target;
893
+ targetEl.addEventListener(FOCUS_OUTSIDE, handler, { once: true });
894
+ targetEl.dispatchEvent(customEvent);
895
+ }
896
+ };
897
+ ownerDocument.addEventListener("focusin", handleFocus);
898
+ return () => ownerDocument.removeEventListener("focusin", handleFocus);
899
+ }, [ownerDocument, handler]);
900
+ return {
901
+ onFocusCapture: () => {
902
+ isFocusInsideReactTreeRef.current = true;
903
+ },
904
+ onBlurCapture: () => {
905
+ isFocusInsideReactTreeRef.current = false;
906
+ }
907
+ };
908
+ }
909
+ function useEscapeKeydown(onEscapeKeyDown, ownerDocument = typeof document !== "undefined" ? document : null) {
910
+ const handler = useCallbackRef(onEscapeKeyDown);
911
+ React17__namespace.useEffect(() => {
912
+ if (!ownerDocument) return;
913
+ const handleKeyDown = (event) => {
914
+ if (event.key === "Escape") handler(event);
915
+ };
916
+ ownerDocument.addEventListener("keydown", handleKeyDown, { capture: true });
917
+ return () => ownerDocument.removeEventListener("keydown", handleKeyDown, { capture: true });
918
+ }, [handler, ownerDocument]);
919
+ }
920
+ function createCollection(name) {
921
+ const CollectionContext = React17__namespace.createContext({
922
+ collectionRef: { current: null },
923
+ itemMap: /* @__PURE__ */ new Map()
924
+ });
925
+ const CollectionProvider = ({ children }) => {
926
+ const collectionRef = React17__namespace.useRef(null);
927
+ const itemMap = React17__namespace.useRef(/* @__PURE__ */ new Map()).current;
928
+ const value = React17__namespace.useMemo(() => ({ collectionRef, itemMap }), [itemMap]);
929
+ return /* @__PURE__ */ React17__namespace.createElement(CollectionContext.Provider, { value }, children);
930
+ };
931
+ CollectionProvider.displayName = `${name}CollectionProvider`;
932
+ const CollectionSlot = React17__namespace.forwardRef(
933
+ ({ children }, forwardedRef) => {
934
+ const context = React17__namespace.useContext(CollectionContext);
935
+ const composedRefs = useComposedRefs(forwardedRef, context.collectionRef);
936
+ return /* @__PURE__ */ React17__namespace.createElement(Slot, { ref: composedRefs }, children);
937
+ }
938
+ );
939
+ CollectionSlot.displayName = `${name}CollectionSlot`;
940
+ const ITEM_DATA_ATTR = `data-${name.toLowerCase()}-collection-item`;
941
+ const CollectionItemSlot = React17__namespace.forwardRef(
942
+ (props, forwardedRef) => {
943
+ const { children, ...itemData } = props;
944
+ const ref = React17__namespace.useRef(null);
945
+ const composedRefs = useComposedRefs(forwardedRef, ref);
946
+ const context = React17__namespace.useContext(CollectionContext);
947
+ React17__namespace.useEffect(() => {
948
+ context.itemMap.set(ref, { ref, ...itemData });
949
+ return () => {
950
+ context.itemMap.delete(ref);
951
+ };
952
+ });
953
+ return /* @__PURE__ */ React17__namespace.createElement(Slot, { ...{ [ITEM_DATA_ATTR]: "" }, ref: composedRefs }, children);
954
+ }
955
+ );
956
+ CollectionItemSlot.displayName = `${name}CollectionItemSlot`;
957
+ function useCollection2() {
958
+ const context = React17__namespace.useContext(CollectionContext);
959
+ return React17__namespace.useCallback(() => {
960
+ const collectionNode = context.collectionRef.current;
961
+ if (!collectionNode) return [];
962
+ const orderedNodes = Array.from(
963
+ collectionNode.querySelectorAll(`[${ITEM_DATA_ATTR}]`)
964
+ );
965
+ const items = Array.from(context.itemMap.values());
966
+ items.sort(
967
+ (a, b) => orderedNodes.indexOf(a.ref.current) - orderedNodes.indexOf(b.ref.current)
968
+ );
969
+ return items;
970
+ }, [context.collectionRef, context.itemMap]);
971
+ }
972
+ return [
973
+ { Provider: CollectionProvider, Slot: CollectionSlot, ItemSlot: CollectionItemSlot },
974
+ useCollection2
975
+ ];
976
+ }
977
+
978
+ // src/roving-focus-group.tsx
979
+ var ENTRY_FOCUS = "rovingFocusGroup.onEntryFocus";
980
+ var EVENT_OPTIONS2 = { bubbles: false, cancelable: true };
981
+ var [Collection, useCollection] = createCollection("RovingFocus");
982
+ var [RovingFocusProvider, useRovingFocusContext] = createContext2("RovingFocusGroup");
983
+ var RovingFocusGroup = React17__namespace.forwardRef(
984
+ (props, forwardedRef) => {
985
+ const {
986
+ orientation,
987
+ dir: dirProp,
988
+ loop = false,
989
+ currentTabStopId: currentTabStopIdProp,
990
+ defaultCurrentTabStopId,
991
+ onCurrentTabStopIdChange,
992
+ onEntryFocus,
993
+ preventScrollOnEntryFocus = false,
994
+ ...groupProps
995
+ } = props;
996
+ const ref = React17__namespace.useRef(null);
997
+ const composedRefs = useComposedRefs(forwardedRef, ref);
998
+ const dir = useDirection(dirProp);
999
+ const [currentTabStopId = null, setCurrentTabStopId] = useControllableState({
1000
+ prop: currentTabStopIdProp,
1001
+ defaultProp: defaultCurrentTabStopId,
1002
+ onChange: onCurrentTabStopIdChange
1003
+ });
1004
+ const [isTabbingBackOut, setIsTabbingBackOut] = React17__namespace.useState(false);
1005
+ const handleEntryFocus = useCallbackRef(onEntryFocus);
1006
+ const getItems = useCollection();
1007
+ const isClickFocusRef = React17__namespace.useRef(false);
1008
+ const [focusableItemsCount, setFocusableItemsCount] = React17__namespace.useState(0);
1009
+ React17__namespace.useEffect(() => {
1010
+ const node = ref.current;
1011
+ if (!node) return;
1012
+ const handler = (e) => handleEntryFocus(e);
1013
+ node.addEventListener(ENTRY_FOCUS, handler);
1014
+ return () => node.removeEventListener(ENTRY_FOCUS, handler);
1015
+ }, [handleEntryFocus]);
1016
+ return /* @__PURE__ */ React17__namespace.createElement(Collection.Provider, null, /* @__PURE__ */ React17__namespace.createElement(Collection.Slot, null, /* @__PURE__ */ React17__namespace.createElement(
1017
+ RovingFocusProvider,
1018
+ {
1019
+ orientation,
1020
+ dir,
1021
+ loop,
1022
+ currentTabStopId,
1023
+ onItemFocus: React17__namespace.useCallback(
1024
+ (id) => setCurrentTabStopId(id),
1025
+ [setCurrentTabStopId]
1026
+ ),
1027
+ onItemShiftTab: React17__namespace.useCallback(() => setIsTabbingBackOut(true), []),
1028
+ onFocusableItemAdd: React17__namespace.useCallback(
1029
+ () => setFocusableItemsCount((c) => c + 1),
1030
+ []
1031
+ ),
1032
+ onFocusableItemRemove: React17__namespace.useCallback(
1033
+ () => setFocusableItemsCount((c) => c - 1),
1034
+ []
1035
+ )
1036
+ },
1037
+ /* @__PURE__ */ React17__namespace.createElement(
1038
+ Primitive.div,
1039
+ {
1040
+ tabIndex: isTabbingBackOut || focusableItemsCount === 0 ? -1 : 0,
1041
+ "data-orientation": orientation,
1042
+ ...groupProps,
1043
+ ref: composedRefs,
1044
+ style: { outline: "none", ...groupProps.style },
1045
+ onMouseDown: composeEventHandlers(groupProps.onMouseDown, () => {
1046
+ isClickFocusRef.current = true;
1047
+ }),
1048
+ onFocus: composeEventHandlers(groupProps.onFocus, (event) => {
1049
+ const isKeyboardFocus = !isClickFocusRef.current;
1050
+ if (event.target === event.currentTarget && isKeyboardFocus && !isTabbingBackOut) {
1051
+ const entryEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS2);
1052
+ event.currentTarget.dispatchEvent(entryEvent);
1053
+ if (!entryEvent.defaultPrevented) {
1054
+ const items = getItems().filter((item) => item.focusable);
1055
+ const activeItem = items.find((item) => item.active);
1056
+ const currentItem = items.find((item) => item.id === currentTabStopId);
1057
+ const candidateItems = [activeItem, currentItem, ...items].filter(
1058
+ Boolean
1059
+ );
1060
+ focusFirst2(
1061
+ candidateItems.map((i) => i.ref.current).filter(Boolean),
1062
+ preventScrollOnEntryFocus
1063
+ );
1064
+ }
1065
+ }
1066
+ isClickFocusRef.current = false;
1067
+ }),
1068
+ onBlur: composeEventHandlers(groupProps.onBlur, () => setIsTabbingBackOut(false))
1069
+ }
1070
+ )
1071
+ )));
1072
+ }
1073
+ );
1074
+ RovingFocusGroup.displayName = "RovingFocusGroup";
1075
+ var RovingFocusItem = React17__namespace.forwardRef(
1076
+ (props, forwardedRef) => {
1077
+ const { focusable = true, active = false, tabStopId, ...itemProps } = props;
1078
+ const autoId = useId2();
1079
+ const id = tabStopId ?? autoId;
1080
+ const context = useRovingFocusContext("RovingFocusItem");
1081
+ const isCurrentTabStop = context.currentTabStopId === id;
1082
+ const getItems = useCollection();
1083
+ const { onFocusableItemAdd, onFocusableItemRemove } = context;
1084
+ React17__namespace.useEffect(() => {
1085
+ if (!focusable) return void 0;
1086
+ onFocusableItemAdd();
1087
+ return () => onFocusableItemRemove();
1088
+ }, [focusable, onFocusableItemAdd, onFocusableItemRemove]);
1089
+ return /* @__PURE__ */ React17__namespace.createElement(Collection.ItemSlot, { id, focusable, active }, /* @__PURE__ */ React17__namespace.createElement(
1090
+ Primitive.span,
1091
+ {
1092
+ tabIndex: isCurrentTabStop ? 0 : -1,
1093
+ "data-orientation": context.orientation,
1094
+ ...itemProps,
1095
+ ref: forwardedRef,
1096
+ onMouseDown: composeEventHandlers(itemProps.onMouseDown, (event) => {
1097
+ if (!focusable) event.preventDefault();
1098
+ else context.onItemFocus(id);
1099
+ }),
1100
+ onFocus: composeEventHandlers(itemProps.onFocus, () => context.onItemFocus(id)),
1101
+ onKeyDown: composeEventHandlers(itemProps.onKeyDown, (event) => {
1102
+ if (event.key === "Tab" && event.shiftKey) {
1103
+ context.onItemShiftTab();
1104
+ return;
1105
+ }
1106
+ if (event.target !== event.currentTarget) return;
1107
+ const focusIntent = getFocusIntent(event, context.orientation, context.dir);
1108
+ if (focusIntent !== void 0) {
1109
+ if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;
1110
+ event.preventDefault();
1111
+ let candidateNodes = getItems().filter((item) => item.focusable).map((item) => item.ref.current).filter(Boolean);
1112
+ if (focusIntent === "last") candidateNodes.reverse();
1113
+ else if (focusIntent === "prev" || focusIntent === "next") {
1114
+ if (focusIntent === "prev") candidateNodes.reverse();
1115
+ const currentIndex = candidateNodes.indexOf(event.currentTarget);
1116
+ candidateNodes = context.loop ? wrapArray(candidateNodes, currentIndex + 1) : candidateNodes.slice(currentIndex + 1);
1117
+ }
1118
+ setTimeout(() => focusFirst2(candidateNodes));
1119
+ }
1120
+ })
1121
+ }
1122
+ ));
1123
+ }
1124
+ );
1125
+ RovingFocusItem.displayName = "RovingFocusItem";
1126
+ var MAP_KEY_TO_FOCUS_INTENT = {
1127
+ ArrowLeft: "prev",
1128
+ ArrowUp: "prev",
1129
+ ArrowRight: "next",
1130
+ ArrowDown: "next",
1131
+ PageUp: "first",
1132
+ Home: "first",
1133
+ PageDown: "last",
1134
+ End: "last"
1135
+ };
1136
+ function getDirectionAwareKey(key, dir) {
1137
+ if (dir !== "rtl") return key;
1138
+ if (key === "ArrowLeft") return "ArrowRight";
1139
+ if (key === "ArrowRight") return "ArrowLeft";
1140
+ return key;
1141
+ }
1142
+ function getFocusIntent(event, orientation, dir) {
1143
+ const key = getDirectionAwareKey(event.key, dir);
1144
+ if (orientation === "vertical" && ["ArrowLeft", "ArrowRight"].includes(key)) return void 0;
1145
+ if (orientation === "horizontal" && ["ArrowUp", "ArrowDown"].includes(key)) return void 0;
1146
+ return MAP_KEY_TO_FOCUS_INTENT[key];
1147
+ }
1148
+ function focusFirst2(candidates, preventScroll = false) {
1149
+ const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;
1150
+ for (const candidate of candidates) {
1151
+ if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;
1152
+ candidate.focus({ preventScroll });
1153
+ if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;
1154
+ }
1155
+ }
1156
+ function wrapArray(arr, startIdx) {
1157
+ return arr.map((_, index) => arr[(startIdx + index) % arr.length]);
1158
+ }
1159
+ var Arrow = React17__namespace.forwardRef((props, forwardedRef) => {
1160
+ const { width = 10, height = 5, ...arrowProps } = props;
1161
+ return /* @__PURE__ */ React17__namespace.createElement(
1162
+ Primitive.svg,
1163
+ {
1164
+ ...arrowProps,
1165
+ ref: forwardedRef,
1166
+ width,
1167
+ height,
1168
+ viewBox: "0 0 30 10",
1169
+ preserveAspectRatio: "none"
1170
+ },
1171
+ /* @__PURE__ */ React17__namespace.createElement("polygon", { points: "0,0 30,0 15,10", fill: "currentColor" })
1172
+ );
1173
+ });
1174
+ Arrow.displayName = "Arrow";
1175
+ var AccessibleIcon = ({ label, children }) => {
1176
+ const child = React17__namespace.Children.only(children);
1177
+ return /* @__PURE__ */ React17__namespace.createElement(React17__namespace.Fragment, null, React17__namespace.cloneElement(child, {
1178
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1179
+ ...child.props,
1180
+ "aria-hidden": "true",
1181
+ focusable: "false"
1182
+ }), /* @__PURE__ */ React17__namespace.createElement(VisuallyHidden, null, label));
1183
+ };
1184
+ AccessibleIcon.displayName = "AccessibleIcon";
1185
+ var lockCount = 0;
1186
+ var originalOverflow = "";
1187
+ var originalPaddingRight = "";
1188
+ function getScrollbarWidth() {
1189
+ if (typeof window === "undefined" || typeof document === "undefined") return 0;
1190
+ return window.innerWidth - document.documentElement.clientWidth;
1191
+ }
1192
+ function useScrollLock(enabled = true) {
1193
+ React17__namespace.useEffect(() => {
1194
+ if (!enabled || typeof document === "undefined") return;
1195
+ if (lockCount === 0) {
1196
+ const body = document.body;
1197
+ const scrollbarWidth = getScrollbarWidth();
1198
+ originalOverflow = body.style.overflow;
1199
+ originalPaddingRight = body.style.paddingRight;
1200
+ const existingPadding = parseInt(window.getComputedStyle(body).paddingRight, 10) || 0;
1201
+ body.style.overflow = "hidden";
1202
+ if (scrollbarWidth > 0) {
1203
+ body.style.paddingRight = `${existingPadding + scrollbarWidth}px`;
1204
+ }
1205
+ }
1206
+ lockCount++;
1207
+ return () => {
1208
+ lockCount--;
1209
+ if (lockCount === 0) {
1210
+ document.body.style.overflow = originalOverflow;
1211
+ document.body.style.paddingRight = originalPaddingRight;
1212
+ }
1213
+ };
1214
+ }, [enabled]);
1215
+ }
1216
+ var ScrollLock = ({
1217
+ children,
1218
+ enabled = true
1219
+ }) => {
1220
+ useScrollLock(enabled);
1221
+ return /* @__PURE__ */ React17__namespace.createElement(React17__namespace.Fragment, null, children);
1222
+ };
1223
+ ScrollLock.displayName = "ScrollLock";
1224
+
1225
+ // src/popper.tsx
1226
+ var popper_exports = {};
1227
+ __export(popper_exports, {
1228
+ Anchor: () => PopperAnchor,
1229
+ Arrow: () => PopperArrow,
1230
+ Content: () => PopperContent,
1231
+ Root: () => Popper
1232
+ });
1233
+ var [PopperProvider, usePopperContext] = createContext2("Popper");
1234
+ var Popper = ({ children }) => {
1235
+ const [anchor, setAnchor] = React17__namespace.useState(null);
1236
+ return /* @__PURE__ */ React17__namespace.createElement(PopperProvider, { anchor, onAnchorChange: setAnchor }, children);
1237
+ };
1238
+ Popper.displayName = "Popper";
1239
+ var PopperAnchor = React17__namespace.forwardRef((props, forwardedRef) => {
1240
+ const { virtualRef, ...anchorProps } = props;
1241
+ const ctx = usePopperContext("PopperAnchor");
1242
+ const ref = React17__namespace.useRef(null);
1243
+ const composedRefs = useComposedRefs(forwardedRef, ref);
1244
+ React17__namespace.useEffect(() => {
1245
+ ctx.onAnchorChange(virtualRef?.current ?? ref.current);
1246
+ });
1247
+ return virtualRef ? null : /* @__PURE__ */ React17__namespace.createElement(Primitive.div, { ...anchorProps, ref: composedRefs });
1248
+ });
1249
+ PopperAnchor.displayName = "PopperAnchor";
1250
+ var [PopperContentProvider, useContentContext] = createContext2("PopperContent");
1251
+ var PopperContent = React17__namespace.forwardRef(
1252
+ (props, forwardedRef) => {
1253
+ const {
1254
+ side = "bottom",
1255
+ sideOffset = 0,
1256
+ align = "center",
1257
+ alignOffset = 0,
1258
+ arrowPadding = 0,
1259
+ avoidCollisions = true,
1260
+ collisionBoundary = [],
1261
+ collisionPadding: collisionPaddingProp = 0,
1262
+ sticky = "partial",
1263
+ hideWhenDetached = false,
1264
+ updatePositionStrategy = "optimized",
1265
+ onPlaced,
1266
+ strategy = "fixed",
1267
+ ...contentProps
1268
+ } = props;
1269
+ const ctx = usePopperContext("PopperContent");
1270
+ const [content, setContent] = React17__namespace.useState(null);
1271
+ const composedRefs = useComposedRefs(forwardedRef, setContent);
1272
+ const [arrow, setArrow] = React17__namespace.useState(null);
1273
+ const arrowSize = useSize(arrow);
1274
+ const arrowWidth = arrowSize?.width ?? 0;
1275
+ const arrowHeight = arrowSize?.height ?? 0;
1276
+ const dir = useDirection();
1277
+ const desiredPlacement = side + (align !== "center" ? "-" + align : "");
1278
+ const collisionPadding = typeof collisionPaddingProp === "number" ? collisionPaddingProp : { top: 0, right: 0, bottom: 0, left: 0, ...collisionPaddingProp };
1279
+ const boundary = Array.isArray(collisionBoundary) ? collisionBoundary : [collisionBoundary].filter(Boolean);
1280
+ const detectOverflowOptions = {
1281
+ padding: collisionPadding,
1282
+ boundary: boundary.filter(isHTMLElement),
1283
+ altBoundary: boundary.length > 0
1284
+ };
1285
+ const middleware = [
1286
+ react.offset({ mainAxis: sideOffset + arrowHeight, alignmentAxis: alignOffset }),
1287
+ avoidCollisions && react.shift({
1288
+ mainAxis: true,
1289
+ crossAxis: true,
1290
+ limiter: sticky === "partial" ? react.limitShift() : void 0,
1291
+ ...detectOverflowOptions
1292
+ }),
1293
+ avoidCollisions && react.flip({ ...detectOverflowOptions }),
1294
+ react.size({
1295
+ ...detectOverflowOptions,
1296
+ apply: ({ elements, rects, availableWidth, availableHeight }) => {
1297
+ const { width: anchorWidth, height: anchorHeight } = rects.reference;
1298
+ const contentStyle = elements.floating.style;
1299
+ contentStyle.setProperty("--structyl-popper-available-width", `${availableWidth}px`);
1300
+ contentStyle.setProperty("--structyl-popper-available-height", `${availableHeight}px`);
1301
+ contentStyle.setProperty("--structyl-popper-anchor-width", `${anchorWidth}px`);
1302
+ contentStyle.setProperty("--structyl-popper-anchor-height", `${anchorHeight}px`);
1303
+ }
1304
+ }),
1305
+ arrow && react.arrow({ element: arrow, padding: arrowPadding }),
1306
+ transformOrigin({ arrowWidth, arrowHeight }),
1307
+ hideWhenDetached && react.hide({ strategy: "referenceHidden" })
1308
+ ].filter(Boolean);
1309
+ const { refs, floatingStyles, placement, isPositioned, middlewareData } = react.useFloating({
1310
+ strategy,
1311
+ placement: desiredPlacement,
1312
+ whileElementsMounted: (...args) => react.autoUpdate(...args, {
1313
+ animationFrame: updatePositionStrategy === "always"
1314
+ }),
1315
+ middleware
1316
+ });
1317
+ React17__namespace.useLayoutEffect(() => {
1318
+ refs.setPositionReference(ctx.anchor);
1319
+ }, [ctx.anchor, refs]);
1320
+ const [placedSide, placedAlign] = getSideAndAlignFromPlacement(placement);
1321
+ const handlePlaced = useCallbackRef(onPlaced);
1322
+ React17__namespace.useLayoutEffect(() => {
1323
+ if (isPositioned) handlePlaced();
1324
+ }, [isPositioned, handlePlaced]);
1325
+ const arrowX = middlewareData.arrow?.x;
1326
+ const arrowY = middlewareData.arrow?.y;
1327
+ const cannotCenterArrow = middlewareData.arrow?.centerOffset !== 0;
1328
+ const contentStyleZIndex = contentProps.style?.zIndex;
1329
+ const [contentZIndex, setContentZIndex] = React17__namespace.useState(
1330
+ contentStyleZIndex ?? 50
1331
+ );
1332
+ React17__namespace.useLayoutEffect(() => {
1333
+ if (!content) {
1334
+ setContentZIndex(contentStyleZIndex ?? 50);
1335
+ return;
1336
+ }
1337
+ const computedZIndex = window.getComputedStyle(content).zIndex;
1338
+ setContentZIndex(computedZIndex === "auto" ? contentStyleZIndex ?? 50 : computedZIndex);
1339
+ }, [content, contentProps.className, contentStyleZIndex]);
1340
+ return /* @__PURE__ */ React17__namespace.createElement(
1341
+ "div",
1342
+ {
1343
+ ref: refs.setFloating,
1344
+ "data-structyl-popper-content-wrapper": "",
1345
+ style: {
1346
+ ...floatingStyles,
1347
+ transform: isPositioned ? floatingStyles.transform : "translate(0, -200%)",
1348
+ minWidth: "max-content",
1349
+ zIndex: contentZIndex,
1350
+ ["--structyl-popper-transform-origin"]: [middlewareData.transformOrigin?.x, middlewareData.transformOrigin?.y].join(" ") || void 0,
1351
+ ...middlewareData.hide?.referenceHidden && {
1352
+ visibility: "hidden",
1353
+ pointerEvents: "none"
1354
+ }
1355
+ },
1356
+ dir
1357
+ },
1358
+ /* @__PURE__ */ React17__namespace.createElement(
1359
+ PopperContentProvider,
1360
+ {
1361
+ placedSide,
1362
+ placedAlign,
1363
+ arrowX,
1364
+ arrowY,
1365
+ shouldHideArrow: cannotCenterArrow,
1366
+ onArrowChange: setArrow
1367
+ },
1368
+ /* @__PURE__ */ React17__namespace.createElement(
1369
+ Primitive.div,
1370
+ {
1371
+ "data-side": placedSide,
1372
+ "data-align": placedAlign,
1373
+ ...contentProps,
1374
+ ref: composedRefs,
1375
+ style: { ...contentProps.style, animation: !isPositioned ? "none" : void 0 }
1376
+ }
1377
+ )
1378
+ )
1379
+ );
1380
+ }
1381
+ );
1382
+ PopperContent.displayName = "PopperContent";
1383
+ var OPPOSITE_SIDE = {
1384
+ top: "bottom",
1385
+ right: "left",
1386
+ bottom: "top",
1387
+ left: "right"
1388
+ };
1389
+ var PopperArrow = React17__namespace.forwardRef((props, forwardedRef) => {
1390
+ const { ...arrowProps } = props;
1391
+ const contentContext = useContentContext("PopperArrow");
1392
+ const baseSide = OPPOSITE_SIDE[contentContext.placedSide];
1393
+ return /* @__PURE__ */ React17__namespace.createElement(
1394
+ "span",
1395
+ {
1396
+ ref: contentContext.onArrowChange,
1397
+ style: {
1398
+ position: "absolute",
1399
+ left: contentContext.arrowX,
1400
+ top: contentContext.arrowY,
1401
+ [baseSide]: 0,
1402
+ transformOrigin: {
1403
+ top: "",
1404
+ right: "0 0",
1405
+ bottom: "center 0",
1406
+ left: "100% 0"
1407
+ }[contentContext.placedSide],
1408
+ transform: {
1409
+ top: "translateY(100%)",
1410
+ right: "translateY(50%) rotate(90deg) translateX(-50%)",
1411
+ bottom: "rotate(180deg)",
1412
+ left: "translateY(50%) rotate(-90deg) translateX(50%)"
1413
+ }[contentContext.placedSide],
1414
+ visibility: contentContext.shouldHideArrow ? "hidden" : void 0
1415
+ }
1416
+ },
1417
+ /* @__PURE__ */ React17__namespace.createElement(
1418
+ Arrow,
1419
+ {
1420
+ ...arrowProps,
1421
+ ref: forwardedRef,
1422
+ style: { ...arrowProps.style, display: "block" }
1423
+ }
1424
+ )
1425
+ );
1426
+ });
1427
+ PopperArrow.displayName = "PopperArrow";
1428
+ function isHTMLElement(value) {
1429
+ return value instanceof Element;
1430
+ }
1431
+ function getSideAndAlignFromPlacement(placement) {
1432
+ const [side, align = "center"] = placement.split("-");
1433
+ return [side, align];
1434
+ }
1435
+ function transformOrigin(options) {
1436
+ return {
1437
+ name: "transformOrigin",
1438
+ options,
1439
+ fn(data) {
1440
+ const { placement, rects, middlewareData } = data;
1441
+ const cannotCenterArrow = middlewareData.arrow?.centerOffset !== 0;
1442
+ const isArrowHidden = cannotCenterArrow;
1443
+ const arrowWidth = isArrowHidden ? 0 : options.arrowWidth;
1444
+ const arrowHeight = isArrowHidden ? 0 : options.arrowHeight;
1445
+ const [placedSide, placedAlign] = getSideAndAlignFromPlacement(placement);
1446
+ const noArrowAlign = { start: "0%", center: "50%", end: "100%" }[placedAlign];
1447
+ const arrowXCenter = (middlewareData.arrow?.x ?? 0) + arrowWidth / 2;
1448
+ const arrowYCenter = (middlewareData.arrow?.y ?? 0) + arrowHeight / 2;
1449
+ let x = "";
1450
+ let y = "";
1451
+ if (placedSide === "bottom") {
1452
+ x = isArrowHidden ? noArrowAlign : `${arrowXCenter}px`;
1453
+ y = `${-arrowHeight}px`;
1454
+ } else if (placedSide === "top") {
1455
+ x = isArrowHidden ? noArrowAlign : `${arrowXCenter}px`;
1456
+ y = `${rects.floating.height + arrowHeight}px`;
1457
+ } else if (placedSide === "right") {
1458
+ x = `${-arrowHeight}px`;
1459
+ y = isArrowHidden ? noArrowAlign : `${arrowYCenter}px`;
1460
+ } else if (placedSide === "left") {
1461
+ x = `${rects.floating.width + arrowHeight}px`;
1462
+ y = isArrowHidden ? noArrowAlign : `${arrowYCenter}px`;
1463
+ }
1464
+ return { data: { x, y } };
1465
+ }
1466
+ };
1467
+ }
1468
+ function useSize(element) {
1469
+ const [size, setSize] = React17__namespace.useState(void 0);
1470
+ React17__namespace.useLayoutEffect(() => {
1471
+ if (!element) {
1472
+ setSize(void 0);
1473
+ return;
1474
+ }
1475
+ setSize({ width: element.offsetWidth, height: element.offsetHeight });
1476
+ const observer = new ResizeObserver((entries) => {
1477
+ const entry = entries[0];
1478
+ if (!entry) return;
1479
+ const borderBox = entry.borderBoxSize?.[0];
1480
+ const w = borderBox ? borderBox.inlineSize : entry.contentRect.width;
1481
+ const h = borderBox ? borderBox.blockSize : entry.contentRect.height;
1482
+ setSize({ width: w, height: h });
1483
+ });
1484
+ observer.observe(element, { box: "border-box" });
1485
+ return () => observer.disconnect();
1486
+ }, [element]);
1487
+ return size;
1488
+ }
1489
+
1490
+ exports.AccessibleIcon = AccessibleIcon;
1491
+ exports.Arrow = Arrow;
1492
+ exports.DirectionProvider = DirectionProvider;
1493
+ exports.DismissableLayer = DismissableLayer;
1494
+ exports.DismissableLayerBranch = DismissableLayerBranch;
1495
+ exports.FocusGuards = FocusGuards;
1496
+ exports.FocusScope = FocusScope;
1497
+ exports.Popper = popper_exports;
1498
+ exports.Portal = Portal;
1499
+ exports.Presence = Presence;
1500
+ exports.Primitive = Primitive;
1501
+ exports.RovingFocusGroup = RovingFocusGroup;
1502
+ exports.RovingFocusItem = RovingFocusItem;
1503
+ exports.ScrollLock = ScrollLock;
1504
+ exports.Slot = Slot;
1505
+ exports.Slottable = Slottable;
1506
+ exports.VisuallyHidden = VisuallyHidden;
1507
+ exports.composeContextScopes = composeContextScopes;
1508
+ exports.createCollection = createCollection;
1509
+ exports.createContext = createContext2;
1510
+ exports.createContextScope = createContextScope;
1511
+ exports.useDirection = useDirection;
1512
+ exports.useFocusGuards = useFocusGuards;
1513
+ exports.usePresence = usePresence;
1514
+ exports.useScrollLock = useScrollLock;
1515
+ //# sourceMappingURL=index.cjs.map
1516
+ //# sourceMappingURL=index.cjs.map