sb-floating-panel-vue 5.0.1 → 5.0.3
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/sb-floating-panel.es.js +168 -146
- package/dist/sb-floating-panel.umd.js +1 -1
- package/dist/types.d.ts +8 -3
- package/dist/utils/dom.d.ts +2 -0
- package/package.json +1 -1
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, about a text field reference of a `menu` or `listbox` panel, about a `tooltip` opened by click, and, the first time the arrow keys look for them, about a `menu` or `listbox` with no items or with items that cannot take focus. 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,34 +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
|
-
import { autoUpdate as A, flip as j, hide as M, limitShift as N, offset as P, shift as F, size as ee, useFloating as
|
|
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
3
|
//#region src/utils/warn.ts
|
|
4
|
-
var
|
|
4
|
+
var I = (() => {
|
|
5
5
|
try {
|
|
6
6
|
return process.env.NODE_ENV !== "production";
|
|
7
7
|
} catch {
|
|
8
8
|
return !1;
|
|
9
9
|
}
|
|
10
10
|
})();
|
|
11
|
-
function
|
|
12
|
-
|
|
11
|
+
function L(e) {
|
|
12
|
+
I && console.warn(`[sb-floating-panel-vue] ${e}`);
|
|
13
13
|
}
|
|
14
14
|
//#endregion
|
|
15
15
|
//#region src/composables/useSbFloatingContext.ts
|
|
16
|
-
var
|
|
17
|
-
function
|
|
18
|
-
return ++e.parts[t] > 1 &&
|
|
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
19
|
}
|
|
20
|
-
function
|
|
21
|
-
let t = c(
|
|
20
|
+
function z(e) {
|
|
21
|
+
let t = c(R, null);
|
|
22
22
|
if (!t) throw Error(`[sb-floating-panel-vue] <${e}> must be used inside <SbContainer>.`);
|
|
23
23
|
return t;
|
|
24
24
|
}
|
|
25
25
|
//#endregion
|
|
26
26
|
//#region src/composables/useSbFloatingPanel.ts
|
|
27
|
-
var
|
|
27
|
+
var B = (e) => `${Math.max(0, e)}px`, V = (e, t) => {
|
|
28
28
|
let [n, r] = e.split("-"), i = t && (n === "top" || n === "bottom"), a = r === "start" ? i ? "100%" : "0%" : r === "end" ? i ? "0%" : "100%" : "50%";
|
|
29
29
|
return n === "top" ? `${a} 100%` : n === "bottom" ? `${a} 0%` : n === "left" ? `100% ${a}` : `0% ${a}`;
|
|
30
30
|
};
|
|
31
|
-
function
|
|
31
|
+
function re(e) {
|
|
32
32
|
let t = v(null), r = v(null), i = n(() => {
|
|
33
33
|
let t = S(e.padding), n = S(e.matchWidth), r = S(e.placement).includes("-"), i = j({
|
|
34
34
|
padding: t,
|
|
@@ -43,15 +43,15 @@ function ne(e) {
|
|
|
43
43
|
ee({
|
|
44
44
|
padding: t,
|
|
45
45
|
apply({ rects: e, elements: t, availableWidth: r, availableHeight: i, placement: a }) {
|
|
46
|
-
let { style: o } = t.floating, s =
|
|
46
|
+
let { style: o } = t.floating, s = B(e.reference.width);
|
|
47
47
|
o.setProperty("--sbf-reference-width", s);
|
|
48
48
|
let c = getComputedStyle(t.floating).direction === "rtl";
|
|
49
|
-
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 : "";
|
|
50
50
|
}
|
|
51
51
|
})
|
|
52
52
|
];
|
|
53
53
|
return S(e.hideWhenDetached) && o.push(M({ padding: t })), o;
|
|
54
|
-
}), { floatingStyles: a, middlewareData: o, placement: s, isPositioned: c, update: l } =
|
|
54
|
+
}), { floatingStyles: a, middlewareData: o, placement: s, isPositioned: c, update: l } = te(t, r, {
|
|
55
55
|
open: () => S(e.isOpen),
|
|
56
56
|
placement: () => S(e.placement),
|
|
57
57
|
strategy: () => S(e.strategy),
|
|
@@ -75,7 +75,7 @@ function ne(e) {
|
|
|
75
75
|
}
|
|
76
76
|
//#endregion
|
|
77
77
|
//#region src/utils/dom.ts
|
|
78
|
-
var
|
|
78
|
+
var ie = "data-sbf-focus-guard", H = [
|
|
79
79
|
"a[href]",
|
|
80
80
|
"button:not([disabled])",
|
|
81
81
|
"input:not([disabled]):not([type=\"hidden\"])",
|
|
@@ -86,24 +86,25 @@ var re = "data-sbf-focus-guard", U = [
|
|
|
86
86
|
"video[controls]",
|
|
87
87
|
"[tabindex]",
|
|
88
88
|
"[contenteditable=\"true\"]"
|
|
89
|
-
].join(","),
|
|
90
|
-
function
|
|
89
|
+
].join(","), ae = (e) => e.tabIndex >= 0 && !e.hasAttribute("data-sbf-focus-guard") && !e.closest("[inert]") && (e.checkVisibility?.() ?? !0);
|
|
90
|
+
function U(e) {
|
|
91
91
|
let t = e && typeof e == "object" && "$el" in e ? e.$el : e;
|
|
92
92
|
return t instanceof HTMLElement ? t : null;
|
|
93
93
|
}
|
|
94
|
-
function
|
|
95
|
-
let t = e instanceof Element ? e.closest(
|
|
94
|
+
function oe(e) {
|
|
95
|
+
let t = e instanceof Element ? e.closest(H) : null;
|
|
96
96
|
return !!t && !t.closest("[inert]");
|
|
97
97
|
}
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
var se = (e) => e.matches(H) && !e.closest("[inert]");
|
|
99
|
+
function W(e) {
|
|
100
|
+
return [...e.querySelectorAll(H)].filter(ae);
|
|
100
101
|
}
|
|
101
|
-
function
|
|
102
|
-
return
|
|
102
|
+
function G(e, t) {
|
|
103
|
+
return W(e.ownerDocument).find((n) => !!(e.compareDocumentPosition(n) & Node.DOCUMENT_POSITION_FOLLOWING) && !e.contains(n) && !t(n));
|
|
103
104
|
}
|
|
104
105
|
//#endregion
|
|
105
106
|
//#region src/components/SbContainer.vue?vue&type=script&setup=true&lang.ts
|
|
106
|
-
var
|
|
107
|
+
var K = [], q = /* @__PURE__ */ s({
|
|
107
108
|
inheritAttrs: !1,
|
|
108
109
|
__name: "SbContainer",
|
|
109
110
|
props: /*@__PURE__*/ l({
|
|
@@ -166,7 +167,7 @@ var q = [], oe = /* @__PURE__ */ s({
|
|
|
166
167
|
close: n
|
|
167
168
|
} : n, a = (t ? r.open : r.close) ?? (t ? 300 : 150);
|
|
168
169
|
u = setTimeout(() => d(t), a);
|
|
169
|
-
}, S =
|
|
170
|
+
}, S = re({
|
|
170
171
|
isOpen: l,
|
|
171
172
|
placement: () => i.placement,
|
|
172
173
|
strategy: () => i.strategy,
|
|
@@ -174,7 +175,7 @@ var q = [], oe = /* @__PURE__ */ s({
|
|
|
174
175
|
padding: () => i.padding,
|
|
175
176
|
matchWidth: () => i.matchWidth,
|
|
176
177
|
hideWhenDetached: () => i.hideWhenDetached
|
|
177
|
-
}), w = /* @__PURE__ */ new Set(), O = c(
|
|
178
|
+
}), w = /* @__PURE__ */ new Set(), O = c(R, null), k = {
|
|
178
179
|
...S,
|
|
179
180
|
isOpen: l,
|
|
180
181
|
disabled: x(i, "disabled"),
|
|
@@ -198,14 +199,14 @@ var q = [], oe = /* @__PURE__ */ s({
|
|
|
198
199
|
contains: (e) => e instanceof Node ? S.reference.value?.contains(e) || S.floating.value?.contains(e) ? !0 : [...w].some((t) => t.contains(e)) : !1,
|
|
199
200
|
registerChild: (e) => (w.add(e), () => w.delete(e))
|
|
200
201
|
};
|
|
201
|
-
O && h(O.registerChild(k)), _(
|
|
202
|
+
O && h(O.registerChild(k)), _(R, k), p(() => k.isUnmounting = !0);
|
|
202
203
|
let A = (e) => {
|
|
203
204
|
let t = e.composedPath()[0] ?? e.target;
|
|
204
|
-
return
|
|
205
|
+
return K.findLast((e) => e.contains(t)) ?? K[K.length - 1];
|
|
205
206
|
}, j = (e) => {
|
|
206
207
|
e.key === "Escape" && A(e) === k && (!i.closeOnEscape || e.defaultPrevented || e.isComposing || (e.preventDefault(), m()));
|
|
207
208
|
}, M = (e) => {
|
|
208
|
-
k.contains(e) || (k.skipFocusReturn =
|
|
209
|
+
k.contains(e) || (k.skipFocusReturn = oe(e), m());
|
|
209
210
|
}, N, P = (e) => {
|
|
210
211
|
if (N?.(), !i.closeOnClickOutside) return;
|
|
211
212
|
let t = e.composedPath()[0] ?? e.target;
|
|
@@ -224,8 +225,8 @@ var q = [], oe = /* @__PURE__ */ s({
|
|
|
224
225
|
i.closeOnFocusOut && (k.contains(e.composedPath()[0] ?? e.target) || m());
|
|
225
226
|
};
|
|
226
227
|
return D(l, (e, t, n) => {
|
|
227
|
-
e && typeof document < "u" && (
|
|
228
|
-
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);
|
|
229
230
|
}));
|
|
230
231
|
}, { immediate: !0 }), D(l, (e) => o(e ? "open" : "close")), D(S.referenceHidden, (e) => {
|
|
231
232
|
e && i.hideWhenDetached === "close" && m();
|
|
@@ -261,17 +262,17 @@ var q = [], oe = /* @__PURE__ */ s({
|
|
|
261
262
|
}, Y = {
|
|
262
263
|
menu: "[role=\"menuitem\"], [role=\"menuitemcheckbox\"], [role=\"menuitemradio\"]",
|
|
263
264
|
listbox: "[role=\"option\"]"
|
|
264
|
-
},
|
|
265
|
-
function
|
|
265
|
+
}, ce = (e) => !!e && Object.hasOwn(Y, e);
|
|
266
|
+
function le(e, t) {
|
|
266
267
|
return [...e.querySelectorAll(Y[t])].filter((n) => n.closest(`[role="${t}"]`) === e && !n.matches(":disabled") && (n.checkVisibility?.() ?? !0));
|
|
267
268
|
}
|
|
268
|
-
var
|
|
269
|
+
var X = (e) => e instanceof Element && !!e.closest("input, textarea, select, [contenteditable]:not([contenteditable=\"false\"])"), ue = (e) => getComputedStyle(e).direction === "rtl" ? {
|
|
269
270
|
open: "ArrowLeft",
|
|
270
271
|
close: "ArrowRight"
|
|
271
272
|
} : {
|
|
272
273
|
open: "ArrowRight",
|
|
273
274
|
close: "ArrowLeft"
|
|
274
|
-
},
|
|
275
|
+
}, de = /* @__PURE__ */ s({
|
|
275
276
|
__name: "SbReference",
|
|
276
277
|
props: {
|
|
277
278
|
as: { default: "button" },
|
|
@@ -279,58 +280,64 @@ var le = (e) => e instanceof Element && !!e.closest("input, textarea, select, [c
|
|
|
279
280
|
ariaHaspopup: { default: void 0 }
|
|
280
281
|
},
|
|
281
282
|
setup(e) {
|
|
282
|
-
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);
|
|
283
289
|
O(() => {
|
|
284
|
-
a.referenceId.value =
|
|
290
|
+
a.referenceId.value = _.value;
|
|
285
291
|
}), m(() => {
|
|
286
|
-
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.");
|
|
287
294
|
}), p(() => {
|
|
288
|
-
a.referenceId.value ===
|
|
295
|
+
a.referenceId.value === _.value && (a.referenceId.value = void 0), u(), !a.parts.reference && !a.isUnmounting && a.close();
|
|
289
296
|
});
|
|
290
|
-
let
|
|
291
|
-
a.reference.value =
|
|
292
|
-
},
|
|
293
|
-
a.trigger.value === "click" && a.toggle();
|
|
294
|
-
},
|
|
295
|
-
|
|
296
|
-
}, j = (e) => {
|
|
297
|
-
E() && !a.contains(e.relatedTarget) && a.close();
|
|
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();
|
|
298
303
|
}, M = (e) => {
|
|
299
|
-
|
|
300
|
-
n && (e.preventDefault(), n.focus());
|
|
304
|
+
D() && !a.contains(e.relatedTarget) && a.close();
|
|
301
305
|
}, N = (e) => {
|
|
306
|
+
let t = a.floating.value, n = t && W(t)[0];
|
|
307
|
+
n && (e.preventDefault(), n.focus());
|
|
308
|
+
}, P = (e) => {
|
|
302
309
|
if (a.navigation.value && !s.value && a.trigger.value !== "manual") {
|
|
303
|
-
if (
|
|
310
|
+
if (A()) {
|
|
304
311
|
let t = e.currentTarget;
|
|
305
312
|
return e.key === ue(t).open ? "first" : void 0;
|
|
306
313
|
}
|
|
307
314
|
return e.key === "ArrowDown" ? "first" : e.key === "ArrowUp" ? "last" : void 0;
|
|
308
315
|
}
|
|
309
|
-
},
|
|
310
|
-
if (e.key === "Tab" && !e.shiftKey && o.value && !
|
|
311
|
-
|
|
316
|
+
}, F = (e) => {
|
|
317
|
+
if (e.key === "Tab" && !e.shiftKey && o.value && !A()) {
|
|
318
|
+
N(e);
|
|
312
319
|
return;
|
|
313
320
|
}
|
|
314
|
-
let t =
|
|
315
|
-
t ? (e.preventDefault(), a.focusRequest.value = t, a.open()) :
|
|
321
|
+
let t = P(e);
|
|
322
|
+
t ? (e.preventDefault(), a.focusRequest.value = t, a.open()) : f() || e.key !== "Enter" && e.key !== " " || (e.preventDefault(), S());
|
|
316
323
|
};
|
|
317
324
|
return (e, n) => (g(), r(b(t.as), {
|
|
318
|
-
id:
|
|
319
|
-
ref:
|
|
325
|
+
id: _.value,
|
|
326
|
+
ref: x,
|
|
320
327
|
type: t.as === "button" ? "button" : void 0,
|
|
321
|
-
role:
|
|
322
|
-
tabindex:
|
|
328
|
+
role: f() ? void 0 : "button",
|
|
329
|
+
tabindex: f() ? void 0 : C(s) ? -1 : 0,
|
|
323
330
|
disabled: t.as === "button" && C(s) ? !0 : void 0,
|
|
324
331
|
"aria-disabled": t.as !== "button" && C(s) ? "true" : void 0,
|
|
325
|
-
"aria-haspopup":
|
|
326
|
-
"aria-expanded":
|
|
327
|
-
"aria-controls": C(o) && !
|
|
328
|
-
"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,
|
|
329
336
|
"data-sbf-state": C(o) ? "open" : "closed",
|
|
330
|
-
onClick:
|
|
331
|
-
onKeydown:
|
|
332
|
-
onFocusin:
|
|
333
|
-
onFocusout:
|
|
337
|
+
onClick: S,
|
|
338
|
+
onKeydown: F,
|
|
339
|
+
onFocusin: j,
|
|
340
|
+
onFocusout: M,
|
|
334
341
|
onPointerenter: C(a).hover,
|
|
335
342
|
onPointerleave: C(a).hover
|
|
336
343
|
}, {
|
|
@@ -375,24 +382,26 @@ var le = (e) => e instanceof Element && !!e.closest("input, textarea, select, [c
|
|
|
375
382
|
},
|
|
376
383
|
emits: ["after-enter", "after-leave"],
|
|
377
384
|
setup(s, { emit: c }) {
|
|
378
|
-
let l = s,
|
|
379
|
-
p(
|
|
380
|
-
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;
|
|
381
388
|
O(() => {
|
|
382
|
-
|
|
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.");
|
|
383
392
|
});
|
|
384
|
-
let
|
|
385
|
-
|
|
386
|
-
},
|
|
387
|
-
|
|
388
|
-
},
|
|
389
|
-
...
|
|
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,
|
|
390
399
|
...l.zIndex === void 0 ? {} : { zIndex: l.zIndex }
|
|
391
400
|
})), I = () => ({
|
|
392
|
-
"aria-labelledby": l.role && Object.hasOwn(J, l.role) &&
|
|
393
|
-
...
|
|
394
|
-
}),
|
|
395
|
-
[
|
|
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]: "",
|
|
396
405
|
tabindex: 0,
|
|
397
406
|
style: {
|
|
398
407
|
position: "fixed",
|
|
@@ -400,63 +409,76 @@ var le = (e) => e instanceof Element && !!e.closest("input, textarea, select, [c
|
|
|
400
409
|
outline: "none",
|
|
401
410
|
pointerEvents: "none"
|
|
402
411
|
}
|
|
403
|
-
},
|
|
412
|
+
}, re = n(() => {
|
|
404
413
|
let { transition: e } = l;
|
|
405
414
|
return e ? typeof e == "string" ? { name: e } : e : { css: !1 };
|
|
406
|
-
}),
|
|
407
|
-
|
|
408
|
-
},
|
|
409
|
-
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;
|
|
410
419
|
if (!t) return;
|
|
411
|
-
let n =
|
|
420
|
+
let n = W(t);
|
|
412
421
|
((e === "first" ? n[0] : n[n.length - 1]) ?? t).focus({ preventScroll: !0 });
|
|
413
|
-
},
|
|
414
|
-
|
|
415
|
-
},
|
|
416
|
-
if (!
|
|
417
|
-
|
|
422
|
+
}, Y = (e) => {
|
|
423
|
+
K(e.relatedTarget) ? S.reference.value?.focus() : q("first");
|
|
424
|
+
}, de = (e) => {
|
|
425
|
+
if (!K(e.relatedTarget)) {
|
|
426
|
+
q("last");
|
|
418
427
|
return;
|
|
419
428
|
}
|
|
420
|
-
let t =
|
|
421
|
-
((t &&
|
|
422
|
-
}, Y = () => {
|
|
423
|
-
let e = x.navigation.value;
|
|
424
|
-
return N.value && e ? ce(N.value, e) : [];
|
|
425
|
-
}, X = (e) => {
|
|
426
|
-
let t = N.value;
|
|
427
|
-
if (!t) return;
|
|
428
|
-
let n = Y();
|
|
429
|
-
(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();
|
|
430
431
|
}, Z = () => {
|
|
431
|
-
let e =
|
|
432
|
-
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;
|
|
433
450
|
}, Q = "", $;
|
|
434
451
|
h(() => clearTimeout($));
|
|
435
|
-
let
|
|
452
|
+
let _e = (e, t, n) => {
|
|
436
453
|
Q += n.toLowerCase(), clearTimeout($), $ = setTimeout(() => Q = "", 500);
|
|
437
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)];
|
|
438
455
|
return r.length === 1 && t >= 0 && a.push(a.shift()), a.find((e) => e.textContent?.trim().toLowerCase().startsWith(r));
|
|
439
|
-
},
|
|
440
|
-
let t =
|
|
456
|
+
}, ve = (e) => {
|
|
457
|
+
let t = S, n = [t];
|
|
441
458
|
for (; t.parent?.navigation.value === "menu";) t = t.parent, n.push(t);
|
|
442
|
-
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;
|
|
443
460
|
for (let e of n) e.skipFocusReturn = !0, e.close();
|
|
444
461
|
i?.focus();
|
|
445
|
-
},
|
|
446
|
-
let t =
|
|
447
|
-
if (!t || e.defaultPrevented || e.isComposing ||
|
|
448
|
-
let n =
|
|
449
|
-
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];
|
|
450
472
|
else if (e.key === "ArrowUp") o = n[r > 0 ? r - 1 : a || r < 0 ? i : 0];
|
|
451
473
|
else if (e.key === "Home") o = n[0];
|
|
452
474
|
else if (e.key === "End") o = n[i];
|
|
453
475
|
else if (e.key === "Tab" && t === "menu") {
|
|
454
|
-
e.preventDefault(),
|
|
476
|
+
e.preventDefault(), ve(e.shiftKey);
|
|
455
477
|
return;
|
|
456
|
-
} else if (t === "menu" &&
|
|
457
|
-
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();
|
|
458
480
|
return;
|
|
459
|
-
} 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);
|
|
460
482
|
else if ((e.key === "Enter" || e.key === " ") && e.target === n[r]) {
|
|
461
483
|
let t = n[r];
|
|
462
484
|
if (t.matches("button, a[href], input, summary")) return;
|
|
@@ -465,46 +487,46 @@ var le = (e) => e instanceof Element && !!e.closest("input, textarea, select, [c
|
|
|
465
487
|
}
|
|
466
488
|
o && (e.preventDefault(), o.focus());
|
|
467
489
|
};
|
|
468
|
-
return D(
|
|
469
|
-
let t =
|
|
470
|
-
if (
|
|
471
|
-
|
|
490
|
+
return D(T, (e) => {
|
|
491
|
+
let t = S.skipFocusReturn;
|
|
492
|
+
if (S.skipFocusReturn = !1, e) {
|
|
493
|
+
N.value = !0;
|
|
472
494
|
return;
|
|
473
495
|
}
|
|
474
496
|
let n = document.activeElement;
|
|
475
|
-
!t && n &&
|
|
476
|
-
}), D(() =>
|
|
497
|
+
!t && n && S.floating.value?.contains(n) && S.reference.value?.focus({ preventScroll: !0 });
|
|
498
|
+
}), D(() => T.value && S.isPositioned.value, async (e) => {
|
|
477
499
|
if (!e) return;
|
|
478
|
-
let t =
|
|
479
|
-
(t ||
|
|
480
|
-
}), D(
|
|
481
|
-
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());
|
|
482
504
|
}), (n, s) => (g(), r(e, {
|
|
483
505
|
to: l.teleport || "body",
|
|
484
506
|
disabled: l.teleport === !1
|
|
485
|
-
}, [
|
|
507
|
+
}, [N.value ? (g(), a("div", {
|
|
486
508
|
key: 0,
|
|
487
|
-
ref:
|
|
488
|
-
style: f(
|
|
489
|
-
onPointerenter: s[0] ||= (...e) => C(
|
|
490
|
-
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)
|
|
491
513
|
}, [
|
|
492
|
-
|
|
493
|
-
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, {
|
|
494
516
|
appear: "",
|
|
495
|
-
onBeforeLeave:
|
|
496
|
-
onAfterEnter:
|
|
497
|
-
onAfterLeave:
|
|
517
|
+
onBeforeLeave: H,
|
|
518
|
+
onAfterEnter: ae,
|
|
519
|
+
onAfterLeave: oe
|
|
498
520
|
}), {
|
|
499
|
-
default: k(() => [C(
|
|
500
|
-
id: C(
|
|
501
|
-
ref:
|
|
521
|
+
default: k(() => [C(T) ? (g(), r(b(l.as), u({ key: 0 }, I(), {
|
|
522
|
+
id: C(E),
|
|
523
|
+
ref: ee,
|
|
502
524
|
role: l.role ?? void 0,
|
|
503
|
-
tabindex:
|
|
525
|
+
tabindex: R.value ? -1 : void 0,
|
|
504
526
|
"data-sbf-state": "open",
|
|
505
|
-
"data-sbf-side": C(
|
|
506
|
-
"data-sbf-align": C(
|
|
507
|
-
onKeydown:
|
|
527
|
+
"data-sbf-side": C(j),
|
|
528
|
+
"data-sbf-align": C(M),
|
|
529
|
+
onKeydown: ye
|
|
508
530
|
}), {
|
|
509
531
|
default: k(() => [y(n.$slots, "default")]),
|
|
510
532
|
_: 3
|
|
@@ -517,9 +539,9 @@ var le = (e) => e instanceof Element && !!e.closest("input, textarea, select, [c
|
|
|
517
539
|
])) : i("", !0)]),
|
|
518
540
|
_: 3
|
|
519
541
|
}, 16),
|
|
520
|
-
|
|
542
|
+
B.value ? (g(), a("span", u({ key: 1 }, V, { onFocus: de }), null, 16)) : i("", !0)
|
|
521
543
|
], 36)) : i("", !0)], 8, ["to", "disabled"]));
|
|
522
544
|
}
|
|
523
545
|
});
|
|
524
546
|
//#endregion
|
|
525
|
-
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=(()=>{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]`)}function g(e){return[...e.querySelectorAll(f)].filter(p)}function _(e,t){return g(e.ownerDocument).find(n=>!!(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING)&&!e.contains(n)&&!t(n))}var v=[],y=(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)},y=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={...y,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?y.reference.value?.contains(e)||y.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 v.findLast(e=>e.contains(t))??v[v.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`&&(v.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?.(),v.splice(v.indexOf(S),1)}))},{immediate:!0}),(0,t.watch)(l,e=>s(e?`open`:`close`)),(0,t.watch)(y.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:y.update}),(e,n)=>(0,t.renderSlot)(e.$slots,`default`,{isOpen:l.value,open:p,close:m,toggle:g,placement:(0,t.unref)(y).placement.value,side:(0,t.unref)(y).side.value,align:(0,t.unref)(y).align.value,floatingId:S.floatingId,referenceId:S.referenceId.value})}}),b={dialog:`dialog`,alertdialog:`dialog`,menu:`menu`,listbox:`listbox`,tree:`tree`,grid:`grid`},x={menu:`[role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"]`,listbox:`[role="option"]`},S=e=>!!e&&Object.hasOwn(x,e);function C(e,t){return[...e.querySelectorAll(x[t])].filter(n=>n.closest(`[role="${t}"]`)===e&&!n.matches(`:disabled`)&&(n.checkVisibility?.()??!0))}var w=e=>e instanceof Element&&!!e.closest(`input, textarea, select, [contenteditable]:not([contenteditable="false"])`),T=e=>getComputedStyle(e).direction===`rtl`?{open:`ArrowLeft`,close:`ArrowRight`}:{open:`ArrowRight`,close:`ArrowLeft`},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)(),a=s(`SbReference`),{isOpen:c,disabled:l,floatingId:u,floatingRole:d}=a,f=o(a,`reference`,`SbReference`),p=()=>typeof n.as!=`string`||n.as===`button`||n.as===`a`&&r.href!=null,h=`${(0,t.useId)()}-reference`,_=(0,t.computed)(()=>n.id??h);(0,t.watchEffect)(()=>{a.referenceId.value=_.value}),(0,t.onMounted)(()=>{a.reference.value||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===_.value&&(a.referenceId.value=void 0),f(),!a.parts.reference&&!a.isUnmounting&&a.close()});let v=(0,t.computed)(()=>n.ariaHaspopup??(d.value&&Object.hasOwn(b,d.value)?b[d.value]:void 0)),y=e=>{a.reference.value=m(e)},x=()=>{a.trigger.value===`click`&&a.toggle()},S=(0,t.computed)(()=>d.value===`tooltip`),C=()=>a.trigger.value===`hover`||a.trigger.value===`focus`,w=()=>a.parent?.navigation.value===`menu`,E=e=>{C()&&!w()&&!a.contains(e.relatedTarget)&&a.open()},D=e=>{C()&&!a.contains(e.relatedTarget)&&a.close()},O=e=>{let t=a.floating.value,n=t&&g(t)[0];n&&(e.preventDefault(),n.focus())},k=e=>{if(a.navigation.value&&!l.value&&a.trigger.value!==`manual`){if(w()){let t=e.currentTarget;return e.key===T(t).open?`first`:void 0}return e.key===`ArrowDown`?`first`:e.key===`ArrowUp`?`last`:void 0}},A=e=>{if(e.key===`Tab`&&!e.shiftKey&&c.value&&!w()){O(e);return}let t=k(e);t?(e.preventDefault(),a.focusRequest.value=t,a.open()):p()||e.key!==`Enter`&&e.key!==` `||(e.preventDefault(),x())};return(e,r)=>((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(n.as),{id:_.value,ref:y,type:n.as===`button`?`button`:void 0,role:p()?void 0:`button`,tabindex:p()?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":v.value,"aria-expanded":S.value?void 0:(0,t.unref)(c)?`true`:`false`,"aria-controls":(0,t.unref)(c)&&!S.value?(0,t.unref)(u):void 0,"aria-describedby":(0,t.unref)(c)&&S.value?(0,t.unref)(u):void 0,"data-sbf-state":(0,t.unref)(c)?`open`:`closed`,onClick:x,onKeydown:A,onFocusin:E,onFocusout:D,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`]))}}),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)(),c=s(`SbFloating`);(0,t.onBeforeUnmount)(o(c,`floating`,`SbFloating`));let{isOpen:l,floatingId:u,referenceId:f,side:p,align:h}=c;(0,t.watchEffect)(()=>{c.floatingRole.value=r.role,c.navigation.value=r.keyboardNavigation&&S(r.role)?r.role:null});let v=(0,t.ref)(l.value),y=(0,t.ref)(null),x=e=>{c.floating.value=e instanceof HTMLElement?e:null},E=e=>{y.value=m(e)},D=(0,t.computed)(()=>({...c.floatingStyles.value,...r.zIndex===void 0?{}:{zIndex:r.zIndex}})),O=()=>({"aria-labelledby":r.role&&Object.hasOwn(b,r.role)&&a[`aria-label`]==null?f.value:void 0,...a}),k=(0,t.computed)(()=>r.autoFocus??(r.role!==`tooltip`&&c.trigger.value!==`hover`&&c.trigger.value!==`focus`)),A=(0,t.computed)(()=>l.value&&r.teleport!==!1&&r.role!==`tooltip`),j={[d]:``,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=()=>{l.value||(v.value=!1),i(`after-leave`)},I=e=>e instanceof Node&&!!y.value?.contains(e),L=e=>{let t=y.value;if(!t)return;let n=g(t);((e===`first`?n[0]:n[n.length-1])??t).focus({preventScroll:!0})},R=e=>{I(e.relatedTarget)?c.reference.value?.focus():L(`first`)},z=e=>{if(!I(e.relatedTarget)){L(`last`);return}let t=c.reference.value;((t&&_(t,e=>c.contains(e)))??t)?.focus()},B=()=>{let e=c.navigation.value;return y.value&&e?C(y.value,e):[]},V=e=>{let t=y.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]??g(t)[0]??t).focus({preventScroll:!0})},H=()=>{let e=c.focusRequest.value;return c.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=c,n=[t];for(;t.parent?.navigation.value===`menu`;)t=t.parent,n.push(t);let r=t.reference.value,i=e?r:(r&&_(r,e=>t.contains(e)))??r;for(let e of n)e.skipFocusReturn=!0,e.close();i?.focus()},q=e=>{let t=c.navigation.value;if(!t||e.defaultPrevented||e.isComposing||w(e.target))return;let n=B(),r=n.findIndex(t=>t.contains(e.target)),i=n.length-1,a=t===`menu`,o;if(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(),K(e.shiftKey);return}else if(t===`menu`&&c.parent?.navigation.value===`menu`&&y.value&&e.key===T(y.value).close){e.preventDefault(),c.close();return}else if(e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&(e.key!==` `||U))e.preventDefault(),o=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}o&&(e.preventDefault(),o.focus())};return(0,t.watch)(l,e=>{let t=c.skipFocusReturn;if(c.skipFocusReturn=!1,e){v.value=!0;return}let n=document.activeElement;!t&&n&&c.floating.value?.contains(n)&&c.reference.value?.focus({preventScroll:!0})}),(0,t.watch)(()=>l.value&&c.isPositioned.value,async e=>{if(!e)return;let n=H();(n||k.value)&&(await(0,t.nextTick)(),V(n))}),(0,t.watch)(c.focusRequest,e=>{e&&l.value&&c.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:x,style:(0,t.normalizeStyle)(D.value),onPointerenter:n[0]||=(...e)=>(0,t.unref)(c).hover&&(0,t.unref)(c).hover(...e),onPointerleave:n[1]||=(...e)=>(0,t.unref)(c).hover&&(0,t.unref)(c).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)(l)?((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(r.as),(0,t.mergeProps)({key:0},O(),{id:(0,t.unref)(u),ref:E,role:r.role??void 0,tabindex:k.value?-1:void 0,"data-sbf-state":`open`,"data-sbf-side":(0,t.unref)(p),"data-sbf-align":(0,t.unref)(h),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=y,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
|
/**
|
package/dist/utils/dom.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export declare const FOCUS_GUARD_ATTRIBUTE = "data-sbf-focus-guard";
|
|
|
4
4
|
export declare function toElement(el: unknown): HTMLElement | null;
|
|
5
5
|
/** Whether pressing `target` moves focus to it (or to a focusable ancestor). */
|
|
6
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;
|
|
7
9
|
/** Elements reachable with Tab inside `root`, in document order (focus guards excluded). */
|
|
8
10
|
export declare function getTabbables(root: ParentNode): HTMLElement[];
|
|
9
11
|
/** First element reachable with Tab after `anchor` in document order, skipping the excluded ones. */
|
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.3",
|
|
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",
|