bits-ui 2.8.0 → 2.8.2
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/bits/accordion/accordion.svelte.d.ts +6 -1
- package/dist/bits/accordion/accordion.svelte.js +15 -5
- package/dist/bits/alert-dialog/components/alert-dialog-content.svelte +6 -9
- package/dist/bits/aspect-ratio/aspect-ratio.svelte.d.ts +2 -1
- package/dist/bits/aspect-ratio/aspect-ratio.svelte.js +3 -1
- package/dist/bits/avatar/avatar.svelte.d.ts +4 -1
- package/dist/bits/avatar/avatar.svelte.js +9 -3
- package/dist/bits/calendar/calendar.svelte.d.ts +15 -1
- package/dist/bits/calendar/calendar.svelte.js +42 -14
- package/dist/bits/calendar/components/calendar-next-button.svelte +3 -1
- package/dist/bits/calendar/components/calendar-prev-button.svelte +3 -1
- package/dist/bits/checkbox/checkbox.svelte.d.ts +4 -1
- package/dist/bits/checkbox/checkbox.svelte.js +9 -3
- package/dist/bits/collapsible/collapsible.svelte.d.ts +4 -1
- package/dist/bits/collapsible/collapsible.svelte.js +9 -3
- package/dist/bits/command/command.svelte.d.ts +13 -1
- package/dist/bits/command/command.svelte.js +36 -12
- package/dist/bits/context-menu/components/context-menu-content.svelte +1 -1
- package/dist/bits/date-field/date-field.svelte.d.ts +8 -2
- package/dist/bits/date-field/date-field.svelte.js +18 -6
- package/dist/bits/date-range-field/date-range-field.svelte.d.ts +3 -1
- package/dist/bits/date-range-field/date-range-field.svelte.js +6 -2
- package/dist/bits/dialog/components/dialog-content.svelte +5 -7
- package/dist/bits/dialog/dialog.svelte.d.ts +9 -1
- package/dist/bits/dialog/dialog.svelte.js +33 -18
- package/dist/bits/dropdown-menu/components/dropdown-menu-content.svelte +3 -2
- package/dist/bits/label/label.svelte.d.ts +2 -1
- package/dist/bits/label/label.svelte.js +3 -1
- package/dist/bits/link-preview/link-preview.svelte.d.ts +3 -1
- package/dist/bits/link-preview/link-preview.svelte.js +6 -2
- package/dist/bits/menu/menu.svelte.d.ts +12 -1
- package/dist/bits/menu/menu.svelte.js +38 -26
- package/dist/bits/menubar/components/menubar-trigger.svelte +4 -3
- package/dist/bits/menubar/menubar.svelte.d.ts +4 -3
- package/dist/bits/menubar/menubar.svelte.js +9 -6
- package/dist/bits/meter/meter.svelte.d.ts +2 -1
- package/dist/bits/meter/meter.svelte.js +3 -1
- package/dist/bits/navigation-menu/components/navigation-menu-link.svelte +2 -1
- package/dist/bits/navigation-menu/components/navigation-menu-trigger.svelte +2 -1
- package/dist/bits/navigation-menu/navigation-menu.svelte.d.ts +14 -3
- package/dist/bits/navigation-menu/navigation-menu.svelte.js +33 -13
- package/dist/bits/pagination/pagination.svelte.d.ts +4 -1
- package/dist/bits/pagination/pagination.svelte.js +9 -3
- package/dist/bits/pin-input/pin-input.svelte.d.ts +4 -1
- package/dist/bits/pin-input/pin-input.svelte.js +8 -3
- package/dist/bits/popover/popover.svelte.d.ts +4 -1
- package/dist/bits/popover/popover.svelte.js +9 -3
- package/dist/bits/progress/progress.svelte.d.ts +2 -1
- package/dist/bits/progress/progress.svelte.js +3 -1
- package/dist/bits/radio-group/radio-group.svelte.d.ts +3 -1
- package/dist/bits/radio-group/radio-group.svelte.js +6 -2
- package/dist/bits/range-calendar/range-calendar.svelte.d.ts +4 -1
- package/dist/bits/range-calendar/range-calendar.svelte.js +9 -3
- package/dist/bits/rating-group/rating-group.svelte.d.ts +3 -1
- package/dist/bits/rating-group/rating-group.svelte.js +6 -2
- package/dist/bits/scroll-area/scroll-area.svelte.d.ts +8 -1
- package/dist/bits/scroll-area/scroll-area.svelte.js +20 -7
- package/dist/bits/select/select.svelte.d.ts +10 -1
- package/dist/bits/select/select.svelte.js +27 -9
- package/dist/bits/separator/separator.svelte.d.ts +2 -1
- package/dist/bits/separator/separator.svelte.js +3 -1
- package/dist/bits/slider/slider.svelte.d.ts +7 -1
- package/dist/bits/slider/slider.svelte.js +18 -6
- package/dist/bits/switch/switch.svelte.d.ts +3 -1
- package/dist/bits/switch/switch.svelte.js +6 -2
- package/dist/bits/tabs/tabs.svelte.d.ts +5 -1
- package/dist/bits/tabs/tabs.svelte.js +12 -4
- package/dist/bits/time-field/time-field.svelte.d.ts +7 -1
- package/dist/bits/time-field/time-field.svelte.js +18 -6
- package/dist/bits/time-range-field/time-range-field.svelte.d.ts +3 -1
- package/dist/bits/time-range-field/time-range-field.svelte.js +6 -2
- package/dist/bits/toggle/toggle.svelte.d.ts +2 -1
- package/dist/bits/toggle/toggle.svelte.js +3 -1
- package/dist/bits/toggle-group/toggle-group.svelte.d.ts +3 -1
- package/dist/bits/toggle-group/toggle-group.svelte.js +6 -2
- package/dist/bits/toolbar/toolbar.svelte.d.ts +6 -1
- package/dist/bits/toolbar/toolbar.svelte.js +15 -5
- package/dist/bits/tooltip/tooltip.svelte.d.ts +3 -1
- package/dist/bits/tooltip/tooltip.svelte.js +6 -2
- package/dist/bits/utilities/floating-layer/use-floating-layer.svelte.d.ts +9 -0
- package/dist/bits/utilities/floating-layer/use-floating-layer.svelte.js +6 -3
- package/dist/bits/utilities/focus-scope/focus-scope-manager.d.ts +12 -0
- package/dist/bits/utilities/focus-scope/focus-scope-manager.js +40 -0
- package/dist/bits/utilities/focus-scope/focus-scope.svelte +6 -8
- package/dist/bits/utilities/focus-scope/focus-scope.svelte.d.ts +1 -0
- package/dist/bits/utilities/focus-scope/focus-scope.svelte.js +204 -0
- package/dist/bits/utilities/focus-scope/types.d.ts +2 -6
- package/dist/bits/utilities/popper-layer/popper-layer-inner.svelte +2 -2
- package/dist/internal/focus.js +1 -1
- package/dist/internal/should-enable-focus-trap.d.ts +5 -0
- package/dist/internal/should-enable-focus-trap.js +5 -0
- package/dist/internal/types.d.ts +2 -1
- package/package.json +2 -2
- package/dist/bits/utilities/focus-scope/focus-scope-stack.svelte.d.ts +0 -14
- package/dist/bits/utilities/focus-scope/focus-scope-stack.svelte.js +0 -50
- package/dist/bits/utilities/focus-scope/use-focus-scope.svelte.d.ts +0 -49
- package/dist/bits/utilities/focus-scope/use-focus-scope.svelte.js +0 -218
- package/dist/internal/should-trap-focus.d.ts +0 -6
- package/dist/internal/should-trap-focus.js +0 -5
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
import { afterSleep, afterTick, DOMContext, executeCallbacks, } from "svelte-toolbelt";
|
|
2
|
-
import { Context, watch } from "runed";
|
|
3
|
-
import { on } from "svelte/events";
|
|
4
|
-
import { createFocusScopeAPI, createFocusScopeStack, removeLinks, } from "./focus-scope-stack.svelte.js";
|
|
5
|
-
import { focus, focusFirst, getTabbableCandidates, getTabbableEdges } from "../../../internal/focus.js";
|
|
6
|
-
import { CustomEventDispatcher } from "../../../internal/events.js";
|
|
7
|
-
import { isHTMLElement } from "../../../internal/is.js";
|
|
8
|
-
import { kbd } from "../../../internal/kbd.js";
|
|
9
|
-
import { isTabbable } from "tabbable";
|
|
10
|
-
const AutoFocusOnMountEvent = new CustomEventDispatcher("focusScope.autoFocusOnMount", {
|
|
11
|
-
bubbles: false,
|
|
12
|
-
cancelable: true,
|
|
13
|
-
});
|
|
14
|
-
const AutoFocusOnDestroyEvent = new CustomEventDispatcher("focusScope.autoFocusOnDestroy", {
|
|
15
|
-
bubbles: false,
|
|
16
|
-
cancelable: true,
|
|
17
|
-
});
|
|
18
|
-
export const FocusScopeContext = new Context("FocusScope");
|
|
19
|
-
export function useFocusScope({ id, loop, enabled, onOpenAutoFocus, onCloseAutoFocus, forceMount, ref, }) {
|
|
20
|
-
const focusScopeStack = createFocusScopeStack();
|
|
21
|
-
const focusScope = createFocusScopeAPI();
|
|
22
|
-
const ctx = FocusScopeContext.getOr({ ignoreCloseAutoFocus: false });
|
|
23
|
-
let lastFocusedElement = null;
|
|
24
|
-
const domContext = new DOMContext(ref);
|
|
25
|
-
function manageFocus(event) {
|
|
26
|
-
if (focusScope.paused || !ref.current || focusScope.isHandlingFocus)
|
|
27
|
-
return;
|
|
28
|
-
focusScope.isHandlingFocus = true;
|
|
29
|
-
try {
|
|
30
|
-
const target = event.target;
|
|
31
|
-
if (!isHTMLElement(target))
|
|
32
|
-
return;
|
|
33
|
-
const isWithinActiveScope = ref.current.contains(target);
|
|
34
|
-
if (event.type === "focusin") {
|
|
35
|
-
if (isWithinActiveScope) {
|
|
36
|
-
lastFocusedElement = target;
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
if (ctx.ignoreCloseAutoFocus)
|
|
40
|
-
return;
|
|
41
|
-
focus(lastFocusedElement, { select: true });
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
else if (event.type === "focusout") {
|
|
45
|
-
if (!isWithinActiveScope && !ctx.ignoreCloseAutoFocus) {
|
|
46
|
-
focus(lastFocusedElement, { select: true });
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
finally {
|
|
51
|
-
focusScope.isHandlingFocus = false;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Handles DOM mutations within the container. Specifically checks if the
|
|
56
|
-
* last known focused element inside the container has been removed. If so,
|
|
57
|
-
* and focus has escaped the container (likely moved to document.body),
|
|
58
|
-
* it refocuses the container itself to maintain the trap.
|
|
59
|
-
*/
|
|
60
|
-
function handleMutations(mutations) {
|
|
61
|
-
// if there's no record of a last focused el, or container isn't mounted, bail
|
|
62
|
-
if (!lastFocusedElement || !ref.current)
|
|
63
|
-
return;
|
|
64
|
-
// track if the last focused element was removed
|
|
65
|
-
let elementWasRemoved = false;
|
|
66
|
-
for (const mutation of mutations) {
|
|
67
|
-
// we only care about mutations where nodes were removed
|
|
68
|
-
if (mutation.type === "childList" && mutation.removedNodes.length > 0) {
|
|
69
|
-
// check if any removed nodes are the last focused element or contain it
|
|
70
|
-
for (const removedNode of mutation.removedNodes) {
|
|
71
|
-
if (removedNode === lastFocusedElement) {
|
|
72
|
-
elementWasRemoved = true;
|
|
73
|
-
// found it directly
|
|
74
|
-
break;
|
|
75
|
-
}
|
|
76
|
-
// contains() only works on elements, so we need to check nodeType
|
|
77
|
-
if (removedNode.nodeType === Node.ELEMENT_NODE &&
|
|
78
|
-
removedNode.contains(lastFocusedElement)) {
|
|
79
|
-
elementWasRemoved = true;
|
|
80
|
-
// descendant found,
|
|
81
|
-
break;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
// if we've confirmed removal in any mutation, bail
|
|
86
|
-
if (elementWasRemoved)
|
|
87
|
-
break;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* If the element was removed and focus is now outside the container,
|
|
91
|
-
* (e.g., browser moved it to body), refocus the container.
|
|
92
|
-
*/
|
|
93
|
-
if (elementWasRemoved &&
|
|
94
|
-
ref.current &&
|
|
95
|
-
!ref.current.contains(domContext.getActiveElement())) {
|
|
96
|
-
focus(ref.current);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
watch([() => ref.current, () => enabled.current], ([container, enabled]) => {
|
|
100
|
-
if (!container || !enabled)
|
|
101
|
-
return;
|
|
102
|
-
const removeEvents = executeCallbacks(on(domContext.getDocument(), "focusin", manageFocus), on(domContext.getDocument(), "focusout", manageFocus));
|
|
103
|
-
const mutationObserver = new MutationObserver(handleMutations);
|
|
104
|
-
mutationObserver.observe(container, {
|
|
105
|
-
childList: true,
|
|
106
|
-
subtree: true,
|
|
107
|
-
attributes: false,
|
|
108
|
-
});
|
|
109
|
-
return () => {
|
|
110
|
-
removeEvents();
|
|
111
|
-
mutationObserver.disconnect();
|
|
112
|
-
};
|
|
113
|
-
});
|
|
114
|
-
watch([() => forceMount.current, () => ref.current], ([forceMount, container]) => {
|
|
115
|
-
if (forceMount)
|
|
116
|
-
return;
|
|
117
|
-
const prevFocusedElement = domContext.getActiveElement();
|
|
118
|
-
handleOpen(container, prevFocusedElement);
|
|
119
|
-
return () => {
|
|
120
|
-
if (!container)
|
|
121
|
-
return;
|
|
122
|
-
handleClose(prevFocusedElement);
|
|
123
|
-
};
|
|
124
|
-
});
|
|
125
|
-
watch([() => forceMount.current, () => ref.current, () => enabled.current], ([forceMount, container]) => {
|
|
126
|
-
if (!forceMount)
|
|
127
|
-
return;
|
|
128
|
-
const prevFocusedElement = domContext.getActiveElement();
|
|
129
|
-
handleOpen(container, prevFocusedElement);
|
|
130
|
-
return () => {
|
|
131
|
-
if (!container)
|
|
132
|
-
return;
|
|
133
|
-
handleClose(prevFocusedElement);
|
|
134
|
-
};
|
|
135
|
-
});
|
|
136
|
-
function handleOpen(container, prevFocusedElement) {
|
|
137
|
-
if (!container)
|
|
138
|
-
container = domContext.getElementById(id.current);
|
|
139
|
-
if (!container || !enabled.current)
|
|
140
|
-
return;
|
|
141
|
-
focusScopeStack.add(focusScope);
|
|
142
|
-
const hasFocusedCandidate = container.contains(prevFocusedElement);
|
|
143
|
-
if (!hasFocusedCandidate) {
|
|
144
|
-
const mountEvent = AutoFocusOnMountEvent.createEvent();
|
|
145
|
-
onOpenAutoFocus.current(mountEvent);
|
|
146
|
-
if (!mountEvent.defaultPrevented) {
|
|
147
|
-
afterTick(() => {
|
|
148
|
-
if (!container)
|
|
149
|
-
return;
|
|
150
|
-
const result = focusFirst(removeLinks(getTabbableCandidates(container)), {
|
|
151
|
-
select: true,
|
|
152
|
-
}, () => domContext.getActiveElement());
|
|
153
|
-
if (!result)
|
|
154
|
-
focus(container);
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
function handleClose(prevFocusedElement) {
|
|
160
|
-
const destroyEvent = AutoFocusOnDestroyEvent.createEvent();
|
|
161
|
-
onCloseAutoFocus.current?.(destroyEvent);
|
|
162
|
-
const shouldIgnore = ctx.ignoreCloseAutoFocus;
|
|
163
|
-
afterSleep(0, () => {
|
|
164
|
-
if (!destroyEvent.defaultPrevented && prevFocusedElement && !shouldIgnore) {
|
|
165
|
-
focus(isTabbable(prevFocusedElement)
|
|
166
|
-
? prevFocusedElement
|
|
167
|
-
: domContext.getDocument().body, {
|
|
168
|
-
select: true,
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
focusScopeStack.remove(focusScope);
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
function handleKeydown(e) {
|
|
175
|
-
if (!enabled.current)
|
|
176
|
-
return;
|
|
177
|
-
if (!loop.current && !enabled.current)
|
|
178
|
-
return;
|
|
179
|
-
if (focusScope.paused)
|
|
180
|
-
return;
|
|
181
|
-
const isTabKey = e.key === kbd.TAB && !e.ctrlKey && !e.altKey && !e.metaKey;
|
|
182
|
-
const focusedElement = domContext.getActiveElement();
|
|
183
|
-
if (!(isTabKey && focusedElement))
|
|
184
|
-
return;
|
|
185
|
-
const container = ref.current;
|
|
186
|
-
if (!container)
|
|
187
|
-
return;
|
|
188
|
-
const [first, last] = getTabbableEdges(container);
|
|
189
|
-
const hasTabbableElementsInside = first && last;
|
|
190
|
-
if (!hasTabbableElementsInside) {
|
|
191
|
-
if (focusedElement === container) {
|
|
192
|
-
e.preventDefault();
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
else {
|
|
196
|
-
if (!e.shiftKey && focusedElement === last) {
|
|
197
|
-
e.preventDefault();
|
|
198
|
-
if (loop.current)
|
|
199
|
-
focus(first, { select: true });
|
|
200
|
-
}
|
|
201
|
-
else if (e.shiftKey && focusedElement === first) {
|
|
202
|
-
e.preventDefault();
|
|
203
|
-
if (loop.current)
|
|
204
|
-
focus(last, { select: true });
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
const props = $derived.by(() => ({
|
|
209
|
-
id: id.current,
|
|
210
|
-
tabindex: -1,
|
|
211
|
-
onkeydown: handleKeydown,
|
|
212
|
-
}));
|
|
213
|
-
return {
|
|
214
|
-
get props() {
|
|
215
|
-
return props;
|
|
216
|
-
},
|
|
217
|
-
};
|
|
218
|
-
}
|