sb-floating-panel-vue 5.0.0 → 5.0.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/README.md +4 -3
- package/dist/{context.d.ts → composables/useSbFloatingContext.d.ts} +3 -10
- package/dist/sb-floating-panel.es.js +207 -187
- package/dist/sb-floating-panel.umd.js +1 -1
- package/dist/types.d.ts +8 -3
- package/dist/utils/aria.d.ts +5 -0
- package/dist/utils/dom.d.ts +12 -0
- package/dist/{focus.d.ts → utils/navigation.d.ts} +0 -8
- package/package.json +1 -1
- /package/dist/{dev.d.ts → utils/warn.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -153,7 +153,7 @@ The trigger the panel is anchored to.
|
|
|
153
153
|
|
|
154
154
|
| Prop | Type | Default | Description |
|
|
155
155
|
|----------------|-----------------------|----------------|--------------------------------------------------------------------------------------------------------------|
|
|
156
|
-
| `as` | `string \| Component` | `'button'` | Tag or component to render. `button` gets `type="button"`; `a` with `href` and components keep their own semantics; any other tag (also `a` without `href`) gets `role="button"`, `tabindex="0"` and Enter/Space |
|
|
156
|
+
| `as` | `string \| Component` | `'button'` | Tag or component to render. `button` gets `type="button"`; form controls (`input`, `textarea`, `select`), `a` with `href` and components keep their own semantics; any other tag (also `a` without `href`) gets `role="button"`, `tabindex="0"` and Enter/Space. A text field opens the panel on click without closing it, and keeps focus (see [Text field references](#text-field-references)) |
|
|
157
157
|
| `id` | `string` | generated | `id` of the element, used to name the panel |
|
|
158
158
|
| `ariaHaspopup` | `'dialog' \| 'menu' \| 'listbox' \| 'tree' \| 'grid' \| 'true' \| 'false'` | follows `role` | Value of `aria-haspopup`. When unset it follows the `role` of the panel (`alertdialog` gives `dialog`) and is left out for other roles |
|
|
159
159
|
|
|
@@ -172,7 +172,7 @@ The panel, teleported to `body`. It is rendered while open and stays mounted unt
|
|
|
172
172
|
| `transition` | `string \| TransitionProps` | — | Name of a Vue `<Transition>` defined in your CSS, or an object of `<Transition>` props (e.g. Tailwind classes). No animation when unset |
|
|
173
173
|
| `teleport` | `string \| HTMLElement \| false` | `'body'` | Teleport target (selector or element); `false` renders the panel in place |
|
|
174
174
|
| `zIndex` | `number \| string` | — | `z-index` of the positioned wrapper. Use this, not a `z-*` class on the panel (see [Stacking](#stacking)) |
|
|
175
|
-
| `autoFocus` | `boolean` | `true`, `false` with `role="tooltip"
|
|
175
|
+
| `autoFocus` | `boolean` | `true`, `false` with `role="tooltip"`, the `hover` / `focus` trigger or a text field reference | Once positioned, focuses the first `[autofocus]` or focusable element inside, else the panel itself (a menu: its first item; a listbox: its selected option) |
|
|
176
176
|
| `keyboardNavigation` | `boolean` | `true` | Handles the keyboard model of `role="menu"` and `role="listbox"` panels (see [Keyboard](#keyboard)). No effect with other roles |
|
|
177
177
|
|
|
178
178
|
| Event | Description |
|
|
@@ -227,6 +227,7 @@ import type {
|
|
|
227
227
|
- Moving focus outside (e.g. tabbing past the last element) closes it.
|
|
228
228
|
- A container nested inside a panel counts as part of it.
|
|
229
229
|
- **What is up to you.** The library does not implement the keyboard model of `tree` and `grid` panels, nor selection: a listbox option or a menu item does what its `click` handler does (e.g. set `aria-selected` and close the panel). Use valid structure: items directly inside the `menu` / `listbox`, or inside `ul` / `li` with `role="none"`.
|
|
230
|
+
- <a id="text-field-references"></a>**Text field references.** `<SbReference as="input">` (or `textarea`) stays a text field: no `role="button"`, Enter and Space type as usual, a click opens the panel without closing it, and focus stays in the field when the panel opens. The [combobox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/) (autocomplete with focus kept in the field and `aria-activedescendant`) is not supported: with a `menu` or `listbox` panel the arrow keys move focus into the panel, and the console warns about it in development.
|
|
230
231
|
|
|
231
232
|
---
|
|
232
233
|
|
|
@@ -356,7 +357,7 @@ SbContainer ── provides the state ──┬─▶ SbReference
|
|
|
356
357
|
- The lookup follows the component tree, not the DOM, so it keeps working although `SbFloating` is teleported to `body`.
|
|
357
358
|
- They can sit at any depth inside the container slot, also inside your own components.
|
|
358
359
|
- Used outside an `SbContainer`, they throw: `[sb-floating-panel-vue] <SbFloating> must be used inside <SbContainer>.`
|
|
359
|
-
- In development, the console warns about a second `SbReference` or `SbFloating` in the same container
|
|
360
|
+
- In development, the console warns about a second `SbReference` or `SbFloating` in the same container about an `as` component that renders no single root element and about a text field reference of a `menu` or `listbox` panel. The warnings depend on `process.env.NODE_ENV`, which your bundler replaces: they are left out of production builds.
|
|
360
361
|
|
|
361
362
|
### Nested containers
|
|
362
363
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { InjectionKey, Ref } from 'vue';
|
|
2
|
-
import {
|
|
3
|
-
import { NavigableRole } from '
|
|
4
|
-
import {
|
|
2
|
+
import { SbTrigger } from '../types';
|
|
3
|
+
import { NavigableRole } from '../utils/navigation';
|
|
4
|
+
import { FloatingPanel } from './useSbFloatingPanel';
|
|
5
5
|
/**
|
|
6
6
|
* State shared by `SbContainer` with `SbReference` and `SbFloating`.
|
|
7
7
|
*/
|
|
@@ -39,14 +39,7 @@ export interface SbFloatingContext extends FloatingPanel {
|
|
|
39
39
|
/** Registers a nested container; returns the function that unregisters it. */
|
|
40
40
|
registerChild: (child: SbFloatingContext) => () => void;
|
|
41
41
|
}
|
|
42
|
-
/**
|
|
43
|
-
* Panel roles that make the reference a popup trigger, with their `aria-haspopup` value.
|
|
44
|
-
* Panels with these roles are also named after the reference.
|
|
45
|
-
*/
|
|
46
|
-
export declare const POPUP_ROLES: Record<string, 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid'>;
|
|
47
42
|
export declare const sbFloatingContextKey: InjectionKey<SbFloatingContext>;
|
|
48
|
-
/** Element of a template ref: a component gives its instance, whose root element is `$el`. */
|
|
49
|
-
export declare function toElement(el: unknown): HTMLElement | null;
|
|
50
43
|
/** Counts a mounted part of the container, warning about a second one; returns the release. */
|
|
51
44
|
export declare function claimPart(context: SbFloatingContext, part: 'reference' | 'floating', component: string): () => void;
|
|
52
45
|
export declare function useSbFloatingContext(component: string): SbFloatingContext;
|
|
@@ -1,11 +1,34 @@
|
|
|
1
1
|
import { Teleport as e, Transition as t, computed as n, createBlock as r, createCommentVNode as i, createElementBlock as a, createVNode as o, defineComponent as s, inject as c, mergeModels as l, mergeProps as u, nextTick as d, normalizeStyle as f, onBeforeUnmount as p, onMounted as m, onScopeDispose as h, openBlock as g, provide as _, ref as v, renderSlot as y, resolveDynamicComponent as b, toRef as x, toValue as S, unref as C, useAttrs as w, useId as T, useModel as E, watch as D, watchEffect as O, withCtx as k } from "vue";
|
|
2
2
|
import { autoUpdate as A, flip as j, hide as M, limitShift as N, offset as P, shift as F, size as ee, useFloating as te } from "@floating-ui/vue";
|
|
3
|
+
//#region src/utils/warn.ts
|
|
4
|
+
var I = (() => {
|
|
5
|
+
try {
|
|
6
|
+
return process.env.NODE_ENV !== "production";
|
|
7
|
+
} catch {
|
|
8
|
+
return !1;
|
|
9
|
+
}
|
|
10
|
+
})();
|
|
11
|
+
function L(e) {
|
|
12
|
+
I && console.warn(`[sb-floating-panel-vue] ${e}`);
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/composables/useSbFloatingContext.ts
|
|
16
|
+
var R = Symbol("SbFloatingContext");
|
|
17
|
+
function ne(e, t, n) {
|
|
18
|
+
return ++e.parts[t] > 1 && L(`<SbContainer> has more than one <${n}>: they share one open state and position, so only the last one works. Use one SbContainer for each panel.`), () => e.parts[t]--;
|
|
19
|
+
}
|
|
20
|
+
function z(e) {
|
|
21
|
+
let t = c(R, null);
|
|
22
|
+
if (!t) throw Error(`[sb-floating-panel-vue] <${e}> must be used inside <SbContainer>.`);
|
|
23
|
+
return t;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
3
26
|
//#region src/composables/useSbFloatingPanel.ts
|
|
4
|
-
var
|
|
27
|
+
var B = (e) => `${Math.max(0, e)}px`, V = (e, t) => {
|
|
5
28
|
let [n, r] = e.split("-"), i = t && (n === "top" || n === "bottom"), a = r === "start" ? i ? "100%" : "0%" : r === "end" ? i ? "0%" : "100%" : "50%";
|
|
6
29
|
return n === "top" ? `${a} 100%` : n === "bottom" ? `${a} 0%` : n === "left" ? `100% ${a}` : `0% ${a}`;
|
|
7
30
|
};
|
|
8
|
-
function
|
|
31
|
+
function re(e) {
|
|
9
32
|
let t = v(null), r = v(null), i = n(() => {
|
|
10
33
|
let t = S(e.padding), n = S(e.matchWidth), r = S(e.placement).includes("-"), i = j({
|
|
11
34
|
padding: t,
|
|
@@ -20,10 +43,10 @@ function R(e) {
|
|
|
20
43
|
ee({
|
|
21
44
|
padding: t,
|
|
22
45
|
apply({ rects: e, elements: t, availableWidth: r, availableHeight: i, placement: a }) {
|
|
23
|
-
let { style: o } = t.floating, s =
|
|
46
|
+
let { style: o } = t.floating, s = B(e.reference.width);
|
|
24
47
|
o.setProperty("--sbf-reference-width", s);
|
|
25
48
|
let c = getComputedStyle(t.floating).direction === "rtl";
|
|
26
|
-
o.setProperty("--sbf-transform-origin",
|
|
49
|
+
o.setProperty("--sbf-transform-origin", V(a, c)), o.setProperty("--sbf-available-width", B(r)), o.setProperty("--sbf-available-height", B(i)), o.width = n === !0 ? s : "", o.minWidth = n === "min" ? s : "";
|
|
27
50
|
}
|
|
28
51
|
})
|
|
29
52
|
];
|
|
@@ -51,42 +74,8 @@ function R(e) {
|
|
|
51
74
|
};
|
|
52
75
|
}
|
|
53
76
|
//#endregion
|
|
54
|
-
//#region src/
|
|
55
|
-
var
|
|
56
|
-
try {
|
|
57
|
-
return process.env.NODE_ENV !== "production";
|
|
58
|
-
} catch {
|
|
59
|
-
return !1;
|
|
60
|
-
}
|
|
61
|
-
})();
|
|
62
|
-
function B(e) {
|
|
63
|
-
z && console.warn(`[sb-floating-panel-vue] ${e}`);
|
|
64
|
-
}
|
|
65
|
-
//#endregion
|
|
66
|
-
//#region src/context.ts
|
|
67
|
-
var V = {
|
|
68
|
-
dialog: "dialog",
|
|
69
|
-
alertdialog: "dialog",
|
|
70
|
-
menu: "menu",
|
|
71
|
-
listbox: "listbox",
|
|
72
|
-
tree: "tree",
|
|
73
|
-
grid: "grid"
|
|
74
|
-
}, H = Symbol("SbFloatingContext");
|
|
75
|
-
function ne(e) {
|
|
76
|
-
let t = e && typeof e == "object" && "$el" in e ? e.$el : e;
|
|
77
|
-
return t instanceof HTMLElement ? t : null;
|
|
78
|
-
}
|
|
79
|
-
function re(e, t, n) {
|
|
80
|
-
return ++e.parts[t] > 1 && B(`<SbContainer> has more than one <${n}>: they share one open state and position, so only the last one works. Use one SbContainer for each panel.`), () => e.parts[t]--;
|
|
81
|
-
}
|
|
82
|
-
function ie(e) {
|
|
83
|
-
let t = c(H, null);
|
|
84
|
-
if (!t) throw Error(`[sb-floating-panel-vue] <${e}> must be used inside <SbContainer>.`);
|
|
85
|
-
return t;
|
|
86
|
-
}
|
|
87
|
-
//#endregion
|
|
88
|
-
//#region src/focus.ts
|
|
89
|
-
var ae = "data-sbf-focus-guard", U = [
|
|
77
|
+
//#region src/utils/dom.ts
|
|
78
|
+
var ie = "data-sbf-focus-guard", H = [
|
|
90
79
|
"a[href]",
|
|
91
80
|
"button:not([disabled])",
|
|
92
81
|
"input:not([disabled]):not([type=\"hidden\"])",
|
|
@@ -97,34 +86,25 @@ var ae = "data-sbf-focus-guard", U = [
|
|
|
97
86
|
"video[controls]",
|
|
98
87
|
"[tabindex]",
|
|
99
88
|
"[contenteditable=\"true\"]"
|
|
100
|
-
].join(","),
|
|
101
|
-
function
|
|
102
|
-
let t = e
|
|
103
|
-
return
|
|
89
|
+
].join(","), ae = (e) => e.tabIndex >= 0 && !e.hasAttribute("data-sbf-focus-guard") && !e.closest("[inert]") && (e.checkVisibility?.() ?? !0);
|
|
90
|
+
function U(e) {
|
|
91
|
+
let t = e && typeof e == "object" && "$el" in e ? e.$el : e;
|
|
92
|
+
return t instanceof HTMLElement ? t : null;
|
|
104
93
|
}
|
|
105
|
-
function
|
|
106
|
-
|
|
94
|
+
function oe(e) {
|
|
95
|
+
let t = e instanceof Element ? e.closest(H) : null;
|
|
96
|
+
return !!t && !t.closest("[inert]");
|
|
107
97
|
}
|
|
108
|
-
var
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}, oe = (e) => !!e && Object.hasOwn(q, e);
|
|
112
|
-
function se(e, t) {
|
|
113
|
-
return [...e.querySelectorAll(q[t])].filter((n) => n.closest(`[role="${t}"]`) === e && !n.matches(":disabled") && (n.checkVisibility?.() ?? !0));
|
|
98
|
+
var se = (e) => e.matches(H) && !e.closest("[inert]");
|
|
99
|
+
function W(e) {
|
|
100
|
+
return [...e.querySelectorAll(H)].filter(ae);
|
|
114
101
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
close: "ArrowRight"
|
|
118
|
-
} : {
|
|
119
|
-
open: "ArrowRight",
|
|
120
|
-
close: "ArrowLeft"
|
|
121
|
-
};
|
|
122
|
-
function ue(e, t) {
|
|
123
|
-
return K(e.ownerDocument).find((n) => !!(e.compareDocumentPosition(n) & Node.DOCUMENT_POSITION_FOLLOWING) && !e.contains(n) && !t(n));
|
|
102
|
+
function G(e, t) {
|
|
103
|
+
return W(e.ownerDocument).find((n) => !!(e.compareDocumentPosition(n) & Node.DOCUMENT_POSITION_FOLLOWING) && !e.contains(n) && !t(n));
|
|
124
104
|
}
|
|
125
105
|
//#endregion
|
|
126
106
|
//#region src/components/SbContainer.vue?vue&type=script&setup=true&lang.ts
|
|
127
|
-
var
|
|
107
|
+
var K = [], q = /* @__PURE__ */ s({
|
|
128
108
|
inheritAttrs: !1,
|
|
129
109
|
__name: "SbContainer",
|
|
130
110
|
props: /*@__PURE__*/ l({
|
|
@@ -179,15 +159,15 @@ var J = [], Y = /* @__PURE__ */ s({
|
|
|
179
159
|
let d = (e) => {
|
|
180
160
|
clearTimeout(u), !(e && i.disabled) && e !== l.value && (a.value = e);
|
|
181
161
|
}, f = () => d(!0), m = () => d(!1), g = () => d(!l.value), b = (e) => {
|
|
182
|
-
if (
|
|
183
|
-
let t = e.type === "pointerenter";
|
|
162
|
+
if (e.pointerType === "touch" || (O?.hover(e), i.trigger !== "hover")) return;
|
|
163
|
+
let t = e.type === "pointerenter" || k.contains(e.relatedTarget);
|
|
184
164
|
if (clearTimeout(u), t === l.value) return;
|
|
185
165
|
let { delay: n } = i, r = typeof n == "number" ? {
|
|
186
166
|
open: n,
|
|
187
167
|
close: n
|
|
188
168
|
} : n, a = (t ? r.open : r.close) ?? (t ? 300 : 150);
|
|
189
169
|
u = setTimeout(() => d(t), a);
|
|
190
|
-
}, S =
|
|
170
|
+
}, S = re({
|
|
191
171
|
isOpen: l,
|
|
192
172
|
placement: () => i.placement,
|
|
193
173
|
strategy: () => i.strategy,
|
|
@@ -195,7 +175,7 @@ var J = [], Y = /* @__PURE__ */ s({
|
|
|
195
175
|
padding: () => i.padding,
|
|
196
176
|
matchWidth: () => i.matchWidth,
|
|
197
177
|
hideWhenDetached: () => i.hideWhenDetached
|
|
198
|
-
}), w = /* @__PURE__ */ new Set(), O = c(
|
|
178
|
+
}), w = /* @__PURE__ */ new Set(), O = c(R, null), k = {
|
|
199
179
|
...S,
|
|
200
180
|
isOpen: l,
|
|
201
181
|
disabled: x(i, "disabled"),
|
|
@@ -219,14 +199,14 @@ var J = [], Y = /* @__PURE__ */ s({
|
|
|
219
199
|
contains: (e) => e instanceof Node ? S.reference.value?.contains(e) || S.floating.value?.contains(e) ? !0 : [...w].some((t) => t.contains(e)) : !1,
|
|
220
200
|
registerChild: (e) => (w.add(e), () => w.delete(e))
|
|
221
201
|
};
|
|
222
|
-
O && h(O.registerChild(k)), _(
|
|
202
|
+
O && h(O.registerChild(k)), _(R, k), p(() => k.isUnmounting = !0);
|
|
223
203
|
let A = (e) => {
|
|
224
204
|
let t = e.composedPath()[0] ?? e.target;
|
|
225
|
-
return
|
|
205
|
+
return K.findLast((e) => e.contains(t)) ?? K[K.length - 1];
|
|
226
206
|
}, j = (e) => {
|
|
227
207
|
e.key === "Escape" && A(e) === k && (!i.closeOnEscape || e.defaultPrevented || e.isComposing || (e.preventDefault(), m()));
|
|
228
208
|
}, M = (e) => {
|
|
229
|
-
k.contains(e) || (k.skipFocusReturn =
|
|
209
|
+
k.contains(e) || (k.skipFocusReturn = oe(e), m());
|
|
230
210
|
}, N, P = (e) => {
|
|
231
211
|
if (N?.(), !i.closeOnClickOutside) return;
|
|
232
212
|
let t = e.composedPath()[0] ?? e.target;
|
|
@@ -245,8 +225,8 @@ var J = [], Y = /* @__PURE__ */ s({
|
|
|
245
225
|
i.closeOnFocusOut && (k.contains(e.composedPath()[0] ?? e.target) || m());
|
|
246
226
|
};
|
|
247
227
|
return D(l, (e, t, n) => {
|
|
248
|
-
e && typeof document < "u" && (
|
|
249
|
-
document.removeEventListener("keydown", j), document.removeEventListener("pointerdown", P, !0), document.removeEventListener("focusin", F), N?.(),
|
|
228
|
+
e && typeof document < "u" && (K.push(k), document.addEventListener("keydown", j), document.addEventListener("pointerdown", P, !0), document.addEventListener("focusin", F), n(() => {
|
|
229
|
+
document.removeEventListener("keydown", j), document.removeEventListener("pointerdown", P, !0), document.removeEventListener("focusin", F), N?.(), K.splice(K.indexOf(k), 1);
|
|
250
230
|
}));
|
|
251
231
|
}, { immediate: !0 }), D(l, (e) => o(e ? "open" : "close")), D(S.referenceHidden, (e) => {
|
|
252
232
|
e && i.hideWhenDetached === "close" && m();
|
|
@@ -272,7 +252,27 @@ var J = [], Y = /* @__PURE__ */ s({
|
|
|
272
252
|
referenceId: k.referenceId.value
|
|
273
253
|
});
|
|
274
254
|
}
|
|
275
|
-
}),
|
|
255
|
+
}), J = {
|
|
256
|
+
dialog: "dialog",
|
|
257
|
+
alertdialog: "dialog",
|
|
258
|
+
menu: "menu",
|
|
259
|
+
listbox: "listbox",
|
|
260
|
+
tree: "tree",
|
|
261
|
+
grid: "grid"
|
|
262
|
+
}, Y = {
|
|
263
|
+
menu: "[role=\"menuitem\"], [role=\"menuitemcheckbox\"], [role=\"menuitemradio\"]",
|
|
264
|
+
listbox: "[role=\"option\"]"
|
|
265
|
+
}, ce = (e) => !!e && Object.hasOwn(Y, e);
|
|
266
|
+
function le(e, t) {
|
|
267
|
+
return [...e.querySelectorAll(Y[t])].filter((n) => n.closest(`[role="${t}"]`) === e && !n.matches(":disabled") && (n.checkVisibility?.() ?? !0));
|
|
268
|
+
}
|
|
269
|
+
var X = (e) => e instanceof Element && !!e.closest("input, textarea, select, [contenteditable]:not([contenteditable=\"false\"])"), ue = (e) => getComputedStyle(e).direction === "rtl" ? {
|
|
270
|
+
open: "ArrowLeft",
|
|
271
|
+
close: "ArrowRight"
|
|
272
|
+
} : {
|
|
273
|
+
open: "ArrowRight",
|
|
274
|
+
close: "ArrowLeft"
|
|
275
|
+
}, de = /* @__PURE__ */ s({
|
|
276
276
|
__name: "SbReference",
|
|
277
277
|
props: {
|
|
278
278
|
as: { default: "button" },
|
|
@@ -280,59 +280,64 @@ var J = [], Y = /* @__PURE__ */ s({
|
|
|
280
280
|
ariaHaspopup: { default: void 0 }
|
|
281
281
|
},
|
|
282
282
|
setup(e) {
|
|
283
|
-
let t = e, i = w(), a =
|
|
283
|
+
let t = e, i = w(), a = z("SbReference"), { isOpen: o, disabled: s, floatingId: c, floatingRole: l } = a, u = ne(a, "reference", "SbReference"), d = [
|
|
284
|
+
"button",
|
|
285
|
+
"input",
|
|
286
|
+
"textarea",
|
|
287
|
+
"select"
|
|
288
|
+
], f = () => typeof t.as != "string" || d.includes(t.as) || t.as === "a" && i.href != null, h = `${T()}-reference`, _ = n(() => t.id ?? h);
|
|
284
289
|
O(() => {
|
|
285
|
-
a.referenceId.value =
|
|
290
|
+
a.referenceId.value = _.value;
|
|
286
291
|
}), m(() => {
|
|
287
|
-
a.reference.value
|
|
292
|
+
let e = a.reference.value;
|
|
293
|
+
e ? X(e) && a.navigation.value && L(`<SbReference> is a text field with a role="${a.navigation.value}" panel: the arrow keys move focus into the panel, so typing stops there. The combobox pattern (focus kept in the field with aria-activedescendant) is not supported.`) : L("<SbReference> rendered no element, so the panel cannot be positioned: a component in `as` must render a single root element.");
|
|
288
294
|
}), p(() => {
|
|
289
|
-
a.referenceId.value ===
|
|
295
|
+
a.referenceId.value === _.value && (a.referenceId.value = void 0), u(), !a.parts.reference && !a.isUnmounting && a.close();
|
|
290
296
|
});
|
|
291
|
-
let
|
|
292
|
-
a.reference.value =
|
|
293
|
-
},
|
|
294
|
-
a.trigger.value === "click" && a.toggle();
|
|
295
|
-
},
|
|
296
|
-
|
|
297
|
-
}, A = (e) => {
|
|
298
|
-
E() && !a.contains(e.relatedTarget) && a.close();
|
|
299
|
-
}, j = (e) => {
|
|
300
|
-
let t = a.floating.value, n = t && K(t)[0];
|
|
301
|
-
n && (e.preventDefault(), n.focus());
|
|
297
|
+
let v = n(() => t.ariaHaspopup ?? (l.value && Object.hasOwn(J, l.value) ? J[l.value] : void 0)), x = (e) => {
|
|
298
|
+
a.reference.value = U(e);
|
|
299
|
+
}, S = () => {
|
|
300
|
+
a.trigger.value === "click" && (X(a.reference.value) ? a.open() : a.toggle());
|
|
301
|
+
}, E = n(() => l.value === "tooltip"), D = () => a.trigger.value === "hover" || a.trigger.value === "focus", A = () => a.parent?.navigation.value === "menu", j = (e) => {
|
|
302
|
+
D() && !A() && !a.contains(e.relatedTarget) && a.open();
|
|
302
303
|
}, M = (e) => {
|
|
304
|
+
D() && !a.contains(e.relatedTarget) && a.close();
|
|
305
|
+
}, N = (e) => {
|
|
306
|
+
let t = a.floating.value, n = t && W(t)[0];
|
|
307
|
+
n && (e.preventDefault(), n.focus());
|
|
308
|
+
}, P = (e) => {
|
|
303
309
|
if (a.navigation.value && !s.value && a.trigger.value !== "manual") {
|
|
304
|
-
if (
|
|
310
|
+
if (A()) {
|
|
305
311
|
let t = e.currentTarget;
|
|
306
|
-
return e.key ===
|
|
312
|
+
return e.key === ue(t).open ? "first" : void 0;
|
|
307
313
|
}
|
|
308
314
|
return e.key === "ArrowDown" ? "first" : e.key === "ArrowUp" ? "last" : void 0;
|
|
309
315
|
}
|
|
310
|
-
},
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
j(e);
|
|
316
|
+
}, F = (e) => {
|
|
317
|
+
if (e.key === "Tab" && !e.shiftKey && o.value && !A()) {
|
|
318
|
+
N(e);
|
|
314
319
|
return;
|
|
315
320
|
}
|
|
316
|
-
let
|
|
317
|
-
|
|
321
|
+
let t = P(e);
|
|
322
|
+
t ? (e.preventDefault(), a.focusRequest.value = t, a.open()) : f() || e.key !== "Enter" && e.key !== " " || (e.preventDefault(), S());
|
|
318
323
|
};
|
|
319
324
|
return (e, n) => (g(), r(b(t.as), {
|
|
320
|
-
id:
|
|
321
|
-
ref:
|
|
325
|
+
id: _.value,
|
|
326
|
+
ref: x,
|
|
322
327
|
type: t.as === "button" ? "button" : void 0,
|
|
323
|
-
role:
|
|
324
|
-
tabindex:
|
|
328
|
+
role: f() ? void 0 : "button",
|
|
329
|
+
tabindex: f() ? void 0 : C(s) ? -1 : 0,
|
|
325
330
|
disabled: t.as === "button" && C(s) ? !0 : void 0,
|
|
326
331
|
"aria-disabled": t.as !== "button" && C(s) ? "true" : void 0,
|
|
327
|
-
"aria-haspopup":
|
|
328
|
-
"aria-expanded":
|
|
329
|
-
"aria-controls": C(o) && !
|
|
330
|
-
"aria-describedby": C(o) &&
|
|
332
|
+
"aria-haspopup": v.value,
|
|
333
|
+
"aria-expanded": E.value ? void 0 : C(o) ? "true" : "false",
|
|
334
|
+
"aria-controls": C(o) && !E.value ? C(c) : void 0,
|
|
335
|
+
"aria-describedby": C(o) && E.value ? C(c) : void 0,
|
|
331
336
|
"data-sbf-state": C(o) ? "open" : "closed",
|
|
332
|
-
onClick:
|
|
333
|
-
onKeydown:
|
|
334
|
-
onFocusin:
|
|
335
|
-
onFocusout:
|
|
337
|
+
onClick: S,
|
|
338
|
+
onKeydown: F,
|
|
339
|
+
onFocusin: j,
|
|
340
|
+
onFocusout: M,
|
|
336
341
|
onPointerenter: C(a).hover,
|
|
337
342
|
onPointerleave: C(a).hover
|
|
338
343
|
}, {
|
|
@@ -377,24 +382,26 @@ var J = [], Y = /* @__PURE__ */ s({
|
|
|
377
382
|
},
|
|
378
383
|
emits: ["after-enter", "after-leave"],
|
|
379
384
|
setup(s, { emit: c }) {
|
|
380
|
-
let l = s,
|
|
381
|
-
p(
|
|
382
|
-
let { isOpen:
|
|
385
|
+
let l = s, _ = c, x = w(), S = z("SbFloating");
|
|
386
|
+
p(ne(S, "floating", "SbFloating"));
|
|
387
|
+
let { isOpen: T, floatingId: E, referenceId: A, side: j, align: M } = S;
|
|
383
388
|
O(() => {
|
|
384
|
-
|
|
389
|
+
S.floatingRole.value = l.role, S.navigation.value = l.keyboardNavigation && ce(l.role) ? l.role : null;
|
|
390
|
+
}), m(() => {
|
|
391
|
+
l.role === "tooltip" && S.trigger.value === "click" && L("<SbFloating role=\"tooltip\"> opens on click: a tooltip shows on hover and focus. Set trigger=\"hover\" or \"focus\" on SbContainer, or use another role for a panel opened by click.");
|
|
385
392
|
});
|
|
386
|
-
let
|
|
387
|
-
|
|
388
|
-
},
|
|
389
|
-
|
|
390
|
-
},
|
|
391
|
-
...
|
|
393
|
+
let N = v(T.value), P = v(null), F = (e) => {
|
|
394
|
+
S.floating.value = e instanceof HTMLElement ? e : null;
|
|
395
|
+
}, ee = (e) => {
|
|
396
|
+
P.value = U(e);
|
|
397
|
+
}, te = n(() => ({
|
|
398
|
+
...S.floatingStyles.value,
|
|
392
399
|
...l.zIndex === void 0 ? {} : { zIndex: l.zIndex }
|
|
393
|
-
})),
|
|
394
|
-
"aria-labelledby": l.role && Object.hasOwn(
|
|
395
|
-
...
|
|
396
|
-
}),
|
|
397
|
-
[
|
|
400
|
+
})), I = () => ({
|
|
401
|
+
"aria-labelledby": l.role && Object.hasOwn(J, l.role) && x["aria-label"] == null ? A.value : void 0,
|
|
402
|
+
...x
|
|
403
|
+
}), R = n(() => l.autoFocus ?? (l.role !== "tooltip" && S.trigger.value !== "hover" && S.trigger.value !== "focus" && !X(S.reference.value))), B = n(() => T.value && l.teleport !== !1 && l.role !== "tooltip"), V = {
|
|
404
|
+
[ie]: "",
|
|
398
405
|
tabindex: 0,
|
|
399
406
|
style: {
|
|
400
407
|
position: "fixed",
|
|
@@ -402,63 +409,76 @@ var J = [], Y = /* @__PURE__ */ s({
|
|
|
402
409
|
outline: "none",
|
|
403
410
|
pointerEvents: "none"
|
|
404
411
|
}
|
|
405
|
-
},
|
|
412
|
+
}, re = n(() => {
|
|
406
413
|
let { transition: e } = l;
|
|
407
414
|
return e ? typeof e == "string" ? { name: e } : e : { css: !1 };
|
|
408
|
-
}),
|
|
409
|
-
|
|
410
|
-
},
|
|
411
|
-
let t =
|
|
415
|
+
}), H = (e) => e.setAttribute("data-sbf-state", "closed"), ae = () => _("after-enter"), oe = () => {
|
|
416
|
+
T.value || (N.value = !1), _("after-leave");
|
|
417
|
+
}, K = (e) => e instanceof Node && !!P.value?.contains(e), q = (e) => {
|
|
418
|
+
let t = P.value;
|
|
412
419
|
if (!t) return;
|
|
413
|
-
let n =
|
|
420
|
+
let n = W(t);
|
|
414
421
|
((e === "first" ? n[0] : n[n.length - 1]) ?? t).focus({ preventScroll: !0 });
|
|
415
|
-
},
|
|
416
|
-
|
|
417
|
-
},
|
|
418
|
-
if (!
|
|
419
|
-
|
|
422
|
+
}, Y = (e) => {
|
|
423
|
+
K(e.relatedTarget) ? S.reference.value?.focus() : q("first");
|
|
424
|
+
}, de = (e) => {
|
|
425
|
+
if (!K(e.relatedTarget)) {
|
|
426
|
+
q("last");
|
|
420
427
|
return;
|
|
421
428
|
}
|
|
422
|
-
let t =
|
|
423
|
-
((t &&
|
|
424
|
-
}, Y = () => {
|
|
425
|
-
let e = x.navigation.value;
|
|
426
|
-
return N.value && e ? se(N.value, e) : [];
|
|
427
|
-
}, X = (e) => {
|
|
428
|
-
let t = N.value;
|
|
429
|
-
if (!t) return;
|
|
430
|
-
let n = Y();
|
|
431
|
-
(n.find((e) => e.getAttribute("aria-selected") === "true") ?? (e === "last" ? n[n.length - 1] : e ? n[0] : void 0) ?? t.querySelector("[autofocus]") ?? n[0] ?? K(t)[0] ?? t).focus({ preventScroll: !0 });
|
|
429
|
+
let t = S.reference.value;
|
|
430
|
+
((t && G(t, (e) => S.contains(e))) ?? t)?.focus();
|
|
432
431
|
}, Z = () => {
|
|
433
|
-
let e =
|
|
434
|
-
return
|
|
432
|
+
let e = S.navigation.value;
|
|
433
|
+
return P.value && e ? le(P.value, e) : [];
|
|
434
|
+
}, fe = {
|
|
435
|
+
menu: "role=\"menuitem\", \"menuitemcheckbox\" or \"menuitemradio\"",
|
|
436
|
+
listbox: "role=\"option\""
|
|
437
|
+
}, pe = !1, me = (e) => {
|
|
438
|
+
let t = S.navigation.value;
|
|
439
|
+
if (pe || !t) return;
|
|
440
|
+
let n = e.length ? e.every(se) ? null : "has items that cannot take focus, so the arrow keys cannot move to them: make each one a button, or give it tabindex=\"-1\"." : `has no items the arrow keys can move to: items need ${fe[t]} and must not be disabled.`;
|
|
441
|
+
n && (pe = !0, L(`<SbFloating role="${t}"> ${n}`));
|
|
442
|
+
}, he = (e) => {
|
|
443
|
+
let t = P.value;
|
|
444
|
+
if (!t) return;
|
|
445
|
+
let n = Z();
|
|
446
|
+
e && me(n), (n.find((e) => e.getAttribute("aria-selected") === "true") ?? (e === "last" ? n[n.length - 1] : e ? n[0] : void 0) ?? t.querySelector("[autofocus]") ?? n[0] ?? W(t)[0] ?? t).focus({ preventScroll: !0 });
|
|
447
|
+
}, ge = () => {
|
|
448
|
+
let e = S.focusRequest.value;
|
|
449
|
+
return S.focusRequest.value = null, e;
|
|
435
450
|
}, Q = "", $;
|
|
436
451
|
h(() => clearTimeout($));
|
|
437
|
-
let
|
|
452
|
+
let _e = (e, t, n) => {
|
|
438
453
|
Q += n.toLowerCase(), clearTimeout($), $ = setTimeout(() => Q = "", 500);
|
|
439
454
|
let r = [...Q].every((e) => e === Q[0]) ? Q[0] : Q, i = Math.max(t, 0), a = [...e.slice(i), ...e.slice(0, i)];
|
|
440
455
|
return r.length === 1 && t >= 0 && a.push(a.shift()), a.find((e) => e.textContent?.trim().toLowerCase().startsWith(r));
|
|
441
|
-
},
|
|
442
|
-
let t =
|
|
456
|
+
}, ve = (e) => {
|
|
457
|
+
let t = S, n = [t];
|
|
443
458
|
for (; t.parent?.navigation.value === "menu";) t = t.parent, n.push(t);
|
|
444
|
-
let r = t.reference.value, i = e ? r : (r &&
|
|
459
|
+
let r = t.reference.value, i = e ? r : (r && G(r, (e) => t.contains(e))) ?? r;
|
|
445
460
|
for (let e of n) e.skipFocusReturn = !0, e.close();
|
|
446
461
|
i?.focus();
|
|
447
|
-
},
|
|
448
|
-
let t =
|
|
449
|
-
if (!t || e.defaultPrevented || e.isComposing ||
|
|
450
|
-
let n =
|
|
451
|
-
if (
|
|
462
|
+
}, ye = (e) => {
|
|
463
|
+
let t = S.navigation.value;
|
|
464
|
+
if (!t || e.defaultPrevented || e.isComposing || X(e.target)) return;
|
|
465
|
+
let n = Z(), r = n.findIndex((t) => t.contains(e.target)), i = n.length - 1, a = t === "menu", o;
|
|
466
|
+
if ([
|
|
467
|
+
"ArrowDown",
|
|
468
|
+
"ArrowUp",
|
|
469
|
+
"Home",
|
|
470
|
+
"End"
|
|
471
|
+
].includes(e.key) && me(n), e.key === "ArrowDown") o = n[r < i ? r + 1 : a ? 0 : i];
|
|
452
472
|
else if (e.key === "ArrowUp") o = n[r > 0 ? r - 1 : a || r < 0 ? i : 0];
|
|
453
473
|
else if (e.key === "Home") o = n[0];
|
|
454
474
|
else if (e.key === "End") o = n[i];
|
|
455
475
|
else if (e.key === "Tab" && t === "menu") {
|
|
456
|
-
e.preventDefault(),
|
|
476
|
+
e.preventDefault(), ve(e.shiftKey);
|
|
457
477
|
return;
|
|
458
|
-
} else if (t === "menu" &&
|
|
459
|
-
e.preventDefault(),
|
|
478
|
+
} else if (t === "menu" && S.parent?.navigation.value === "menu" && P.value && e.key === ue(P.value).close) {
|
|
479
|
+
e.preventDefault(), S.close();
|
|
460
480
|
return;
|
|
461
|
-
} else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey && (e.key !== " " || Q)) e.preventDefault(), o =
|
|
481
|
+
} else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey && (e.key !== " " || Q)) e.preventDefault(), o = _e(n, r, e.key);
|
|
462
482
|
else if ((e.key === "Enter" || e.key === " ") && e.target === n[r]) {
|
|
463
483
|
let t = n[r];
|
|
464
484
|
if (t.matches("button, a[href], input, summary")) return;
|
|
@@ -467,46 +487,46 @@ var J = [], Y = /* @__PURE__ */ s({
|
|
|
467
487
|
}
|
|
468
488
|
o && (e.preventDefault(), o.focus());
|
|
469
489
|
};
|
|
470
|
-
return D(
|
|
471
|
-
let t =
|
|
472
|
-
if (
|
|
473
|
-
|
|
490
|
+
return D(T, (e) => {
|
|
491
|
+
let t = S.skipFocusReturn;
|
|
492
|
+
if (S.skipFocusReturn = !1, e) {
|
|
493
|
+
N.value = !0;
|
|
474
494
|
return;
|
|
475
495
|
}
|
|
476
496
|
let n = document.activeElement;
|
|
477
|
-
!t && n &&
|
|
478
|
-
}), D(() =>
|
|
497
|
+
!t && n && S.floating.value?.contains(n) && S.reference.value?.focus({ preventScroll: !0 });
|
|
498
|
+
}), D(() => T.value && S.isPositioned.value, async (e) => {
|
|
479
499
|
if (!e) return;
|
|
480
|
-
let t =
|
|
481
|
-
(t ||
|
|
482
|
-
}), D(
|
|
483
|
-
e &&
|
|
500
|
+
let t = ge();
|
|
501
|
+
(t || R.value) && (await d(), he(t));
|
|
502
|
+
}), D(S.focusRequest, (e) => {
|
|
503
|
+
e && T.value && S.isPositioned.value && he(ge());
|
|
484
504
|
}), (n, s) => (g(), r(e, {
|
|
485
505
|
to: l.teleport || "body",
|
|
486
506
|
disabled: l.teleport === !1
|
|
487
|
-
}, [
|
|
507
|
+
}, [N.value ? (g(), a("div", {
|
|
488
508
|
key: 0,
|
|
489
|
-
ref:
|
|
490
|
-
style: f(
|
|
491
|
-
onPointerenter: s[0] ||= (...e) => C(
|
|
492
|
-
onPointerleave: s[1] ||= (...e) => C(
|
|
509
|
+
ref: F,
|
|
510
|
+
style: f(te.value),
|
|
511
|
+
onPointerenter: s[0] ||= (...e) => C(S).hover && C(S).hover(...e),
|
|
512
|
+
onPointerleave: s[1] ||= (...e) => C(S).hover && C(S).hover(...e)
|
|
493
513
|
}, [
|
|
494
|
-
|
|
495
|
-
o(t, u(
|
|
514
|
+
B.value ? (g(), a("span", u({ key: 0 }, V, { onFocus: Y }), null, 16)) : i("", !0),
|
|
515
|
+
o(t, u(re.value, {
|
|
496
516
|
appear: "",
|
|
497
|
-
onBeforeLeave:
|
|
498
|
-
onAfterEnter:
|
|
499
|
-
onAfterLeave:
|
|
517
|
+
onBeforeLeave: H,
|
|
518
|
+
onAfterEnter: ae,
|
|
519
|
+
onAfterLeave: oe
|
|
500
520
|
}), {
|
|
501
|
-
default: k(() => [C(
|
|
502
|
-
id: C(
|
|
503
|
-
ref:
|
|
521
|
+
default: k(() => [C(T) ? (g(), r(b(l.as), u({ key: 0 }, I(), {
|
|
522
|
+
id: C(E),
|
|
523
|
+
ref: ee,
|
|
504
524
|
role: l.role ?? void 0,
|
|
505
|
-
tabindex:
|
|
525
|
+
tabindex: R.value ? -1 : void 0,
|
|
506
526
|
"data-sbf-state": "open",
|
|
507
|
-
"data-sbf-side": C(
|
|
508
|
-
"data-sbf-align": C(
|
|
509
|
-
onKeydown:
|
|
527
|
+
"data-sbf-side": C(j),
|
|
528
|
+
"data-sbf-align": C(M),
|
|
529
|
+
onKeydown: ye
|
|
510
530
|
}), {
|
|
511
531
|
default: k(() => [y(n.$slots, "default")]),
|
|
512
532
|
_: 3
|
|
@@ -519,9 +539,9 @@ var J = [], Y = /* @__PURE__ */ s({
|
|
|
519
539
|
])) : i("", !0)]),
|
|
520
540
|
_: 3
|
|
521
541
|
}, 16),
|
|
522
|
-
|
|
542
|
+
B.value ? (g(), a("span", u({ key: 1 }, V, { onFocus: de }), null, 16)) : i("", !0)
|
|
523
543
|
], 36)) : i("", !0)], 8, ["to", "disabled"]));
|
|
524
544
|
}
|
|
525
545
|
});
|
|
526
546
|
//#endregion
|
|
527
|
-
export {
|
|
547
|
+
export { q as SbContainer, Z as SbFloating, de as SbReference };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("vue"),require("@floating-ui/vue")):typeof define==`function`&&define.amd?define([`exports`,`vue`,`@floating-ui/vue`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.SbFloatingPanelVue={},e.Vue,e.FloatingUIVue))})(this,function(e,t,n){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var r=e=>`${Math.max(0,e)}px`,i=(e,t)=>{let[n,r]=e.split(`-`),i=t&&(n===`top`||n===`bottom`),a=r===`start`?i?`100%`:`0%`:r===`end`?i?`0%`:`100%`:`50%`;return n===`top`?`${a} 100%`:n===`bottom`?`${a} 0%`:n===`left`?`100% ${a}`:`0% ${a}`};function a(e){let a=(0,t.ref)(null),o=(0,t.ref)(null),s=(0,t.computed)(()=>{let a=(0,t.toValue)(e.padding),o=(0,t.toValue)(e.matchWidth),s=(0,t.toValue)(e.placement).includes(`-`),c=(0,n.flip)({padding:a,crossAxis:`alignment`,fallbackAxisSideDirection:`end`}),l=(0,n.shift)({padding:a,limiter:(0,n.limitShift)()}),u=[(0,n.offset)((0,t.toValue)(e.offset)),...s?[c,l]:[l,c],(0,n.size)({padding:a,apply({rects:e,elements:t,availableWidth:n,availableHeight:a,placement:s}){let{style:c}=t.floating,l=r(e.reference.width);c.setProperty(`--sbf-reference-width`,l);let u=getComputedStyle(t.floating).direction===`rtl`;c.setProperty(`--sbf-transform-origin`,i(s,u)),c.setProperty(`--sbf-available-width`,r(n)),c.setProperty(`--sbf-available-height`,r(a)),c.width=o===!0?l:``,c.minWidth=o===`min`?l:``}})];return(0,t.toValue)(e.hideWhenDetached)&&u.push((0,n.hide)({padding:a})),u}),{floatingStyles:c,middlewareData:l,placement:u,isPositioned:d,update:f}=(0,n.useFloating)(a,o,{open:()=>(0,t.toValue)(e.isOpen),placement:()=>(0,t.toValue)(e.placement),strategy:()=>(0,t.toValue)(e.strategy),middleware:s,whileElementsMounted:n.autoUpdate}),p=(0,t.computed)(()=>!!l.value.hide?.referenceHidden);return{reference:a,floating:o,floatingStyles:(0,t.computed)(()=>({...c.value,...p.value?{visibility:`hidden`}:{}})),placement:u,side:(0,t.computed)(()=>u.value.split(`-`)[0]),align:(0,t.computed)(()=>u.value.split(`-`)[1]),isPositioned:d,referenceHidden:p,update:f}}var o=(()=>{try{return process.env.NODE_ENV!==`production`}catch{return!1}})();function s(e){o&&console.warn(`[sb-floating-panel-vue] ${e}`)}var c={dialog:`dialog`,alertdialog:`dialog`,menu:`menu`,listbox:`listbox`,tree:`tree`,grid:`grid`},l=Symbol(`SbFloatingContext`);function u(e){let t=e&&typeof e==`object`&&`$el`in e?e.$el:e;return t instanceof HTMLElement?t:null}function d(e,t,n){return++e.parts[t]>1&&s(`<SbContainer> has more than one <${n}>: they share one open state and position, so only the last one works. Use one SbContainer for each panel.`),()=>e.parts[t]--}function f(e){let n=(0,t.inject)(l,null);if(!n)throw Error(`[sb-floating-panel-vue] <${e}> must be used inside <SbContainer>.`);return n}var p=`data-sbf-focus-guard`,m=[`a[href]`,`button:not([disabled])`,`input:not([disabled]):not([type="hidden"])`,`select:not([disabled])`,`textarea:not([disabled])`,`summary`,`audio[controls]`,`video[controls]`,`[tabindex]`,`[contenteditable="true"]`].join(`,`),h=e=>e.tabIndex>=0&&!e.hasAttribute(`data-sbf-focus-guard`)&&!e.closest(`[inert]`)&&(e.checkVisibility?.()??!0);function g(e){let t=e instanceof Element?e.closest(m):null;return!!t&&!t.closest(`[inert]`)}function _(e){return[...e.querySelectorAll(m)].filter(h)}var v={menu:`[role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"]`,listbox:`[role="option"]`},y=e=>!!e&&Object.hasOwn(v,e);function b(e,t){return[...e.querySelectorAll(v[t])].filter(n=>n.closest(`[role="${t}"]`)===e&&!n.matches(`:disabled`)&&(n.checkVisibility?.()??!0))}var x=e=>e instanceof Element&&!!e.closest(`input, textarea, select, [contenteditable]:not([contenteditable="false"])`),S=e=>getComputedStyle(e).direction===`rtl`?{open:`ArrowLeft`,close:`ArrowRight`}:{open:`ArrowRight`,close:`ArrowLeft`};function C(e,t){return _(e.ownerDocument).find(n=>!!(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING)&&!e.contains(n)&&!t(n))}var w=[],T=(0,t.defineComponent)({inheritAttrs:!1,__name:`SbContainer`,props:(0,t.mergeModels)({placement:{default:`bottom-start`},strategy:{default:`absolute`},offset:{default:8},padding:{default:8},matchWidth:{type:[Boolean,String],default:!1},hideWhenDetached:{type:[Boolean,String],default:!1},defaultOpen:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},trigger:{default:`click`},delay:{default:()=>({open:300,close:150})},closeOnEscape:{type:Boolean,default:!0},closeOnClickOutside:{type:Boolean,default:!0},closeOnFocusOut:{type:Boolean,default:!0}},{open:{type:Boolean,default:void 0},openModifiers:{}}),emits:(0,t.mergeModels)([`open`,`close`],[`update:open`]),setup(e,{expose:n,emit:r}){let i=e,o=(0,t.useModel)(e,`open`),s=r,c=i.defaultOpen,u=(0,t.computed)(()=>!i.disabled&&(o.value??c)),d;(0,t.onScopeDispose)(()=>clearTimeout(d));let f=e=>{clearTimeout(d),!(e&&i.disabled)&&e!==u.value&&(o.value=e)},p=()=>f(!0),m=()=>f(!1),h=()=>f(!u.value),_=e=>{if(i.trigger!==`hover`||e.pointerType===`touch`)return;let t=e.type===`pointerenter`;if(clearTimeout(d),t===u.value)return;let{delay:n}=i,r=typeof n==`number`?{open:n,close:n}:n,a=(t?r.open:r.close)??(t?300:150);d=setTimeout(()=>f(t),a)},v=a({isOpen:u,placement:()=>i.placement,strategy:()=>i.strategy,offset:()=>i.offset,padding:()=>i.padding,matchWidth:()=>i.matchWidth,hideWhenDetached:()=>i.hideWhenDetached}),y=new Set,b=(0,t.inject)(l,null),x={...v,isOpen:u,disabled:(0,t.toRef)(i,`disabled`),trigger:(0,t.toRef)(i,`trigger`),floatingId:`${(0,t.useId)()}-floating`,referenceId:(0,t.ref)(),floatingRole:(0,t.ref)(`dialog`),navigation:(0,t.ref)(null),focusRequest:(0,t.ref)(null),parent:b,skipFocusReturn:!1,parts:{reference:0,floating:0},isUnmounting:!1,open:p,close:m,toggle:h,hover:_,contains:e=>e instanceof Node?v.reference.value?.contains(e)||v.floating.value?.contains(e)?!0:[...y].some(t=>t.contains(e)):!1,registerChild:e=>(y.add(e),()=>y.delete(e))};b&&(0,t.onScopeDispose)(b.registerChild(x)),(0,t.provide)(l,x),(0,t.onBeforeUnmount)(()=>x.isUnmounting=!0);let S=e=>{let t=e.composedPath()[0]??e.target;return w.findLast(e=>e.contains(t))??w[w.length-1]},C=e=>{e.key===`Escape`&&S(e)===x&&(!i.closeOnEscape||e.defaultPrevented||e.isComposing||(e.preventDefault(),m()))},T=e=>{x.contains(e)||(x.skipFocusReturn=g(e),m())},E,D=e=>{if(E?.(),!i.closeOnClickOutside)return;let t=e.composedPath()[0]??e.target;if(e.pointerType!==`touch`){T(t);return}if(x.contains(t))return;let n=e=>{E?.(),T(e.composedPath()[0]??e.target)};document.addEventListener(`click`,n,!0),E=()=>{document.removeEventListener(`click`,n,!0),E=void 0}},O=e=>{i.closeOnFocusOut&&(x.contains(e.composedPath()[0]??e.target)||m())};return(0,t.watch)(u,(e,t,n)=>{e&&typeof document<`u`&&(w.push(x),document.addEventListener(`keydown`,C),document.addEventListener(`pointerdown`,D,!0),document.addEventListener(`focusin`,O),n(()=>{document.removeEventListener(`keydown`,C),document.removeEventListener(`pointerdown`,D,!0),document.removeEventListener(`focusin`,O),E?.(),w.splice(w.indexOf(x),1)}))},{immediate:!0}),(0,t.watch)(u,e=>s(e?`open`:`close`)),(0,t.watch)(v.referenceHidden,e=>{e&&i.hideWhenDetached===`close`&&m()}),(0,t.watch)(()=>i.disabled,e=>{e&&(o.value??c)&&(o.value=!1)}),n({get isOpen(){return u.value},open:p,close:m,toggle:h,update:v.update}),(e,n)=>(0,t.renderSlot)(e.$slots,`default`,{isOpen:u.value,open:p,close:m,toggle:h,placement:(0,t.unref)(v).placement.value,side:(0,t.unref)(v).side.value,align:(0,t.unref)(v).align.value,floatingId:x.floatingId,referenceId:x.referenceId.value})}}),E=(0,t.defineComponent)({__name:`SbReference`,props:{as:{default:`button`},id:{default:void 0},ariaHaspopup:{default:void 0}},setup(e){let n=e,r=(0,t.useAttrs)(),i=f(`SbReference`),{isOpen:a,disabled:o,floatingId:l,floatingRole:p}=i,m=d(i,`reference`,`SbReference`),h=()=>typeof n.as!=`string`||n.as===`button`||n.as===`a`&&r.href!=null,g=`${(0,t.useId)()}-reference`,v=(0,t.computed)(()=>n.id??g);(0,t.watchEffect)(()=>{i.referenceId.value=v.value}),(0,t.onMounted)(()=>{i.reference.value||s("<SbReference> rendered no element, so the panel cannot be positioned: a component in `as` must render a single root element.")}),(0,t.onBeforeUnmount)(()=>{i.referenceId.value===v.value&&(i.referenceId.value=void 0),m(),!i.parts.reference&&!i.isUnmounting&&i.close()});let y=(0,t.computed)(()=>n.ariaHaspopup??(p.value&&Object.hasOwn(c,p.value)?c[p.value]:void 0)),b=e=>{i.reference.value=u(e)},x=()=>{i.trigger.value===`click`&&i.toggle()},C=(0,t.computed)(()=>p.value===`tooltip`),w=()=>i.trigger.value===`hover`||i.trigger.value===`focus`,T=e=>{w()&&!i.contains(e.relatedTarget)&&i.open()},E=e=>{w()&&!i.contains(e.relatedTarget)&&i.close()},D=e=>{let t=i.floating.value,n=t&&_(t)[0];n&&(e.preventDefault(),n.focus())},O=e=>{if(i.navigation.value&&!o.value&&i.trigger.value!==`manual`){if(i.parent?.navigation.value===`menu`){let t=e.currentTarget;return e.key===S(t).open?`first`:void 0}return e.key===`ArrowDown`?`first`:e.key===`ArrowUp`?`last`:void 0}},k=e=>{let t=i.parent?.navigation.value===`menu`;if(e.key===`Tab`&&!e.shiftKey&&a.value&&!t){D(e);return}let n=O(e);n?(e.preventDefault(),i.focusRequest.value=n,i.open()):h()||e.key!==`Enter`&&e.key!==` `||(e.preventDefault(),x())};return(e,r)=>((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(n.as),{id:v.value,ref:b,type:n.as===`button`?`button`:void 0,role:h()?void 0:`button`,tabindex:h()?void 0:(0,t.unref)(o)?-1:0,disabled:n.as===`button`&&(0,t.unref)(o)?!0:void 0,"aria-disabled":n.as!==`button`&&(0,t.unref)(o)?`true`:void 0,"aria-haspopup":y.value,"aria-expanded":C.value?void 0:(0,t.unref)(a)?`true`:`false`,"aria-controls":(0,t.unref)(a)&&!C.value?(0,t.unref)(l):void 0,"aria-describedby":(0,t.unref)(a)&&C.value?(0,t.unref)(l):void 0,"data-sbf-state":(0,t.unref)(a)?`open`:`closed`,onClick:x,onKeydown:k,onFocusin:T,onFocusout:E,onPointerenter:(0,t.unref)(i).hover,onPointerleave:(0,t.unref)(i).hover},{default:(0,t.withCtx)(()=>[(0,t.renderSlot)(e.$slots,`default`)]),_:3},40,[`id`,`type`,`role`,`tabindex`,`disabled`,`aria-disabled`,`aria-haspopup`,`aria-expanded`,`aria-controls`,`aria-describedby`,`data-sbf-state`,`onPointerenter`,`onPointerleave`]))}}),D=(0,t.defineComponent)({inheritAttrs:!1,__name:`SbFloating`,props:{as:{default:`div`},role:{default:`dialog`},transition:{default:void 0},teleport:{type:[String,Boolean],default:`body`},zIndex:{default:void 0},autoFocus:{type:Boolean,default:void 0},keyboardNavigation:{type:Boolean,default:!0}},emits:[`after-enter`,`after-leave`],setup(e,{emit:n}){let r=e,i=n,a=(0,t.useAttrs)(),o=f(`SbFloating`);(0,t.onBeforeUnmount)(d(o,`floating`,`SbFloating`));let{isOpen:s,floatingId:l,referenceId:m,side:h,align:g}=o;(0,t.watchEffect)(()=>{o.floatingRole.value=r.role,o.navigation.value=r.keyboardNavigation&&y(r.role)?r.role:null});let v=(0,t.ref)(s.value),w=(0,t.ref)(null),T=e=>{o.floating.value=e instanceof HTMLElement?e:null},E=e=>{w.value=u(e)},D=(0,t.computed)(()=>({...o.floatingStyles.value,...r.zIndex===void 0?{}:{zIndex:r.zIndex}})),O=()=>({"aria-labelledby":r.role&&Object.hasOwn(c,r.role)&&a[`aria-label`]==null?m.value:void 0,...a}),k=(0,t.computed)(()=>r.autoFocus??(r.role!==`tooltip`&&o.trigger.value!==`hover`&&o.trigger.value!==`focus`)),A=(0,t.computed)(()=>s.value&&r.teleport!==!1&&r.role!==`tooltip`),j={[p]:``,tabindex:0,style:{position:`fixed`,opacity:0,outline:`none`,pointerEvents:`none`}},M=(0,t.computed)(()=>{let{transition:e}=r;return e?typeof e==`string`?{name:e}:e:{css:!1}}),N=e=>e.setAttribute(`data-sbf-state`,`closed`),P=()=>i(`after-enter`),F=()=>{s.value||(v.value=!1),i(`after-leave`)},I=e=>e instanceof Node&&!!w.value?.contains(e),L=e=>{let t=w.value;if(!t)return;let n=_(t);((e===`first`?n[0]:n[n.length-1])??t).focus({preventScroll:!0})},R=e=>{I(e.relatedTarget)?o.reference.value?.focus():L(`first`)},z=e=>{if(!I(e.relatedTarget)){L(`last`);return}let t=o.reference.value;((t&&C(t,e=>o.contains(e)))??t)?.focus()},B=()=>{let e=o.navigation.value;return w.value&&e?b(w.value,e):[]},V=e=>{let t=w.value;if(!t)return;let n=B();(n.find(e=>e.getAttribute(`aria-selected`)===`true`)??(e===`last`?n[n.length-1]:e?n[0]:void 0)??t.querySelector(`[autofocus]`)??n[0]??_(t)[0]??t).focus({preventScroll:!0})},H=()=>{let e=o.focusRequest.value;return o.focusRequest.value=null,e},U=``,W;(0,t.onScopeDispose)(()=>clearTimeout(W));let G=(e,t,n)=>{U+=n.toLowerCase(),clearTimeout(W),W=setTimeout(()=>U=``,500);let r=[...U].every(e=>e===U[0])?U[0]:U,i=Math.max(t,0),a=[...e.slice(i),...e.slice(0,i)];return r.length===1&&t>=0&&a.push(a.shift()),a.find(e=>e.textContent?.trim().toLowerCase().startsWith(r))},K=e=>{let t=o,n=[t];for(;t.parent?.navigation.value===`menu`;)t=t.parent,n.push(t);let r=t.reference.value,i=e?r:(r&&C(r,e=>t.contains(e)))??r;for(let e of n)e.skipFocusReturn=!0,e.close();i?.focus()},q=e=>{let t=o.navigation.value;if(!t||e.defaultPrevented||e.isComposing||x(e.target))return;let n=B(),r=n.findIndex(t=>t.contains(e.target)),i=n.length-1,a=t===`menu`,s;if(e.key===`ArrowDown`)s=n[r<i?r+1:a?0:i];else if(e.key===`ArrowUp`)s=n[r>0?r-1:a||r<0?i:0];else if(e.key===`Home`)s=n[0];else if(e.key===`End`)s=n[i];else if(e.key===`Tab`&&t===`menu`){e.preventDefault(),K(e.shiftKey);return}else if(t===`menu`&&o.parent?.navigation.value===`menu`&&w.value&&e.key===S(w.value).close){e.preventDefault(),o.close();return}else if(e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&(e.key!==` `||U))e.preventDefault(),s=G(n,r,e.key);else if((e.key===`Enter`||e.key===` `)&&e.target===n[r]){let t=n[r];if(t.matches(`button, a[href], input, summary`))return;e.preventDefault(),t.getAttribute(`aria-disabled`)!==`true`&&t.click();return}s&&(e.preventDefault(),s.focus())};return(0,t.watch)(s,e=>{let t=o.skipFocusReturn;if(o.skipFocusReturn=!1,e){v.value=!0;return}let n=document.activeElement;!t&&n&&o.floating.value?.contains(n)&&o.reference.value?.focus({preventScroll:!0})}),(0,t.watch)(()=>s.value&&o.isPositioned.value,async e=>{if(!e)return;let n=H();(n||k.value)&&(await(0,t.nextTick)(),V(n))}),(0,t.watch)(o.focusRequest,e=>{e&&s.value&&o.isPositioned.value&&V(H())}),(e,n)=>((0,t.openBlock)(),(0,t.createBlock)(t.Teleport,{to:r.teleport||`body`,disabled:r.teleport===!1},[v.value?((0,t.openBlock)(),(0,t.createElementBlock)(`div`,{key:0,ref:T,style:(0,t.normalizeStyle)(D.value),onPointerenter:n[0]||=(...e)=>(0,t.unref)(o).hover&&(0,t.unref)(o).hover(...e),onPointerleave:n[1]||=(...e)=>(0,t.unref)(o).hover&&(0,t.unref)(o).hover(...e)},[A.value?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,(0,t.mergeProps)({key:0},j,{onFocus:R}),null,16)):(0,t.createCommentVNode)(``,!0),(0,t.createVNode)(t.Transition,(0,t.mergeProps)(M.value,{appear:``,onBeforeLeave:N,onAfterEnter:P,onAfterLeave:F}),{default:(0,t.withCtx)(()=>[(0,t.unref)(s)?((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(r.as),(0,t.mergeProps)({key:0},O(),{id:(0,t.unref)(l),ref:E,role:r.role??void 0,tabindex:k.value?-1:void 0,"data-sbf-state":`open`,"data-sbf-side":(0,t.unref)(h),"data-sbf-align":(0,t.unref)(g),onKeydown:q}),{default:(0,t.withCtx)(()=>[(0,t.renderSlot)(e.$slots,`default`)]),_:3},16,[`id`,`role`,`tabindex`,`data-sbf-side`,`data-sbf-align`])):(0,t.createCommentVNode)(``,!0)]),_:3},16),A.value?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,(0,t.mergeProps)({key:1},j,{onFocus:z}),null,16)):(0,t.createCommentVNode)(``,!0)],36)):(0,t.createCommentVNode)(``,!0)],8,[`to`,`disabled`]))}});e.SbContainer=T,e.SbFloating=D,e.SbReference=E});
|
|
1
|
+
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("vue"),require("@floating-ui/vue")):typeof define==`function`&&define.amd?define([`exports`,`vue`,`@floating-ui/vue`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.SbFloatingPanelVue={},e.Vue,e.FloatingUIVue))})(this,function(e,t,n){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var r=(()=>{try{return process.env.NODE_ENV!==`production`}catch{return!1}})();function i(e){r&&console.warn(`[sb-floating-panel-vue] ${e}`)}var a=Symbol(`SbFloatingContext`);function o(e,t,n){return++e.parts[t]>1&&i(`<SbContainer> has more than one <${n}>: they share one open state and position, so only the last one works. Use one SbContainer for each panel.`),()=>e.parts[t]--}function s(e){let n=(0,t.inject)(a,null);if(!n)throw Error(`[sb-floating-panel-vue] <${e}> must be used inside <SbContainer>.`);return n}var c=e=>`${Math.max(0,e)}px`,l=(e,t)=>{let[n,r]=e.split(`-`),i=t&&(n===`top`||n===`bottom`),a=r===`start`?i?`100%`:`0%`:r===`end`?i?`0%`:`100%`:`50%`;return n===`top`?`${a} 100%`:n===`bottom`?`${a} 0%`:n===`left`?`100% ${a}`:`0% ${a}`};function u(e){let r=(0,t.ref)(null),i=(0,t.ref)(null),a=(0,t.computed)(()=>{let r=(0,t.toValue)(e.padding),i=(0,t.toValue)(e.matchWidth),a=(0,t.toValue)(e.placement).includes(`-`),o=(0,n.flip)({padding:r,crossAxis:`alignment`,fallbackAxisSideDirection:`end`}),s=(0,n.shift)({padding:r,limiter:(0,n.limitShift)()}),u=[(0,n.offset)((0,t.toValue)(e.offset)),...a?[o,s]:[s,o],(0,n.size)({padding:r,apply({rects:e,elements:t,availableWidth:n,availableHeight:r,placement:a}){let{style:o}=t.floating,s=c(e.reference.width);o.setProperty(`--sbf-reference-width`,s);let u=getComputedStyle(t.floating).direction===`rtl`;o.setProperty(`--sbf-transform-origin`,l(a,u)),o.setProperty(`--sbf-available-width`,c(n)),o.setProperty(`--sbf-available-height`,c(r)),o.width=i===!0?s:``,o.minWidth=i===`min`?s:``}})];return(0,t.toValue)(e.hideWhenDetached)&&u.push((0,n.hide)({padding:r})),u}),{floatingStyles:o,middlewareData:s,placement:u,isPositioned:d,update:f}=(0,n.useFloating)(r,i,{open:()=>(0,t.toValue)(e.isOpen),placement:()=>(0,t.toValue)(e.placement),strategy:()=>(0,t.toValue)(e.strategy),middleware:a,whileElementsMounted:n.autoUpdate}),p=(0,t.computed)(()=>!!s.value.hide?.referenceHidden);return{reference:r,floating:i,floatingStyles:(0,t.computed)(()=>({...o.value,...p.value?{visibility:`hidden`}:{}})),placement:u,side:(0,t.computed)(()=>u.value.split(`-`)[0]),align:(0,t.computed)(()=>u.value.split(`-`)[1]),isPositioned:d,referenceHidden:p,update:f}}var d=`data-sbf-focus-guard`,f=[`a[href]`,`button:not([disabled])`,`input:not([disabled]):not([type="hidden"])`,`select:not([disabled])`,`textarea:not([disabled])`,`summary`,`audio[controls]`,`video[controls]`,`[tabindex]`,`[contenteditable="true"]`].join(`,`),p=e=>e.tabIndex>=0&&!e.hasAttribute(`data-sbf-focus-guard`)&&!e.closest(`[inert]`)&&(e.checkVisibility?.()??!0);function m(e){let t=e&&typeof e==`object`&&`$el`in e?e.$el:e;return t instanceof HTMLElement?t:null}function h(e){let t=e instanceof Element?e.closest(f):null;return!!t&&!t.closest(`[inert]`)}var g=e=>e.matches(f)&&!e.closest(`[inert]`);function _(e){return[...e.querySelectorAll(f)].filter(p)}function v(e,t){return _(e.ownerDocument).find(n=>!!(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING)&&!e.contains(n)&&!t(n))}var y=[],b=(0,t.defineComponent)({inheritAttrs:!1,__name:`SbContainer`,props:(0,t.mergeModels)({placement:{default:`bottom-start`},strategy:{default:`absolute`},offset:{default:8},padding:{default:8},matchWidth:{type:[Boolean,String],default:!1},hideWhenDetached:{type:[Boolean,String],default:!1},defaultOpen:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},trigger:{default:`click`},delay:{default:()=>({open:300,close:150})},closeOnEscape:{type:Boolean,default:!0},closeOnClickOutside:{type:Boolean,default:!0},closeOnFocusOut:{type:Boolean,default:!0}},{open:{type:Boolean,default:void 0},openModifiers:{}}),emits:(0,t.mergeModels)([`open`,`close`],[`update:open`]),setup(e,{expose:n,emit:r}){let i=e,o=(0,t.useModel)(e,`open`),s=r,c=i.defaultOpen,l=(0,t.computed)(()=>!i.disabled&&(o.value??c)),d;(0,t.onScopeDispose)(()=>clearTimeout(d));let f=e=>{clearTimeout(d),!(e&&i.disabled)&&e!==l.value&&(o.value=e)},p=()=>f(!0),m=()=>f(!1),g=()=>f(!l.value),_=e=>{if(e.pointerType===`touch`||(x?.hover(e),i.trigger!==`hover`))return;let t=e.type===`pointerenter`||S.contains(e.relatedTarget);if(clearTimeout(d),t===l.value)return;let{delay:n}=i,r=typeof n==`number`?{open:n,close:n}:n,a=(t?r.open:r.close)??(t?300:150);d=setTimeout(()=>f(t),a)},v=u({isOpen:l,placement:()=>i.placement,strategy:()=>i.strategy,offset:()=>i.offset,padding:()=>i.padding,matchWidth:()=>i.matchWidth,hideWhenDetached:()=>i.hideWhenDetached}),b=new Set,x=(0,t.inject)(a,null),S={...v,isOpen:l,disabled:(0,t.toRef)(i,`disabled`),trigger:(0,t.toRef)(i,`trigger`),floatingId:`${(0,t.useId)()}-floating`,referenceId:(0,t.ref)(),floatingRole:(0,t.ref)(`dialog`),navigation:(0,t.ref)(null),focusRequest:(0,t.ref)(null),parent:x,skipFocusReturn:!1,parts:{reference:0,floating:0},isUnmounting:!1,open:p,close:m,toggle:g,hover:_,contains:e=>e instanceof Node?v.reference.value?.contains(e)||v.floating.value?.contains(e)?!0:[...b].some(t=>t.contains(e)):!1,registerChild:e=>(b.add(e),()=>b.delete(e))};x&&(0,t.onScopeDispose)(x.registerChild(S)),(0,t.provide)(a,S),(0,t.onBeforeUnmount)(()=>S.isUnmounting=!0);let C=e=>{let t=e.composedPath()[0]??e.target;return y.findLast(e=>e.contains(t))??y[y.length-1]},w=e=>{e.key===`Escape`&&C(e)===S&&(!i.closeOnEscape||e.defaultPrevented||e.isComposing||(e.preventDefault(),m()))},T=e=>{S.contains(e)||(S.skipFocusReturn=h(e),m())},E,D=e=>{if(E?.(),!i.closeOnClickOutside)return;let t=e.composedPath()[0]??e.target;if(e.pointerType!==`touch`){T(t);return}if(S.contains(t))return;let n=e=>{E?.(),T(e.composedPath()[0]??e.target)};document.addEventListener(`click`,n,!0),E=()=>{document.removeEventListener(`click`,n,!0),E=void 0}},O=e=>{i.closeOnFocusOut&&(S.contains(e.composedPath()[0]??e.target)||m())};return(0,t.watch)(l,(e,t,n)=>{e&&typeof document<`u`&&(y.push(S),document.addEventListener(`keydown`,w),document.addEventListener(`pointerdown`,D,!0),document.addEventListener(`focusin`,O),n(()=>{document.removeEventListener(`keydown`,w),document.removeEventListener(`pointerdown`,D,!0),document.removeEventListener(`focusin`,O),E?.(),y.splice(y.indexOf(S),1)}))},{immediate:!0}),(0,t.watch)(l,e=>s(e?`open`:`close`)),(0,t.watch)(v.referenceHidden,e=>{e&&i.hideWhenDetached===`close`&&m()}),(0,t.watch)(()=>i.disabled,e=>{e&&(o.value??c)&&(o.value=!1)}),n({get isOpen(){return l.value},open:p,close:m,toggle:g,update:v.update}),(e,n)=>(0,t.renderSlot)(e.$slots,`default`,{isOpen:l.value,open:p,close:m,toggle:g,placement:(0,t.unref)(v).placement.value,side:(0,t.unref)(v).side.value,align:(0,t.unref)(v).align.value,floatingId:S.floatingId,referenceId:S.referenceId.value})}}),x={dialog:`dialog`,alertdialog:`dialog`,menu:`menu`,listbox:`listbox`,tree:`tree`,grid:`grid`},S={menu:`[role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"]`,listbox:`[role="option"]`},C=e=>!!e&&Object.hasOwn(S,e);function w(e,t){return[...e.querySelectorAll(S[t])].filter(n=>n.closest(`[role="${t}"]`)===e&&!n.matches(`:disabled`)&&(n.checkVisibility?.()??!0))}var T=e=>e instanceof Element&&!!e.closest(`input, textarea, select, [contenteditable]:not([contenteditable="false"])`),E=e=>getComputedStyle(e).direction===`rtl`?{open:`ArrowLeft`,close:`ArrowRight`}:{open:`ArrowRight`,close:`ArrowLeft`},D=(0,t.defineComponent)({__name:`SbReference`,props:{as:{default:`button`},id:{default:void 0},ariaHaspopup:{default:void 0}},setup(e){let n=e,r=(0,t.useAttrs)(),a=s(`SbReference`),{isOpen:c,disabled:l,floatingId:u,floatingRole:d}=a,f=o(a,`reference`,`SbReference`),p=[`button`,`input`,`textarea`,`select`],h=()=>typeof n.as!=`string`||p.includes(n.as)||n.as===`a`&&r.href!=null,g=`${(0,t.useId)()}-reference`,v=(0,t.computed)(()=>n.id??g);(0,t.watchEffect)(()=>{a.referenceId.value=v.value}),(0,t.onMounted)(()=>{let e=a.reference.value;e?T(e)&&a.navigation.value&&i(`<SbReference> is a text field with a role="${a.navigation.value}" panel: the arrow keys move focus into the panel, so typing stops there. The combobox pattern (focus kept in the field with aria-activedescendant) is not supported.`):i("<SbReference> rendered no element, so the panel cannot be positioned: a component in `as` must render a single root element.")}),(0,t.onBeforeUnmount)(()=>{a.referenceId.value===v.value&&(a.referenceId.value=void 0),f(),!a.parts.reference&&!a.isUnmounting&&a.close()});let y=(0,t.computed)(()=>n.ariaHaspopup??(d.value&&Object.hasOwn(x,d.value)?x[d.value]:void 0)),b=e=>{a.reference.value=m(e)},S=()=>{a.trigger.value===`click`&&(T(a.reference.value)?a.open():a.toggle())},C=(0,t.computed)(()=>d.value===`tooltip`),w=()=>a.trigger.value===`hover`||a.trigger.value===`focus`,D=()=>a.parent?.navigation.value===`menu`,O=e=>{w()&&!D()&&!a.contains(e.relatedTarget)&&a.open()},k=e=>{w()&&!a.contains(e.relatedTarget)&&a.close()},A=e=>{let t=a.floating.value,n=t&&_(t)[0];n&&(e.preventDefault(),n.focus())},j=e=>{if(a.navigation.value&&!l.value&&a.trigger.value!==`manual`){if(D()){let t=e.currentTarget;return e.key===E(t).open?`first`:void 0}return e.key===`ArrowDown`?`first`:e.key===`ArrowUp`?`last`:void 0}},M=e=>{if(e.key===`Tab`&&!e.shiftKey&&c.value&&!D()){A(e);return}let t=j(e);t?(e.preventDefault(),a.focusRequest.value=t,a.open()):h()||e.key!==`Enter`&&e.key!==` `||(e.preventDefault(),S())};return(e,r)=>((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(n.as),{id:v.value,ref:b,type:n.as===`button`?`button`:void 0,role:h()?void 0:`button`,tabindex:h()?void 0:(0,t.unref)(l)?-1:0,disabled:n.as===`button`&&(0,t.unref)(l)?!0:void 0,"aria-disabled":n.as!==`button`&&(0,t.unref)(l)?`true`:void 0,"aria-haspopup":y.value,"aria-expanded":C.value?void 0:(0,t.unref)(c)?`true`:`false`,"aria-controls":(0,t.unref)(c)&&!C.value?(0,t.unref)(u):void 0,"aria-describedby":(0,t.unref)(c)&&C.value?(0,t.unref)(u):void 0,"data-sbf-state":(0,t.unref)(c)?`open`:`closed`,onClick:S,onKeydown:M,onFocusin:O,onFocusout:k,onPointerenter:(0,t.unref)(a).hover,onPointerleave:(0,t.unref)(a).hover},{default:(0,t.withCtx)(()=>[(0,t.renderSlot)(e.$slots,`default`)]),_:3},40,[`id`,`type`,`role`,`tabindex`,`disabled`,`aria-disabled`,`aria-haspopup`,`aria-expanded`,`aria-controls`,`aria-describedby`,`data-sbf-state`,`onPointerenter`,`onPointerleave`]))}}),O=(0,t.defineComponent)({inheritAttrs:!1,__name:`SbFloating`,props:{as:{default:`div`},role:{default:`dialog`},transition:{default:void 0},teleport:{type:[String,Boolean],default:`body`},zIndex:{default:void 0},autoFocus:{type:Boolean,default:void 0},keyboardNavigation:{type:Boolean,default:!0}},emits:[`after-enter`,`after-leave`],setup(e,{emit:n}){let r=e,a=n,c=(0,t.useAttrs)(),l=s(`SbFloating`);(0,t.onBeforeUnmount)(o(l,`floating`,`SbFloating`));let{isOpen:u,floatingId:f,referenceId:p,side:h,align:y}=l;(0,t.watchEffect)(()=>{l.floatingRole.value=r.role,l.navigation.value=r.keyboardNavigation&&C(r.role)?r.role:null}),(0,t.onMounted)(()=>{r.role===`tooltip`&&l.trigger.value===`click`&&i(`<SbFloating role="tooltip"> opens on click: a tooltip shows on hover and focus. Set trigger="hover" or "focus" on SbContainer, or use another role for a panel opened by click.`)});let b=(0,t.ref)(u.value),S=(0,t.ref)(null),D=e=>{l.floating.value=e instanceof HTMLElement?e:null},O=e=>{S.value=m(e)},k=(0,t.computed)(()=>({...l.floatingStyles.value,...r.zIndex===void 0?{}:{zIndex:r.zIndex}})),A=()=>({"aria-labelledby":r.role&&Object.hasOwn(x,r.role)&&c[`aria-label`]==null?p.value:void 0,...c}),j=(0,t.computed)(()=>r.autoFocus??(r.role!==`tooltip`&&l.trigger.value!==`hover`&&l.trigger.value!==`focus`&&!T(l.reference.value))),M=(0,t.computed)(()=>u.value&&r.teleport!==!1&&r.role!==`tooltip`),N={[d]:``,tabindex:0,style:{position:`fixed`,opacity:0,outline:`none`,pointerEvents:`none`}},P=(0,t.computed)(()=>{let{transition:e}=r;return e?typeof e==`string`?{name:e}:e:{css:!1}}),F=e=>e.setAttribute(`data-sbf-state`,`closed`),I=()=>a(`after-enter`),L=()=>{u.value||(b.value=!1),a(`after-leave`)},R=e=>e instanceof Node&&!!S.value?.contains(e),z=e=>{let t=S.value;if(!t)return;let n=_(t);((e===`first`?n[0]:n[n.length-1])??t).focus({preventScroll:!0})},B=e=>{R(e.relatedTarget)?l.reference.value?.focus():z(`first`)},V=e=>{if(!R(e.relatedTarget)){z(`last`);return}let t=l.reference.value;((t&&v(t,e=>l.contains(e)))??t)?.focus()},H=()=>{let e=l.navigation.value;return S.value&&e?w(S.value,e):[]},U={menu:`role="menuitem", "menuitemcheckbox" or "menuitemradio"`,listbox:`role="option"`},W=!1,G=e=>{let t=l.navigation.value;if(W||!t)return;let n=e.length?e.every(g)?null:`has items that cannot take focus, so the arrow keys cannot move to them: make each one a button, or give it tabindex="-1".`:`has no items the arrow keys can move to: items need ${U[t]} and must not be disabled.`;n&&(W=!0,i(`<SbFloating role="${t}"> ${n}`))},K=e=>{let t=S.value;if(!t)return;let n=H();e&&G(n),(n.find(e=>e.getAttribute(`aria-selected`)===`true`)??(e===`last`?n[n.length-1]:e?n[0]:void 0)??t.querySelector(`[autofocus]`)??n[0]??_(t)[0]??t).focus({preventScroll:!0})},q=()=>{let e=l.focusRequest.value;return l.focusRequest.value=null,e},J=``,Y;(0,t.onScopeDispose)(()=>clearTimeout(Y));let X=(e,t,n)=>{J+=n.toLowerCase(),clearTimeout(Y),Y=setTimeout(()=>J=``,500);let r=[...J].every(e=>e===J[0])?J[0]:J,i=Math.max(t,0),a=[...e.slice(i),...e.slice(0,i)];return r.length===1&&t>=0&&a.push(a.shift()),a.find(e=>e.textContent?.trim().toLowerCase().startsWith(r))},Z=e=>{let t=l,n=[t];for(;t.parent?.navigation.value===`menu`;)t=t.parent,n.push(t);let r=t.reference.value,i=e?r:(r&&v(r,e=>t.contains(e)))??r;for(let e of n)e.skipFocusReturn=!0,e.close();i?.focus()},Q=e=>{let t=l.navigation.value;if(!t||e.defaultPrevented||e.isComposing||T(e.target))return;let n=H(),r=n.findIndex(t=>t.contains(e.target)),i=n.length-1,a=t===`menu`,o;if([`ArrowDown`,`ArrowUp`,`Home`,`End`].includes(e.key)&&G(n),e.key===`ArrowDown`)o=n[r<i?r+1:a?0:i];else if(e.key===`ArrowUp`)o=n[r>0?r-1:a||r<0?i:0];else if(e.key===`Home`)o=n[0];else if(e.key===`End`)o=n[i];else if(e.key===`Tab`&&t===`menu`){e.preventDefault(),Z(e.shiftKey);return}else if(t===`menu`&&l.parent?.navigation.value===`menu`&&S.value&&e.key===E(S.value).close){e.preventDefault(),l.close();return}else if(e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&(e.key!==` `||J))e.preventDefault(),o=X(n,r,e.key);else if((e.key===`Enter`||e.key===` `)&&e.target===n[r]){let t=n[r];if(t.matches(`button, a[href], input, summary`))return;e.preventDefault(),t.getAttribute(`aria-disabled`)!==`true`&&t.click();return}o&&(e.preventDefault(),o.focus())};return(0,t.watch)(u,e=>{let t=l.skipFocusReturn;if(l.skipFocusReturn=!1,e){b.value=!0;return}let n=document.activeElement;!t&&n&&l.floating.value?.contains(n)&&l.reference.value?.focus({preventScroll:!0})}),(0,t.watch)(()=>u.value&&l.isPositioned.value,async e=>{if(!e)return;let n=q();(n||j.value)&&(await(0,t.nextTick)(),K(n))}),(0,t.watch)(l.focusRequest,e=>{e&&u.value&&l.isPositioned.value&&K(q())}),(e,n)=>((0,t.openBlock)(),(0,t.createBlock)(t.Teleport,{to:r.teleport||`body`,disabled:r.teleport===!1},[b.value?((0,t.openBlock)(),(0,t.createElementBlock)(`div`,{key:0,ref:D,style:(0,t.normalizeStyle)(k.value),onPointerenter:n[0]||=(...e)=>(0,t.unref)(l).hover&&(0,t.unref)(l).hover(...e),onPointerleave:n[1]||=(...e)=>(0,t.unref)(l).hover&&(0,t.unref)(l).hover(...e)},[M.value?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,(0,t.mergeProps)({key:0},N,{onFocus:B}),null,16)):(0,t.createCommentVNode)(``,!0),(0,t.createVNode)(t.Transition,(0,t.mergeProps)(P.value,{appear:``,onBeforeLeave:F,onAfterEnter:I,onAfterLeave:L}),{default:(0,t.withCtx)(()=>[(0,t.unref)(u)?((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(r.as),(0,t.mergeProps)({key:0},A(),{id:(0,t.unref)(f),ref:O,role:r.role??void 0,tabindex:j.value?-1:void 0,"data-sbf-state":`open`,"data-sbf-side":(0,t.unref)(h),"data-sbf-align":(0,t.unref)(y),onKeydown:Q}),{default:(0,t.withCtx)(()=>[(0,t.renderSlot)(e.$slots,`default`)]),_:3},16,[`id`,`role`,`tabindex`,`data-sbf-side`,`data-sbf-align`])):(0,t.createCommentVNode)(``,!0)]),_:3},16),M.value?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,(0,t.mergeProps)({key:1},N,{onFocus:V}),null,16)):(0,t.createCommentVNode)(``,!0)],36)):(0,t.createCommentVNode)(``,!0)],8,[`to`,`disabled`]))}});e.SbContainer=b,e.SbFloating=O,e.SbReference=D});
|
package/dist/types.d.ts
CHANGED
|
@@ -153,11 +153,15 @@ export interface SbReferenceProps {
|
|
|
153
153
|
/**
|
|
154
154
|
* HTML tag or component to render.
|
|
155
155
|
* - `button` gets `type="button"`.
|
|
156
|
-
* - `a` with `href` and components are left as
|
|
157
|
-
* its own focusable element (e.g. the
|
|
156
|
+
* - Form controls (`input`, `textarea`, `select`), `a` with `href` and components are left as
|
|
157
|
+
* they are: a component is expected to render its own focusable element (e.g. the
|
|
158
|
+
* `<Button>` of your design system).
|
|
158
159
|
* - Any other tag (including `a` without `href`) gets `role="button"`, `tabindex="0"` and
|
|
159
160
|
* Enter/Space handling.
|
|
160
161
|
*
|
|
162
|
+
* A text field (`input`, `textarea`) opens the panel on click without closing it, and keeps
|
|
163
|
+
* focus when the panel opens. The combobox pattern (`aria-activedescendant`) is not supported.
|
|
164
|
+
*
|
|
161
165
|
* When disabled, `button` gets `disabled`; anything else gets `aria-disabled="true"`.
|
|
162
166
|
* @default 'button'
|
|
163
167
|
*/
|
|
@@ -219,7 +223,8 @@ export interface SbFloatingProps {
|
|
|
219
223
|
* Moves focus inside the panel once it is positioned: to the first element with
|
|
220
224
|
* `autofocus`, otherwise to the first focusable element, otherwise to the panel itself.
|
|
221
225
|
* With `role="menu"` or `listbox` the first item gets focus (a listbox: its selected option).
|
|
222
|
-
* @default true, false with `role="tooltip"
|
|
226
|
+
* @default true, false with `role="tooltip"`, with the `hover` and `focus` triggers and with
|
|
227
|
+
* a text field reference
|
|
223
228
|
*/
|
|
224
229
|
autoFocus?: boolean;
|
|
225
230
|
/**
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Marks the hidden elements placed around a teleported panel to keep the Tab order. */
|
|
2
|
+
export declare const FOCUS_GUARD_ATTRIBUTE = "data-sbf-focus-guard";
|
|
3
|
+
/** Element of a template ref: a component gives its instance, whose root element is `$el`. */
|
|
4
|
+
export declare function toElement(el: unknown): HTMLElement | null;
|
|
5
|
+
/** Whether pressing `target` moves focus to it (or to a focusable ancestor). */
|
|
6
|
+
export declare function isFocusable(target: EventTarget | null): boolean;
|
|
7
|
+
/** Whether `el` itself can take focus, with Tab or from script. */
|
|
8
|
+
export declare const canFocus: (el: Element) => boolean;
|
|
9
|
+
/** Elements reachable with Tab inside `root`, in document order (focus guards excluded). */
|
|
10
|
+
export declare function getTabbables(root: ParentNode): HTMLElement[];
|
|
11
|
+
/** First element reachable with Tab after `anchor` in document order, skipping the excluded ones. */
|
|
12
|
+
export declare function getNextTabbable(anchor: Element, exclude: (el: HTMLElement) => boolean): HTMLElement | undefined;
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/** Marks the hidden elements placed around a teleported panel to keep the Tab order. */
|
|
2
|
-
export declare const FOCUS_GUARD_ATTRIBUTE = "data-sbf-focus-guard";
|
|
3
|
-
/** Whether pressing `target` moves focus to it (or to a focusable ancestor). */
|
|
4
|
-
export declare function isFocusable(target: EventTarget | null): boolean;
|
|
5
|
-
/** Elements reachable with Tab inside `root`, in document order (focus guards excluded). */
|
|
6
|
-
export declare function getTabbables(root: ParentNode): HTMLElement[];
|
|
7
1
|
/** Panel roles whose keyboard model the components handle, with the selector of their items. */
|
|
8
2
|
export declare const ITEM_SELECTORS: {
|
|
9
3
|
readonly menu: "[role=\"menuitem\"], [role=\"menuitemcheckbox\"], [role=\"menuitemradio\"]";
|
|
@@ -20,5 +14,3 @@ export declare const submenuKeys: (el: Element) => {
|
|
|
20
14
|
open: string;
|
|
21
15
|
close: string;
|
|
22
16
|
};
|
|
23
|
-
/** First element reachable with Tab after `anchor` in document order, skipping the excluded ones. */
|
|
24
|
-
export declare function getNextTabbable(anchor: Element, exclude: (el: HTMLElement) => boolean): HTMLElement | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sb-floating-panel-vue",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.2",
|
|
4
4
|
"description": "Headless Vue 3 floating panel components (dropdowns, popovers, menus) powered by @floating-ui/vue: positioning, accessibility and open state handled, zero CSS shipped.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vue",
|
|
File without changes
|