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
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { box } from "svelte-toolbelt";
|
|
2
|
+
import { FocusScope } from "./focus-scope.svelte.js";
|
|
3
|
+
export class FocusScopeManager {
|
|
4
|
+
static instance;
|
|
5
|
+
#scopeStack = box([]);
|
|
6
|
+
#focusHistory = new WeakMap();
|
|
7
|
+
static getInstance() {
|
|
8
|
+
if (!this.instance) {
|
|
9
|
+
this.instance = new FocusScopeManager();
|
|
10
|
+
}
|
|
11
|
+
return this.instance;
|
|
12
|
+
}
|
|
13
|
+
register(scope) {
|
|
14
|
+
const current = this.getActive();
|
|
15
|
+
if (current && current !== scope) {
|
|
16
|
+
current.pause();
|
|
17
|
+
}
|
|
18
|
+
this.#scopeStack.current = this.#scopeStack.current.filter((s) => s !== scope);
|
|
19
|
+
this.#scopeStack.current.unshift(scope);
|
|
20
|
+
}
|
|
21
|
+
unregister(scope) {
|
|
22
|
+
this.#scopeStack.current = this.#scopeStack.current.filter((s) => s !== scope);
|
|
23
|
+
const next = this.getActive();
|
|
24
|
+
if (next) {
|
|
25
|
+
next.resume();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
getActive() {
|
|
29
|
+
return this.#scopeStack.current[0];
|
|
30
|
+
}
|
|
31
|
+
setFocusMemory(scope, element) {
|
|
32
|
+
this.#focusHistory.set(scope, element);
|
|
33
|
+
}
|
|
34
|
+
getFocusMemory(scope) {
|
|
35
|
+
return this.#focusHistory.get(scope);
|
|
36
|
+
}
|
|
37
|
+
isActiveScope(scope) {
|
|
38
|
+
return this.getActive() === scope;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { box } from "svelte-toolbelt";
|
|
3
3
|
import type { FocusScopeImplProps } from "./types.js";
|
|
4
|
-
import { useFocusScope } from "./use-focus-scope.svelte.js";
|
|
5
4
|
import { noop } from "../../../internal/noop.js";
|
|
5
|
+
import { FocusScope } from "./focus-scope.svelte.js";
|
|
6
6
|
|
|
7
7
|
let {
|
|
8
|
-
|
|
8
|
+
enabled = false,
|
|
9
9
|
trapFocus = false,
|
|
10
10
|
loop = false,
|
|
11
11
|
onCloseAutoFocus = noop,
|
|
12
12
|
onOpenAutoFocus = noop,
|
|
13
13
|
focusScope,
|
|
14
|
-
forceMount = false,
|
|
15
14
|
ref,
|
|
16
15
|
}: FocusScopeImplProps = $props();
|
|
17
16
|
|
|
18
|
-
const focusScopeState =
|
|
19
|
-
enabled: box.with(() =>
|
|
20
|
-
|
|
17
|
+
const focusScopeState = FocusScope.use({
|
|
18
|
+
enabled: box.with(() => enabled),
|
|
19
|
+
trap: box.with(() => trapFocus),
|
|
20
|
+
loop: loop,
|
|
21
21
|
onCloseAutoFocus: box.with(() => onCloseAutoFocus),
|
|
22
22
|
onOpenAutoFocus: box.with(() => onOpenAutoFocus),
|
|
23
|
-
id: box.with(() => id),
|
|
24
|
-
forceMount: box.with(() => forceMount),
|
|
25
23
|
ref,
|
|
26
24
|
});
|
|
27
25
|
</script>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FocusScopeImplProps } from "./types.js";
|
|
2
|
+
import { FocusScope } from "./focus-scope.svelte.js";
|
|
2
3
|
declare const FocusScope: import("svelte").Component<FocusScopeImplProps, {}, "">;
|
|
3
4
|
type FocusScope = ReturnType<typeof FocusScope>;
|
|
4
5
|
export default FocusScope;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { onDestroyEffect } from "svelte-toolbelt";
|
|
2
|
+
import { FocusScopeManager } from "./focus-scope-manager.js";
|
|
3
|
+
import { focusable, isFocusable, tabbable } from "tabbable";
|
|
4
|
+
import { on } from "svelte/events";
|
|
5
|
+
import { watch } from "runed";
|
|
6
|
+
export class FocusScope {
|
|
7
|
+
#paused = false;
|
|
8
|
+
#container = null;
|
|
9
|
+
#manager = FocusScopeManager.getInstance();
|
|
10
|
+
#cleanupFns = [];
|
|
11
|
+
#opts;
|
|
12
|
+
constructor(opts) {
|
|
13
|
+
this.#opts = opts;
|
|
14
|
+
}
|
|
15
|
+
get paused() {
|
|
16
|
+
return this.#paused;
|
|
17
|
+
}
|
|
18
|
+
pause() {
|
|
19
|
+
this.#paused = true;
|
|
20
|
+
}
|
|
21
|
+
resume() {
|
|
22
|
+
this.#paused = false;
|
|
23
|
+
}
|
|
24
|
+
#cleanup() {
|
|
25
|
+
for (const fn of this.#cleanupFns) {
|
|
26
|
+
fn();
|
|
27
|
+
}
|
|
28
|
+
this.#cleanupFns = [];
|
|
29
|
+
}
|
|
30
|
+
mount(container) {
|
|
31
|
+
if (this.#container) {
|
|
32
|
+
this.unmount();
|
|
33
|
+
}
|
|
34
|
+
this.#container = container;
|
|
35
|
+
this.#manager.register(this);
|
|
36
|
+
this.#setupEventListeners();
|
|
37
|
+
this.#handleOpenAutoFocus();
|
|
38
|
+
}
|
|
39
|
+
unmount() {
|
|
40
|
+
if (!this.#container)
|
|
41
|
+
return;
|
|
42
|
+
this.#cleanup();
|
|
43
|
+
// handle close auto-focus
|
|
44
|
+
this.#handleCloseAutoFocus();
|
|
45
|
+
this.#manager.unregister(this);
|
|
46
|
+
this.#container = null;
|
|
47
|
+
}
|
|
48
|
+
#handleOpenAutoFocus() {
|
|
49
|
+
if (!this.#container)
|
|
50
|
+
return;
|
|
51
|
+
const event = new CustomEvent("focusScope.onOpenAutoFocus", {
|
|
52
|
+
bubbles: false,
|
|
53
|
+
cancelable: true,
|
|
54
|
+
});
|
|
55
|
+
this.#opts.onOpenAutoFocus.current(event);
|
|
56
|
+
if (!event.defaultPrevented) {
|
|
57
|
+
requestAnimationFrame(() => {
|
|
58
|
+
if (!this.#container)
|
|
59
|
+
return;
|
|
60
|
+
const firstTabbable = this.#getFirstTabbable();
|
|
61
|
+
if (firstTabbable) {
|
|
62
|
+
firstTabbable.focus();
|
|
63
|
+
this.#manager.setFocusMemory(this, firstTabbable);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
this.#container.focus();
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
#handleCloseAutoFocus() {
|
|
72
|
+
const event = new CustomEvent("focusScope.onCloseAutoFocus", {
|
|
73
|
+
bubbles: false,
|
|
74
|
+
cancelable: true,
|
|
75
|
+
});
|
|
76
|
+
this.#opts.onCloseAutoFocus.current(event);
|
|
77
|
+
if (!event.defaultPrevented) {
|
|
78
|
+
// return focus to previously focused element
|
|
79
|
+
const prevFocused = document.activeElement;
|
|
80
|
+
if (prevFocused && prevFocused !== document.body) {
|
|
81
|
+
prevFocused.focus();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
#setupEventListeners() {
|
|
86
|
+
if (!this.#container || !this.#opts.trap.current)
|
|
87
|
+
return;
|
|
88
|
+
const container = this.#container;
|
|
89
|
+
const doc = container.ownerDocument;
|
|
90
|
+
const handleFocus = (e) => {
|
|
91
|
+
if (this.#paused || !this.#manager.isActiveScope(this))
|
|
92
|
+
return;
|
|
93
|
+
const target = e.target;
|
|
94
|
+
if (!target)
|
|
95
|
+
return;
|
|
96
|
+
const isInside = container.contains(target);
|
|
97
|
+
if (isInside) {
|
|
98
|
+
// store last focused element
|
|
99
|
+
this.#manager.setFocusMemory(this, target);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// focus escaped - bring it back
|
|
103
|
+
const lastFocused = this.#manager.getFocusMemory(this);
|
|
104
|
+
if (lastFocused && container.contains(lastFocused) && isFocusable(lastFocused)) {
|
|
105
|
+
e.preventDefault();
|
|
106
|
+
lastFocused.focus();
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// fallback to first tabbable or first focusable or container
|
|
110
|
+
const firstTabbable = this.#getFirstTabbable();
|
|
111
|
+
const firstFocusable = this.#getAllFocusables()[0];
|
|
112
|
+
(firstTabbable || firstFocusable || container).focus();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const handleKeydown = (e) => {
|
|
117
|
+
if (!this.#opts.loop || this.#paused || e.key !== "Tab")
|
|
118
|
+
return;
|
|
119
|
+
if (!this.#manager.isActiveScope(this))
|
|
120
|
+
return;
|
|
121
|
+
const tabbables = this.#getTabbables();
|
|
122
|
+
if (tabbables.length < 2)
|
|
123
|
+
return;
|
|
124
|
+
const first = tabbables[0];
|
|
125
|
+
const last = tabbables[tabbables.length - 1];
|
|
126
|
+
if (!e.shiftKey && doc.activeElement === last) {
|
|
127
|
+
e.preventDefault();
|
|
128
|
+
first.focus();
|
|
129
|
+
}
|
|
130
|
+
else if (e.shiftKey && doc.activeElement === first) {
|
|
131
|
+
e.preventDefault();
|
|
132
|
+
last.focus();
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
this.#cleanupFns.push(on(doc, "focusin", handleFocus, { capture: true }), on(container, "keydown", handleKeydown));
|
|
136
|
+
const observer = new MutationObserver(() => {
|
|
137
|
+
const lastFocused = this.#manager.getFocusMemory(this);
|
|
138
|
+
if (lastFocused && !container.contains(lastFocused)) {
|
|
139
|
+
// last focused element was removed
|
|
140
|
+
const firstTabbable = this.#getFirstTabbable();
|
|
141
|
+
const firstFocusable = this.#getAllFocusables()[0];
|
|
142
|
+
const elementToFocus = firstTabbable || firstFocusable;
|
|
143
|
+
if (elementToFocus) {
|
|
144
|
+
elementToFocus.focus();
|
|
145
|
+
this.#manager.setFocusMemory(this, elementToFocus);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
// no focusable elements left, focus container
|
|
149
|
+
container.focus();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
observer.observe(container, {
|
|
154
|
+
childList: true,
|
|
155
|
+
subtree: true,
|
|
156
|
+
});
|
|
157
|
+
this.#cleanupFns.push(() => observer.disconnect());
|
|
158
|
+
}
|
|
159
|
+
#getTabbables() {
|
|
160
|
+
if (!this.#container)
|
|
161
|
+
return [];
|
|
162
|
+
return tabbable(this.#container, {
|
|
163
|
+
includeContainer: false,
|
|
164
|
+
getShadowRoot: true,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
#getFirstTabbable() {
|
|
168
|
+
const tabbables = this.#getTabbables();
|
|
169
|
+
return tabbables[0] || null;
|
|
170
|
+
}
|
|
171
|
+
#getAllFocusables() {
|
|
172
|
+
if (!this.#container)
|
|
173
|
+
return [];
|
|
174
|
+
return focusable(this.#container, {
|
|
175
|
+
includeContainer: false,
|
|
176
|
+
getShadowRoot: true,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
static use(opts) {
|
|
180
|
+
let scope = null;
|
|
181
|
+
watch([() => opts.ref.current, () => opts.enabled.current], ([ref, enabled]) => {
|
|
182
|
+
if (ref && enabled) {
|
|
183
|
+
if (!scope) {
|
|
184
|
+
scope = new FocusScope(opts);
|
|
185
|
+
}
|
|
186
|
+
scope.mount(ref);
|
|
187
|
+
}
|
|
188
|
+
else if (scope) {
|
|
189
|
+
scope.unmount();
|
|
190
|
+
scope = null;
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
onDestroyEffect(() => {
|
|
194
|
+
scope?.unmount();
|
|
195
|
+
});
|
|
196
|
+
return {
|
|
197
|
+
get props() {
|
|
198
|
+
return {
|
|
199
|
+
tabindex: -1,
|
|
200
|
+
};
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Snippet } from "svelte";
|
|
2
|
-
import type { FocusScopeContainerProps } from "./use-focus-scope.svelte.js";
|
|
3
2
|
import type { EventCallback } from "../../../internal/events.js";
|
|
4
3
|
import type { ReadableBox } from "svelte-toolbelt";
|
|
5
4
|
export type FocusScopeProps = {
|
|
@@ -21,15 +20,11 @@ export type FocusScopeProps = {
|
|
|
21
20
|
trapFocus?: boolean;
|
|
22
21
|
};
|
|
23
22
|
export type FocusScopeImplProps = {
|
|
24
|
-
/**
|
|
25
|
-
* The ID of the focus scope container node.
|
|
26
|
-
*/
|
|
27
|
-
id: string;
|
|
28
23
|
/**
|
|
29
24
|
* The snippet to render the focus scope container with its props.
|
|
30
25
|
*/
|
|
31
26
|
focusScope?: Snippet<[{
|
|
32
|
-
props:
|
|
27
|
+
props: Record<string, unknown>;
|
|
33
28
|
}]>;
|
|
34
29
|
/**
|
|
35
30
|
* When `true` will loop through the tabbable elements in the focus scope.
|
|
@@ -39,5 +34,6 @@ export type FocusScopeImplProps = {
|
|
|
39
34
|
* Whether the content within the focus trap is being force mounted or not.
|
|
40
35
|
*/
|
|
41
36
|
forceMount?: boolean;
|
|
37
|
+
enabled: boolean;
|
|
42
38
|
ref: ReadableBox<HTMLElement | null>;
|
|
43
39
|
} & FocusScopeProps;
|
package/dist/internal/focus.js
CHANGED
|
@@ -34,7 +34,7 @@ export function focusWithoutScroll(element) {
|
|
|
34
34
|
* A utility function that focuses an element.
|
|
35
35
|
*/
|
|
36
36
|
export function focus(element, { select = false } = {}) {
|
|
37
|
-
if (!
|
|
37
|
+
if (!element || !element.focus)
|
|
38
38
|
return;
|
|
39
39
|
const doc = getDocument(element);
|
|
40
40
|
if (doc.activeElement === element)
|
package/dist/internal/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Snippet } from "svelte";
|
|
2
|
-
import type { Box, ReadableBoxedValues, WritableBoxedValues } from "svelte-toolbelt";
|
|
2
|
+
import type { attachRef, Box, ReadableBoxedValues, WritableBoxedValues } from "svelte-toolbelt";
|
|
3
3
|
import type { StyleProperties } from "../shared/index.js";
|
|
4
4
|
export type OnChangeFn<T> = (value: T) => void;
|
|
5
5
|
export type ElementRef = Box<HTMLElement | null>;
|
|
@@ -90,3 +90,4 @@ export type BitsPointerEvent<T extends HTMLElement = HTMLElement> = BitsEvent<Po
|
|
|
90
90
|
export type BitsKeyboardEvent<T extends HTMLElement = HTMLElement> = BitsEvent<KeyboardEvent, T>;
|
|
91
91
|
export type BitsMouseEvent<T extends HTMLElement = HTMLElement> = BitsEvent<MouseEvent, T>;
|
|
92
92
|
export type BitsFocusEvent<T extends HTMLElement = HTMLElement> = BitsEvent<FocusEvent, T>;
|
|
93
|
+
export type RefAttachment<T extends HTMLElement = HTMLElement> = ReturnType<typeof attachRef<T>>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bits-ui",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": "github:huntabyte/bits-ui",
|
|
6
6
|
"funding": "https://github.com/sponsors/huntabyte",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"css.escape": "^1.5.1",
|
|
47
47
|
"esm-env": "^1.1.2",
|
|
48
48
|
"runed": "^0.28.0",
|
|
49
|
-
"svelte-toolbelt": "^0.9.
|
|
49
|
+
"svelte-toolbelt": "^0.9.2",
|
|
50
50
|
"tabbable": "^6.2.0"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export interface FocusScopeAPI {
|
|
2
|
-
id: string;
|
|
3
|
-
paused: boolean;
|
|
4
|
-
pause: () => void;
|
|
5
|
-
resume: () => void;
|
|
6
|
-
isHandlingFocus: boolean;
|
|
7
|
-
}
|
|
8
|
-
export declare function createFocusScopeStack(): {
|
|
9
|
-
add(focusScope: FocusScopeAPI): void;
|
|
10
|
-
remove(focusScope: FocusScopeAPI): void;
|
|
11
|
-
readonly current: FocusScopeAPI[];
|
|
12
|
-
};
|
|
13
|
-
export declare function createFocusScopeAPI(): FocusScopeAPI;
|
|
14
|
-
export declare function removeLinks(items: HTMLElement[]): HTMLElement[];
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { box } from "svelte-toolbelt";
|
|
2
|
-
import { useId } from "../../../internal/use-id.js";
|
|
3
|
-
const focusStack = box([]);
|
|
4
|
-
export function createFocusScopeStack() {
|
|
5
|
-
return {
|
|
6
|
-
add(focusScope) {
|
|
7
|
-
const activeFocusScope = focusStack.current[0];
|
|
8
|
-
if (activeFocusScope && focusScope.id !== activeFocusScope.id) {
|
|
9
|
-
activeFocusScope.pause();
|
|
10
|
-
}
|
|
11
|
-
focusStack.current = removeFromFocusScopeArray(focusStack.current, focusScope);
|
|
12
|
-
focusStack.current.unshift(focusScope);
|
|
13
|
-
},
|
|
14
|
-
remove(focusScope) {
|
|
15
|
-
focusStack.current = removeFromFocusScopeArray(focusStack.current, focusScope);
|
|
16
|
-
focusStack.current[0]?.resume();
|
|
17
|
-
},
|
|
18
|
-
get current() {
|
|
19
|
-
return focusStack.current;
|
|
20
|
-
},
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
export function createFocusScopeAPI() {
|
|
24
|
-
let paused = $state(false);
|
|
25
|
-
let isHandlingFocus = $state(false);
|
|
26
|
-
return {
|
|
27
|
-
id: useId(),
|
|
28
|
-
get paused() {
|
|
29
|
-
return paused;
|
|
30
|
-
},
|
|
31
|
-
get isHandlingFocus() {
|
|
32
|
-
return isHandlingFocus;
|
|
33
|
-
},
|
|
34
|
-
set isHandlingFocus(value) {
|
|
35
|
-
isHandlingFocus = value;
|
|
36
|
-
},
|
|
37
|
-
pause() {
|
|
38
|
-
paused = true;
|
|
39
|
-
},
|
|
40
|
-
resume() {
|
|
41
|
-
paused = false;
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
function removeFromFocusScopeArray(arr, item) {
|
|
46
|
-
return [...arr].filter((i) => i.id !== item.id);
|
|
47
|
-
}
|
|
48
|
-
export function removeLinks(items) {
|
|
49
|
-
return items.filter((item) => item.tagName !== "A");
|
|
50
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { type ReadableBoxedValues } from "svelte-toolbelt";
|
|
2
|
-
import { Context } from "runed";
|
|
3
|
-
import { type EventCallback } from "../../../internal/events.js";
|
|
4
|
-
export type FocusScopeContextValue = {
|
|
5
|
-
ignoreCloseAutoFocus: boolean;
|
|
6
|
-
};
|
|
7
|
-
export declare const FocusScopeContext: Context<FocusScopeContextValue>;
|
|
8
|
-
type UseFocusScopeProps = ReadableBoxedValues<{
|
|
9
|
-
/**
|
|
10
|
-
* ID of the focus scope container node.
|
|
11
|
-
*/
|
|
12
|
-
id: string;
|
|
13
|
-
/**
|
|
14
|
-
* When `true` will loop through the tabbable elements in the focus scope.
|
|
15
|
-
*
|
|
16
|
-
* @defaultValue false
|
|
17
|
-
*/
|
|
18
|
-
loop: boolean;
|
|
19
|
-
/**
|
|
20
|
-
* Whether focus is trapped within the focus scope.
|
|
21
|
-
*
|
|
22
|
-
* @defaultValue false
|
|
23
|
-
*/
|
|
24
|
-
enabled: boolean;
|
|
25
|
-
/**
|
|
26
|
-
* Event handler called when auto-focusing onMount.
|
|
27
|
-
* Can be prevented.
|
|
28
|
-
*/
|
|
29
|
-
onOpenAutoFocus: EventCallback;
|
|
30
|
-
/**
|
|
31
|
-
* Event handler called when auto-focusing onDestroy.
|
|
32
|
-
* Can be prevented.
|
|
33
|
-
*/
|
|
34
|
-
onCloseAutoFocus: EventCallback;
|
|
35
|
-
/**
|
|
36
|
-
* Whether force mount is enabled or not
|
|
37
|
-
*/
|
|
38
|
-
forceMount: boolean;
|
|
39
|
-
ref: HTMLElement | null;
|
|
40
|
-
}>;
|
|
41
|
-
export type FocusScopeContainerProps = {
|
|
42
|
-
id: string;
|
|
43
|
-
tabindex: number;
|
|
44
|
-
onkeydown: EventCallback<KeyboardEvent>;
|
|
45
|
-
};
|
|
46
|
-
export declare function useFocusScope({ id, loop, enabled, onOpenAutoFocus, onCloseAutoFocus, forceMount, ref, }: UseFocusScopeProps): {
|
|
47
|
-
readonly props: FocusScopeContainerProps;
|
|
48
|
-
};
|
|
49
|
-
export {};
|