sb-floating-panel-vue 5.0.1 → 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 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"` or the `hover` / `focus` trigger | Once positioned, focuses the first `[autofocus]` or focusable element inside, else the panel itself (a menu: its first item; a listbox: its selected option) |
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 and about an `as` component that renders no single root element. The warnings depend on `process.env.NODE_ENV`, which your bundler replaces: they are left out of production builds.
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,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 I } from "@floating-ui/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 te } from "@floating-ui/vue";
3
3
  //#region src/utils/warn.ts
4
- var L = (() => {
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 R(e) {
12
- L && console.warn(`[sb-floating-panel-vue] ${e}`);
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 z = Symbol("SbFloatingContext");
17
- function B(e, t, n) {
18
- return ++e.parts[t] > 1 && R(`<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]--;
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 V(e) {
21
- let t = c(z, null);
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 H = (e) => `${Math.max(0, e)}px`, te = (e, t) => {
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 ne(e) {
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 = H(e.reference.width);
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", te(a, c)), o.setProperty("--sbf-available-width", H(r)), o.setProperty("--sbf-available-height", H(i)), o.width = n === !0 ? s : "", o.minWidth = n === "min" ? s : "";
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 } = I(t, r, {
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 re = "data-sbf-focus-guard", U = [
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(","), W = (e) => e.tabIndex >= 0 && !e.hasAttribute("data-sbf-focus-guard") && !e.closest("[inert]") && (e.checkVisibility?.() ?? !0);
90
- function ie(e) {
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 G(e) {
95
- let t = e instanceof Element ? e.closest(U) : null;
94
+ function oe(e) {
95
+ let t = e instanceof Element ? e.closest(H) : null;
96
96
  return !!t && !t.closest("[inert]");
97
97
  }
98
- function K(e) {
99
- return [...e.querySelectorAll(U)].filter(W);
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 ae(e, t) {
102
- 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));
103
104
  }
104
105
  //#endregion
105
106
  //#region src/components/SbContainer.vue?vue&type=script&setup=true&lang.ts
106
- var q = [], oe = /* @__PURE__ */ s({
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 = ne({
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(z, null), k = {
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)), _(z, k), p(() => k.isUnmounting = !0);
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 q.findLast((e) => e.contains(t)) ?? q[q.length - 1];
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 = G(e), m());
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" && (q.push(k), document.addEventListener("keydown", j), document.addEventListener("pointerdown", P, !0), document.addEventListener("focusin", F), n(() => {
228
- document.removeEventListener("keydown", j), document.removeEventListener("pointerdown", P, !0), document.removeEventListener("focusin", F), N?.(), q.splice(q.indexOf(k), 1);
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
- }, se = (e) => !!e && Object.hasOwn(Y, e);
265
- function ce(e, t) {
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 le = (e) => e instanceof Element && !!e.closest("input, textarea, select, [contenteditable]:not([contenteditable=\"false\"])"), ue = (e) => getComputedStyle(e).direction === "rtl" ? {
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
- }, X = /* @__PURE__ */ s({
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 = V("SbReference"), { isOpen: o, disabled: s, floatingId: c, floatingRole: l } = a, u = B(a, "reference", "SbReference"), d = () => typeof t.as != "string" || t.as === "button" || t.as === "a" && i.href != null, f = `${T()}-reference`, h = n(() => t.id ?? f);
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 = h.value;
290
+ a.referenceId.value = _.value;
285
291
  }), m(() => {
286
- a.reference.value || R("<SbReference> rendered no element, so the panel cannot be positioned: a component in `as` must render a single root element.");
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 === h.value && (a.referenceId.value = void 0), u(), !a.parts.reference && !a.isUnmounting && a.close();
295
+ a.referenceId.value === _.value && (a.referenceId.value = void 0), u(), !a.parts.reference && !a.isUnmounting && a.close();
289
296
  });
290
- let _ = n(() => t.ariaHaspopup ?? (l.value && Object.hasOwn(J, l.value) ? J[l.value] : void 0)), v = (e) => {
291
- a.reference.value = ie(e);
292
- }, x = () => {
293
- a.trigger.value === "click" && a.toggle();
294
- }, S = n(() => l.value === "tooltip"), E = () => a.trigger.value === "hover" || a.trigger.value === "focus", D = () => a.parent?.navigation.value === "menu", A = (e) => {
295
- E() && !D() && !a.contains(e.relatedTarget) && a.open();
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
- let t = a.floating.value, n = t && K(t)[0];
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 (D()) {
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
- }, P = (e) => {
310
- if (e.key === "Tab" && !e.shiftKey && o.value && !D()) {
311
- M(e);
316
+ }, F = (e) => {
317
+ if (e.key === "Tab" && !e.shiftKey && o.value && !A()) {
318
+ N(e);
312
319
  return;
313
320
  }
314
- let t = N(e);
315
- t ? (e.preventDefault(), a.focusRequest.value = t, a.open()) : d() || e.key !== "Enter" && e.key !== " " || (e.preventDefault(), x());
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: h.value,
319
- ref: v,
325
+ id: _.value,
326
+ ref: x,
320
327
  type: t.as === "button" ? "button" : void 0,
321
- role: d() ? void 0 : "button",
322
- tabindex: d() ? void 0 : C(s) ? -1 : 0,
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": _.value,
326
- "aria-expanded": S.value ? void 0 : C(o) ? "true" : "false",
327
- "aria-controls": C(o) && !S.value ? C(c) : void 0,
328
- "aria-describedby": C(o) && S.value ? C(c) : void 0,
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: x,
331
- onKeydown: P,
332
- onFocusin: A,
333
- onFocusout: j,
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, m = c, _ = w(), x = V("SbFloating");
379
- p(B(x, "floating", "SbFloating"));
380
- let { isOpen: S, floatingId: T, referenceId: E, side: A, align: j } = x;
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
- x.floatingRole.value = l.role, x.navigation.value = l.keyboardNavigation && se(l.role) ? l.role : null;
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 M = v(S.value), N = v(null), P = (e) => {
385
- x.floating.value = e instanceof HTMLElement ? e : null;
386
- }, F = (e) => {
387
- N.value = ie(e);
388
- }, ee = n(() => ({
389
- ...x.floatingStyles.value,
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) && _["aria-label"] == null ? E.value : void 0,
393
- ..._
394
- }), L = n(() => l.autoFocus ?? (l.role !== "tooltip" && x.trigger.value !== "hover" && x.trigger.value !== "focus")), R = n(() => S.value && l.teleport !== !1 && l.role !== "tooltip"), z = {
395
- [re]: "",
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
- }, H = n(() => {
412
+ }, re = n(() => {
404
413
  let { transition: e } = l;
405
414
  return e ? typeof e == "string" ? { name: e } : e : { css: !1 };
406
- }), te = (e) => e.setAttribute("data-sbf-state", "closed"), ne = () => m("after-enter"), U = () => {
407
- S.value || (M.value = !1), m("after-leave");
408
- }, W = (e) => e instanceof Node && !!N.value?.contains(e), G = (e) => {
409
- let t = N.value;
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 = K(t);
420
+ let n = W(t);
412
421
  ((e === "first" ? n[0] : n[n.length - 1]) ?? t).focus({ preventScroll: !0 });
413
- }, q = (e) => {
414
- W(e.relatedTarget) ? x.reference.value?.focus() : G("first");
415
- }, oe = (e) => {
416
- if (!W(e.relatedTarget)) {
417
- G("last");
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 = x.reference.value;
421
- ((t && ae(t, (e) => x.contains(e))) ?? t)?.focus();
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 = x.focusRequest.value;
432
- return x.focusRequest.value = null, e;
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 de = (e, t, n) => {
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
- }, fe = (e) => {
440
- let t = x, n = [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 && ae(r, (e) => t.contains(e))) ?? 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
- }, pe = (e) => {
446
- let t = x.navigation.value;
447
- if (!t || e.defaultPrevented || e.isComposing || le(e.target)) return;
448
- let n = Y(), r = n.findIndex((t) => t.contains(e.target)), i = n.length - 1, a = t === "menu", o;
449
- if (e.key === "ArrowDown") o = n[r < i ? r + 1 : a ? 0 : i];
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(), fe(e.shiftKey);
476
+ e.preventDefault(), ve(e.shiftKey);
455
477
  return;
456
- } else if (t === "menu" && x.parent?.navigation.value === "menu" && N.value && e.key === ue(N.value).close) {
457
- e.preventDefault(), x.close();
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 = de(n, r, e.key);
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(S, (e) => {
469
- let t = x.skipFocusReturn;
470
- if (x.skipFocusReturn = !1, e) {
471
- M.value = !0;
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 && x.floating.value?.contains(n) && x.reference.value?.focus({ preventScroll: !0 });
476
- }), D(() => S.value && x.isPositioned.value, async (e) => {
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 = Z();
479
- (t || L.value) && (await d(), X(t));
480
- }), D(x.focusRequest, (e) => {
481
- e && S.value && x.isPositioned.value && X(Z());
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
- }, [M.value ? (g(), a("div", {
507
+ }, [N.value ? (g(), a("div", {
486
508
  key: 0,
487
- ref: P,
488
- style: f(ee.value),
489
- onPointerenter: s[0] ||= (...e) => C(x).hover && C(x).hover(...e),
490
- onPointerleave: s[1] ||= (...e) => C(x).hover && C(x).hover(...e)
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
- R.value ? (g(), a("span", u({ key: 0 }, z, { onFocus: q }), null, 16)) : i("", !0),
493
- o(t, u(H.value, {
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: te,
496
- onAfterEnter: ne,
497
- onAfterLeave: U
517
+ onBeforeLeave: H,
518
+ onAfterEnter: ae,
519
+ onAfterLeave: oe
498
520
  }), {
499
- default: k(() => [C(S) ? (g(), r(b(l.as), u({ key: 0 }, I(), {
500
- id: C(T),
501
- ref: F,
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: L.value ? -1 : void 0,
525
+ tabindex: R.value ? -1 : void 0,
504
526
  "data-sbf-state": "open",
505
- "data-sbf-side": C(A),
506
- "data-sbf-align": C(j),
507
- onKeydown: pe
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
- R.value ? (g(), a("span", u({ key: 1 }, z, { onFocus: oe }), null, 16)) : i("", !0)
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 { oe as SbContainer, Z as SbFloating, X as SbReference };
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 they are: a component is expected to render
157
- * its own focusable element (e.g. the `<Button>` of your design system).
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"` and with the `hover` and `focus` triggers
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
  /**
@@ -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.1",
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",