sb-floating-panel-vue 5.0.3 → 5.2.0

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.
@@ -1,62 +1,103 @@
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 te } from "@floating-ui/vue";
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 ee, normalizeStyle as d, onBeforeUnmount as f, onMounted as p, onScopeDispose as m, openBlock as h, provide as g, readonly as _, ref as v, renderSlot as te, resolveDynamicComponent as y, toRef as b, toValue as x, unref as S, useAttrs as C, useId as w, useModel as T, watch as E, watchEffect as D, withCtx as O } from "vue";
2
+ import { autoUpdate as k, flip as A, hide as j, limitShift as M, offset as N, shift as P, size as F, useFloating as ne } from "@floating-ui/vue";
3
+ //#region src/utils/dom.ts
4
+ var re = "data-sbf-focus-guard", I = [
5
+ "a[href]",
6
+ "button:not([disabled])",
7
+ "input:not([disabled]):not([type=\"hidden\"])",
8
+ "select:not([disabled])",
9
+ "textarea:not([disabled])",
10
+ "summary",
11
+ "audio[controls]",
12
+ "video[controls]",
13
+ "[tabindex]",
14
+ "[contenteditable=\"true\"]"
15
+ ].join(","), L = (e) => e.tabIndex >= 0 && !e.hasAttribute("data-sbf-focus-guard") && !e.closest("[inert]") && (e.checkVisibility?.() ?? !0);
16
+ function ie(e) {
17
+ let t = e && typeof e == "object" && "$el" in e ? e.$el : e;
18
+ return t instanceof HTMLElement ? t : null;
19
+ }
20
+ function R(e) {
21
+ let t = e instanceof Element ? e.closest(I) : null;
22
+ return !!t && !t.closest("[inert]");
23
+ }
24
+ function ae(e) {
25
+ try {
26
+ return e.matches(":focus-visible");
27
+ } catch {
28
+ return !0;
29
+ }
30
+ }
31
+ var oe = (e) => e.matches(I) && !e.closest("[inert]");
32
+ function z(e) {
33
+ return [...e.querySelectorAll(I)].filter(L);
34
+ }
35
+ function B(e, t) {
36
+ return z(e.ownerDocument).find((n) => !!(e.compareDocumentPosition(n) & Node.DOCUMENT_POSITION_FOLLOWING) && !e.contains(n) && !t(n));
37
+ }
38
+ //#endregion
3
39
  //#region src/utils/warn.ts
4
- var I = (() => {
40
+ var se = (() => {
5
41
  try {
6
42
  return process.env.NODE_ENV !== "production";
7
43
  } catch {
8
44
  return !1;
9
45
  }
10
46
  })();
11
- function L(e) {
12
- I && console.warn(`[sb-floating-panel-vue] ${e}`);
47
+ function V(e) {
48
+ se && console.warn(`[sb-floating-panel-vue] ${e}`);
13
49
  }
14
50
  //#endregion
15
51
  //#region src/composables/useSbFloatingContext.ts
16
- var R = Symbol("SbFloatingContext");
17
- function ne(e, t, n) {
18
- return ++e.parts[t] > 1 && L(`<SbContainer> has more than one <${n}>: they share one open state and position, so only the last one works. Use one SbContainer for each panel.`), () => e.parts[t]--;
52
+ var H = (e) => e.parent?.navigation.value === "menu";
53
+ function U(e, t) {
54
+ let n = document.activeElement, r = e.reference.value;
55
+ return !n || !r || r.contains(n) || !e.contains(n) ? !1 : ((t ? r : B(r, (t) => e.contains(t)))?.focus({ preventScroll: !0 }), !0);
19
56
  }
20
- function z(e) {
21
- let t = c(R, null);
57
+ var W = Symbol("SbFloatingContext");
58
+ function ce(e, t, n) {
59
+ return ++e.parts[t] > 1 && V(`<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]--;
60
+ }
61
+ function le(e) {
62
+ let t = c(W, null);
22
63
  if (!t) throw Error(`[sb-floating-panel-vue] <${e}> must be used inside <SbContainer>.`);
23
64
  return t;
24
65
  }
25
66
  //#endregion
26
67
  //#region src/composables/useSbFloatingPanel.ts
27
- var B = (e) => `${Math.max(0, e)}px`, V = (e, t) => {
68
+ var G = (e) => `${Math.max(0, e)}px`, K = (e, t) => {
28
69
  let [n, r] = e.split("-"), i = t && (n === "top" || n === "bottom"), a = r === "start" ? i ? "100%" : "0%" : r === "end" ? i ? "0%" : "100%" : "50%";
29
70
  return n === "top" ? `${a} 100%` : n === "bottom" ? `${a} 0%` : n === "left" ? `100% ${a}` : `0% ${a}`;
30
71
  };
31
- function re(e) {
72
+ function q(e) {
32
73
  let t = v(null), r = v(null), i = n(() => {
33
- let t = S(e.padding), n = S(e.matchWidth), r = S(e.placement).includes("-"), i = j({
74
+ let t = x(e.padding), n = x(e.matchWidth), r = x(e.placement).includes("-"), i = A({
34
75
  padding: t,
35
76
  crossAxis: "alignment",
36
77
  fallbackAxisSideDirection: "end"
37
- }), a = F({
78
+ }), a = P({
38
79
  padding: t,
39
- limiter: N()
80
+ limiter: M()
40
81
  }), o = [
41
- P(S(e.offset)),
82
+ N(x(e.offset)),
42
83
  ...r ? [i, a] : [a, i],
43
- ee({
84
+ F({
44
85
  padding: t,
45
86
  apply({ rects: e, elements: t, availableWidth: r, availableHeight: i, placement: a }) {
46
- let { style: o } = t.floating, s = B(e.reference.width);
87
+ let { style: o } = t.floating, s = G(e.reference.width);
47
88
  o.setProperty("--sbf-reference-width", s);
48
89
  let c = getComputedStyle(t.floating).direction === "rtl";
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 : "";
90
+ o.setProperty("--sbf-transform-origin", K(a, c)), o.setProperty("--sbf-available-width", G(r)), o.setProperty("--sbf-available-height", G(i)), o.width = n === !0 ? s : "", o.minWidth = n === "min" ? s : "";
50
91
  }
51
92
  })
52
93
  ];
53
- return S(e.hideWhenDetached) && o.push(M({ padding: t })), o;
54
- }), { floatingStyles: a, middlewareData: o, placement: s, isPositioned: c, update: l } = te(t, r, {
55
- open: () => S(e.isOpen),
56
- placement: () => S(e.placement),
57
- strategy: () => S(e.strategy),
94
+ return x(e.hideWhenDetached) && o.push(j({ padding: t })), o;
95
+ }), { floatingStyles: a, middlewareData: o, placement: s, isPositioned: c, update: l } = ne(t, r, {
96
+ open: () => x(e.isOpen),
97
+ placement: () => x(e.placement),
98
+ strategy: () => x(e.strategy),
58
99
  middleware: i,
59
- whileElementsMounted: A
100
+ whileElementsMounted: k
60
101
  }), u = n(() => !!o.value.hide?.referenceHidden);
61
102
  return {
62
103
  reference: t,
@@ -74,37 +115,11 @@ function re(e) {
74
115
  };
75
116
  }
76
117
  //#endregion
77
- //#region src/utils/dom.ts
78
- var ie = "data-sbf-focus-guard", H = [
79
- "a[href]",
80
- "button:not([disabled])",
81
- "input:not([disabled]):not([type=\"hidden\"])",
82
- "select:not([disabled])",
83
- "textarea:not([disabled])",
84
- "summary",
85
- "audio[controls]",
86
- "video[controls]",
87
- "[tabindex]",
88
- "[contenteditable=\"true\"]"
89
- ].join(","), ae = (e) => e.tabIndex >= 0 && !e.hasAttribute("data-sbf-focus-guard") && !e.closest("[inert]") && (e.checkVisibility?.() ?? !0);
90
- function U(e) {
91
- let t = e && typeof e == "object" && "$el" in e ? e.$el : e;
92
- return t instanceof HTMLElement ? t : null;
93
- }
94
- function oe(e) {
95
- let t = e instanceof Element ? e.closest(H) : null;
96
- return !!t && !t.closest("[inert]");
97
- }
98
- var se = (e) => e.matches(H) && !e.closest("[inert]");
99
- function W(e) {
100
- return [...e.querySelectorAll(H)].filter(ae);
101
- }
102
- function G(e, t) {
103
- return W(e.ownerDocument).find((n) => !!(e.compareDocumentPosition(n) & Node.DOCUMENT_POSITION_FOLLOWING) && !e.contains(n) && !t(n));
104
- }
105
- //#endregion
106
118
  //#region src/components/SbContainer.vue?vue&type=script&setup=true&lang.ts
107
- var K = [], q = /* @__PURE__ */ s({
119
+ var J = [], Y = {
120
+ open: 300,
121
+ close: 150
122
+ }, X = /* @__PURE__ */ s({
108
123
  inheritAttrs: !1,
109
124
  __name: "SbContainer",
110
125
  props: /*@__PURE__*/ l({
@@ -129,10 +144,7 @@ var K = [], q = /* @__PURE__ */ s({
129
144
  default: !1
130
145
  },
131
146
  trigger: { default: "click" },
132
- delay: { default: () => ({
133
- open: 300,
134
- close: 150
135
- }) },
147
+ delay: { default: () => ({ ...Y }) },
136
148
  closeOnEscape: {
137
149
  type: Boolean,
138
150
  default: !0
@@ -154,20 +166,17 @@ var K = [], q = /* @__PURE__ */ s({
154
166
  }),
155
167
  emits: /*@__PURE__*/ l(["open", "close"], ["update:open"]),
156
168
  setup(e, { expose: t, emit: r }) {
157
- let i = e, a = E(e, "open"), o = r, s = i.defaultOpen, l = n(() => !i.disabled && (a.value ?? s)), u;
158
- h(() => clearTimeout(u));
159
- let d = (e) => {
160
- clearTimeout(u), !(e && i.disabled) && e !== l.value && (a.value = e);
161
- }, f = () => d(!0), m = () => d(!1), g = () => d(!l.value), b = (e) => {
169
+ let i = e, a = T(e, "open"), o = r, s = i.defaultOpen, l = n(() => !i.disabled && (a.value ?? s)), u;
170
+ m(() => clearTimeout(u));
171
+ let d, p = ((e, t) => {
172
+ clearTimeout(u), !(e && i.disabled) && e !== l.value && (d = t, a.value = e, ee(() => d = void 0));
173
+ }), h = () => p(!0, "api"), _ = () => p(!1, "api"), y = () => p(!l.value, "api"), x = (e) => {
162
174
  if (e.pointerType === "touch" || (O?.hover(e), i.trigger !== "hover")) return;
163
175
  let t = e.type === "pointerenter" || k.contains(e.relatedTarget);
164
176
  if (clearTimeout(u), t === l.value) return;
165
- let { delay: n } = i, r = typeof n == "number" ? {
166
- open: n,
167
- close: n
168
- } : n, a = (t ? r.open : r.close) ?? (t ? 300 : 150);
169
- u = setTimeout(() => d(t), a);
170
- }, S = re({
177
+ let { delay: n } = i, r = t ? "open" : "close", a = typeof n == "number" ? n : n[r] ?? Y[r];
178
+ u = setTimeout(() => p(t, "hover"), a);
179
+ }, C = q({
171
180
  isOpen: l,
172
181
  placement: () => i.placement,
173
182
  strategy: () => i.strategy,
@@ -175,12 +184,12 @@ var K = [], q = /* @__PURE__ */ s({
175
184
  padding: () => i.padding,
176
185
  matchWidth: () => i.matchWidth,
177
186
  hideWhenDetached: () => i.hideWhenDetached
178
- }), w = /* @__PURE__ */ new Set(), O = c(R, null), k = {
179
- ...S,
187
+ }), D = /* @__PURE__ */ new Set(), O = c(W, null), k = {
188
+ ...C,
180
189
  isOpen: l,
181
- disabled: x(i, "disabled"),
182
- trigger: x(i, "trigger"),
183
- floatingId: `${T()}-floating`,
190
+ disabled: b(i, "disabled"),
191
+ trigger: b(i, "trigger"),
192
+ floatingId: `${w()}-floating`,
184
193
  referenceId: v(),
185
194
  floatingRole: v("dialog"),
186
195
  navigation: v(null),
@@ -192,21 +201,24 @@ var K = [], q = /* @__PURE__ */ s({
192
201
  floating: 0
193
202
  },
194
203
  isUnmounting: !1,
195
- open: f,
196
- close: m,
197
- toggle: g,
198
- hover: b,
199
- contains: (e) => e instanceof Node ? S.reference.value?.contains(e) || S.floating.value?.contains(e) ? !0 : [...w].some((t) => t.contains(e)) : !1,
200
- registerChild: (e) => (w.add(e), () => w.delete(e))
204
+ open: h,
205
+ close: _,
206
+ toggle: y,
207
+ setOpen: p,
208
+ hover: x,
209
+ contains: (e) => e instanceof Node ? C.reference.value?.contains(e) || C.floating.value?.contains(e) ? !0 : [...D].some((t) => t.contains(e)) : !1,
210
+ registerChild: (e) => (D.add(e), () => D.delete(e))
201
211
  };
202
- O && h(O.registerChild(k)), _(R, k), p(() => k.isUnmounting = !0);
212
+ O && m(O.registerChild(k)), g(W, k), f(() => {
213
+ k.isUnmounting = !0, U(k, !1);
214
+ });
203
215
  let A = (e) => {
204
216
  let t = e.composedPath()[0] ?? e.target;
205
- return K.findLast((e) => e.contains(t)) ?? K[K.length - 1];
217
+ return J.findLast((e) => e.contains(t)) ?? J[J.length - 1];
206
218
  }, j = (e) => {
207
- e.key === "Escape" && A(e) === k && (!i.closeOnEscape || e.defaultPrevented || e.isComposing || (e.preventDefault(), m()));
219
+ e.key === "Escape" && A(e) === k && (!i.closeOnEscape || e.defaultPrevented || e.isComposing || (e.preventDefault(), p(!1, "escape")));
208
220
  }, M = (e) => {
209
- k.contains(e) || (k.skipFocusReturn = oe(e), m());
221
+ k.contains(e) || (k.skipFocusReturn = R(e), p(!1, "outside"));
210
222
  }, N, P = (e) => {
211
223
  if (N?.(), !i.closeOnClickOutside) return;
212
224
  let t = e.composedPath()[0] ?? e.target;
@@ -222,57 +234,60 @@ var K = [], q = /* @__PURE__ */ s({
222
234
  document.removeEventListener("click", n, !0), N = void 0;
223
235
  };
224
236
  }, F = (e) => {
225
- i.closeOnFocusOut && (k.contains(e.composedPath()[0] ?? e.target) || m());
237
+ i.closeOnFocusOut && (k.contains(e.composedPath()[0] ?? e.target) || p(!1, "focus-out"));
226
238
  };
227
- return D(l, (e, t, 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);
239
+ return E(l, (e, t, n) => {
240
+ e && typeof document < "u" && (J.push(k), document.addEventListener("keydown", j), document.addEventListener("pointerdown", P, !0), document.addEventListener("focusin", F), n(() => {
241
+ document.removeEventListener("keydown", j), document.removeEventListener("pointerdown", P, !0), document.removeEventListener("focusin", F), N?.(), J.splice(J.indexOf(k), 1);
230
242
  }));
231
- }, { immediate: !0 }), D(l, (e) => o(e ? "open" : "close")), D(S.referenceHidden, (e) => {
232
- e && i.hideWhenDetached === "close" && m();
233
- }), D(() => i.disabled, (e) => {
243
+ }, { immediate: !0 }), E(l, (e) => {
244
+ let t = d ?? (!e && i.disabled ? "disabled" : "api");
245
+ d = void 0, o(e ? "open" : "close", t);
246
+ }), E(C.referenceHidden, (e) => {
247
+ e && i.hideWhenDetached === "close" && p(!1, "detached");
248
+ }), E(() => i.disabled, (e) => {
234
249
  e && (a.value ?? s) && (a.value = !1);
235
250
  }), t({
236
251
  get isOpen() {
237
252
  return l.value;
238
253
  },
239
- open: f,
240
- close: m,
241
- toggle: g,
242
- update: S.update
243
- }), (e, t) => y(e.$slots, "default", {
254
+ open: h,
255
+ close: _,
256
+ toggle: y,
257
+ update: C.update
258
+ }), (e, t) => te(e.$slots, "default", {
244
259
  isOpen: l.value,
245
- open: f,
246
- close: m,
247
- toggle: g,
248
- placement: C(S).placement.value,
249
- side: C(S).side.value,
250
- align: C(S).align.value,
260
+ open: h,
261
+ close: _,
262
+ toggle: y,
263
+ placement: S(C).placement.value,
264
+ side: S(C).side.value,
265
+ align: S(C).align.value,
251
266
  floatingId: k.floatingId,
252
267
  referenceId: k.referenceId.value
253
268
  });
254
269
  }
255
- }), J = {
270
+ }), ue = {
256
271
  dialog: "dialog",
257
272
  alertdialog: "dialog",
258
273
  menu: "menu",
259
274
  listbox: "listbox",
260
275
  tree: "tree",
261
276
  grid: "grid"
262
- }, Y = {
277
+ }, Z = {
263
278
  menu: "[role=\"menuitem\"], [role=\"menuitemcheckbox\"], [role=\"menuitemradio\"]",
264
279
  listbox: "[role=\"option\"]"
265
- }, ce = (e) => !!e && Object.hasOwn(Y, e);
266
- function le(e, t) {
267
- return [...e.querySelectorAll(Y[t])].filter((n) => n.closest(`[role="${t}"]`) === e && !n.matches(":disabled") && (n.checkVisibility?.() ?? !0));
280
+ }, de = (e) => !!e && Object.hasOwn(Z, e);
281
+ function fe(e, t) {
282
+ return [...e.querySelectorAll(Z[t])].filter((n) => n.closest(`[role="${t}"]`) === e && !n.matches(":disabled") && (n.checkVisibility?.() ?? !0));
268
283
  }
269
- var X = (e) => e instanceof Element && !!e.closest("input, textarea, select, [contenteditable]:not([contenteditable=\"false\"])"), ue = (e) => getComputedStyle(e).direction === "rtl" ? {
284
+ var Q = (e) => e instanceof Element && !!e.closest("input, textarea, select, [contenteditable]:not([contenteditable=\"false\"])"), pe = (e) => getComputedStyle(e).direction === "rtl" ? {
270
285
  open: "ArrowLeft",
271
286
  close: "ArrowRight"
272
287
  } : {
273
288
  open: "ArrowRight",
274
289
  close: "ArrowLeft"
275
- }, de = /* @__PURE__ */ s({
290
+ }, me = /* @__PURE__ */ s({
276
291
  __name: "SbReference",
277
292
  props: {
278
293
  as: { default: "button" },
@@ -280,68 +295,70 @@ var X = (e) => e instanceof Element && !!e.closest("input, textarea, select, [co
280
295
  ariaHaspopup: { default: void 0 }
281
296
  },
282
297
  setup(e) {
283
- let t = e, i = w(), a = z("SbReference"), { isOpen: o, disabled: s, floatingId: c, floatingRole: l } = a, u = ne(a, "reference", "SbReference"), d = [
298
+ let t = e, i = C(), a = le("SbReference"), { isOpen: o, disabled: s, floatingId: c, floatingRole: l } = a, u = ce(a, "reference", "SbReference"), ee = [
284
299
  "button",
285
300
  "input",
286
301
  "textarea",
287
302
  "select"
288
- ], f = () => typeof t.as != "string" || d.includes(t.as) || t.as === "a" && i.href != null, h = `${T()}-reference`, _ = n(() => t.id ?? h);
289
- O(() => {
290
- a.referenceId.value = _.value;
291
- }), m(() => {
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.");
303
+ ], d = () => typeof t.as != "string" || ee.includes(t.as) || t.as === "a" && i.href != null, m = `${w()}-reference`, g = n(() => t.id ?? m);
304
+ D(() => {
305
+ a.referenceId.value = g.value;
294
306
  }), p(() => {
295
- a.referenceId.value === _.value && (a.referenceId.value = void 0), u(), !a.parts.reference && !a.isUnmounting && a.close();
307
+ let e = a.reference.value;
308
+ e ? Q(e) && a.navigation.value && V(`<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.`) : V("<SbReference> rendered no element, so the panel cannot be positioned: a component in `as` must render a single root element.");
309
+ }), f(() => {
310
+ a.referenceId.value === g.value && (a.referenceId.value = void 0), u(), !a.parts.reference && !a.isUnmounting && (a.skipFocusReturn = U(a, !1), a.setOpen(!1, "api"));
296
311
  });
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();
303
- }, M = (e) => {
304
- D() && !a.contains(e.relatedTarget) && a.close();
305
- }, N = (e) => {
306
- let t = a.floating.value, n = t && W(t)[0];
312
+ let _ = n(() => t.ariaHaspopup ?? (l.value && Object.hasOwn(ue, l.value) ? ue[l.value] : void 0)), v = (e) => {
313
+ a.reference.value = ie(e);
314
+ }, b = () => {
315
+ a.trigger.value === "click" && (Q(a.reference.value) ? a.setOpen(!0, "trigger") : a.setOpen(!o.value, "trigger"));
316
+ }, x = n(() => l.value === "tooltip"), T = () => a.trigger.value === "hover" || a.trigger.value === "focus", E = (e) => {
317
+ if (!T() || H(a) || a.contains(e.relatedTarget)) return;
318
+ let t = e.target;
319
+ (a.trigger.value === "focus" || ae(t)) && a.setOpen(!0, "focus");
320
+ }, k = (e) => {
321
+ T() && !a.contains(e.relatedTarget) && a.setOpen(!1, "focus-out");
322
+ }, A = (e) => {
323
+ let t = a.floating.value, n = t && z(t)[0];
307
324
  n && (e.preventDefault(), n.focus());
308
- }, P = (e) => {
325
+ }, j = (e) => {
309
326
  if (a.navigation.value && !s.value && a.trigger.value !== "manual") {
310
- if (A()) {
327
+ if (H(a)) {
311
328
  let t = e.currentTarget;
312
- return e.key === ue(t).open ? "first" : void 0;
329
+ return e.key === pe(t).open ? "first" : void 0;
313
330
  }
314
331
  return e.key === "ArrowDown" ? "first" : e.key === "ArrowUp" ? "last" : void 0;
315
332
  }
316
- }, F = (e) => {
317
- if (e.key === "Tab" && !e.shiftKey && o.value && !A()) {
318
- N(e);
333
+ }, M = (e) => {
334
+ if (e.key === "Tab" && !e.shiftKey && o.value && !H(a)) {
335
+ A(e);
319
336
  return;
320
337
  }
321
- let t = P(e);
322
- t ? (e.preventDefault(), a.focusRequest.value = t, a.open()) : f() || e.key !== "Enter" && e.key !== " " || (e.preventDefault(), S());
338
+ let t = j(e);
339
+ t ? (e.preventDefault(), a.focusRequest.value = t, a.setOpen(!0, "trigger")) : d() || e.key !== "Enter" && e.key !== " " || (e.preventDefault(), b());
323
340
  };
324
- return (e, n) => (g(), r(b(t.as), {
325
- id: _.value,
326
- ref: x,
341
+ return (e, n) => (h(), r(y(t.as), {
342
+ id: g.value,
343
+ ref: v,
327
344
  type: t.as === "button" ? "button" : void 0,
328
- role: f() ? void 0 : "button",
329
- tabindex: f() ? void 0 : C(s) ? -1 : 0,
330
- disabled: t.as === "button" && C(s) ? !0 : void 0,
331
- "aria-disabled": t.as !== "button" && C(s) ? "true" : 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,
336
- "data-sbf-state": C(o) ? "open" : "closed",
337
- onClick: S,
338
- onKeydown: F,
339
- onFocusin: j,
340
- onFocusout: M,
341
- onPointerenter: C(a).hover,
342
- onPointerleave: C(a).hover
345
+ role: d() ? void 0 : "button",
346
+ tabindex: d() ? void 0 : S(s) ? -1 : 0,
347
+ disabled: t.as === "button" && S(s) ? !0 : void 0,
348
+ "aria-disabled": t.as !== "button" && S(s) ? "true" : void 0,
349
+ "aria-haspopup": _.value,
350
+ "aria-expanded": x.value ? void 0 : S(o) ? "true" : "false",
351
+ "aria-controls": S(o) && !x.value ? S(c) : void 0,
352
+ "aria-describedby": S(o) && x.value ? S(c) : void 0,
353
+ "data-sbf-state": S(o) ? "open" : "closed",
354
+ onClick: b,
355
+ onKeydown: M,
356
+ onFocusin: E,
357
+ onFocusout: k,
358
+ onPointerenter: S(a).hover,
359
+ onPointerleave: S(a).hover
343
360
  }, {
344
- default: k(() => [y(e.$slots, "default")]),
361
+ default: O(() => [te(e.$slots, "default")]),
345
362
  _: 3
346
363
  }, 40, [
347
364
  "id",
@@ -359,7 +376,7 @@ var X = (e) => e instanceof Element && !!e.closest("input, textarea, select, [co
359
376
  "onPointerleave"
360
377
  ]));
361
378
  }
362
- }), Z = /* @__PURE__ */ s({
379
+ }), he = ":disabled, [aria-disabled=\"true\"], [aria-haspopup]:not([aria-haspopup=\"false\"])", ge = /* @__PURE__ */ s({
363
380
  inheritAttrs: !1,
364
381
  __name: "SbFloating",
365
382
  props: {
@@ -378,30 +395,36 @@ var X = (e) => e instanceof Element && !!e.closest("input, textarea, select, [co
378
395
  keyboardNavigation: {
379
396
  type: Boolean,
380
397
  default: !0
398
+ },
399
+ closeOnSelect: {
400
+ type: Boolean,
401
+ default: !1
381
402
  }
382
403
  },
383
404
  emits: ["after-enter", "after-leave"],
384
405
  setup(s, { emit: c }) {
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;
388
- O(() => {
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.");
406
+ let l = s, g = c, _ = C(), b = le("SbFloating");
407
+ f(ce(b, "floating", "SbFloating")), f(() => {
408
+ b.isUnmounting || U(b, !0);
409
+ });
410
+ let { isOpen: x, floatingId: w, referenceId: T, side: k, align: A } = b;
411
+ D(() => {
412
+ b.floatingRole.value = l.role, b.navigation.value = l.keyboardNavigation && de(l.role) ? l.role : null;
413
+ }), p(() => {
414
+ l.role === "tooltip" && b.trigger.value === "click" && V("<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.");
392
415
  });
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,
416
+ let j = v(x.value), M = v(null), N = (e) => {
417
+ b.floating.value = e instanceof HTMLElement ? e : null;
418
+ }, P = (e) => {
419
+ M.value = ie(e);
420
+ }, F = n(() => ({
421
+ ...b.floatingStyles.value,
399
422
  ...l.zIndex === void 0 ? {} : { zIndex: l.zIndex }
400
- })), I = () => ({
401
- "aria-labelledby": l.role && Object.hasOwn(J, l.role) && x["aria-label"] == null ? A.value : void 0,
402
- ...x
403
- }), R = n(() => l.autoFocus ?? (l.role !== "tooltip" && S.trigger.value !== "hover" && S.trigger.value !== "focus" && !X(S.reference.value))), B = n(() => T.value && l.teleport !== !1 && l.role !== "tooltip"), V = {
404
- [ie]: "",
423
+ })), ne = () => ({
424
+ "aria-labelledby": l.role && Object.hasOwn(ue, l.role) && _["aria-label"] == null ? T.value : void 0,
425
+ ..._
426
+ }), I = n(() => l.autoFocus ?? (l.role !== "tooltip" && b.trigger.value !== "hover" && b.trigger.value !== "focus" && !Q(b.reference.value))), L = n(() => x.value && l.teleport !== !1 && l.role !== "tooltip"), R = {
427
+ [re]: "",
405
428
  tabindex: 0,
406
429
  style: {
407
430
  position: "fixed",
@@ -409,76 +432,98 @@ var X = (e) => e instanceof Element && !!e.closest("input, textarea, select, [co
409
432
  outline: "none",
410
433
  pointerEvents: "none"
411
434
  }
412
- }, re = n(() => {
435
+ }, ae = n(() => {
413
436
  let { transition: e } = l;
414
437
  return e ? typeof e == "string" ? { name: e } : e : { css: !1 };
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;
438
+ }), se = (e) => e.setAttribute("data-sbf-state", "closed"), W = () => g("after-enter"), G = () => {
439
+ x.value || (j.value = !1), g("after-leave");
440
+ }, K = (e) => e instanceof Node && !!M.value?.contains(e), q = (e) => {
441
+ let t = M.value;
419
442
  if (!t) return;
420
- let n = W(t);
443
+ let n = z(t);
421
444
  ((e === "first" ? n[0] : n[n.length - 1]) ?? t).focus({ preventScroll: !0 });
445
+ }, J = (e) => {
446
+ K(e.relatedTarget) ? b.reference.value?.focus() : q("first");
422
447
  }, Y = (e) => {
423
- K(e.relatedTarget) ? S.reference.value?.focus() : q("first");
424
- }, de = (e) => {
425
448
  if (!K(e.relatedTarget)) {
426
449
  q("last");
427
450
  return;
428
451
  }
429
- let t = S.reference.value;
430
- ((t && G(t, (e) => S.contains(e))) ?? t)?.focus();
431
- }, Z = () => {
432
- let e = S.navigation.value;
433
- return P.value && e ? le(P.value, e) : [];
434
- }, fe = {
452
+ let t = b.reference.value;
453
+ ((t && B(t, (e) => b.contains(e))) ?? t)?.focus();
454
+ }, X = (e) => {
455
+ if (e.key !== "Tab" || !e.shiftKey || e.defaultPrevented) return;
456
+ let t = b.reference.value;
457
+ t && M.value && z(M.value).length && (e.composedPath()[0] ?? e.target) === B(t, (e) => b.contains(e)) && (e.preventDefault(), q("last"));
458
+ };
459
+ E(L, (e, t, n) => {
460
+ e && typeof document < "u" && (document.addEventListener("keydown", X), n(() => document.removeEventListener("keydown", X)));
461
+ }, { immediate: !0 });
462
+ let Z = () => {
463
+ let e = b.navigation.value;
464
+ return M.value && e ? fe(M.value, e) : [];
465
+ }, me = {
466
+ menu: "[role=\"menuitem\"]",
467
+ listbox: "[role=\"option\"]"
468
+ }, ge = {
435
469
  menu: "role=\"menuitem\", \"menuitemcheckbox\" or \"menuitemradio\"",
436
470
  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;
471
+ }, _e = !1, ve = (e) => {
472
+ let t = b.navigation.value;
473
+ if (_e || !t) return;
474
+ let n = e.length ? e.every(oe) ? 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 ${ge[t]} and must not be disabled.`;
475
+ n && (_e = !0, V(`<SbFloating role="${t}"> ${n}`));
476
+ }, ye = (e) => {
477
+ let t = M.value;
444
478
  if (!t) return;
445
479
  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;
450
- }, Q = "", $;
451
- h(() => clearTimeout($));
452
- let _e = (e, t, n) => {
453
- Q += n.toLowerCase(), clearTimeout($), $ = setTimeout(() => Q = "", 500);
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)];
480
+ e && ve(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] ?? z(t)[0] ?? t).focus({ preventScroll: !0 });
481
+ }, be = () => {
482
+ let e = b.focusRequest.value;
483
+ return b.focusRequest.value = null, e;
484
+ }, $ = "", xe;
485
+ m(() => clearTimeout(xe));
486
+ let Se = (e, t, n) => {
487
+ $ += n.toLowerCase(), clearTimeout(xe), xe = setTimeout(() => $ = "", 500);
488
+ let r = [...$].every((e) => e === $[0]) ? $[0] : $, i = Math.max(t, 0), a = [...e.slice(i), ...e.slice(0, i)];
455
489
  return r.length === 1 && t >= 0 && a.push(a.shift()), a.find((e) => e.textContent?.trim().toLowerCase().startsWith(r));
456
- }, ve = (e) => {
457
- let t = S, n = [t];
458
- for (; t.parent?.navigation.value === "menu";) t = t.parent, n.push(t);
459
- let r = t.reference.value, i = e ? r : (r && G(r, (e) => t.contains(e))) ?? r;
460
- for (let e of n) e.skipFocusReturn = !0, e.close();
461
- i?.focus();
462
- }, ye = (e) => {
463
- let t = S.navigation.value;
464
- if (!t || e.defaultPrevented || e.isComposing || X(e.target)) return;
490
+ }, Ce = () => {
491
+ let e = [b];
492
+ for (; H(e[e.length - 1]);) e.push(e[e.length - 1].parent);
493
+ return e;
494
+ }, we = (e, t, n) => {
495
+ for (let t of e) t.skipFocusReturn = !0, t.setOpen(!1, n);
496
+ t?.focus({ preventScroll: !0 });
497
+ }, Te = (e) => {
498
+ let t = Ce(), n = t[t.length - 1], r = n.reference.value, i = e ? r : (r && B(r, (e) => n.contains(e))) ?? r;
499
+ we(t, i, "focus-out");
500
+ }, Ee = (e) => {
501
+ let { role: t } = l, n = M.value;
502
+ if (!l.closeOnSelect || e.defaultPrevented || !n || !de(t)) return;
503
+ let r = e.target.closest(me[t]);
504
+ if (!r || r.closest(`[role="${t}"]`) !== n || r.matches(he) || t === "listbox" && n.getAttribute("aria-multiselectable") === "true") return;
505
+ let i = t === "menu" ? Ce() : [b];
506
+ we(i, i[i.length - 1].reference.value, "select");
507
+ }, De = (e) => {
508
+ let t = b.navigation.value;
509
+ if (!t || e.defaultPrevented || e.isComposing || Q(e.target)) return;
465
510
  let n = Z(), r = n.findIndex((t) => t.contains(e.target)), i = n.length - 1, a = t === "menu", o;
466
511
  if ([
467
512
  "ArrowDown",
468
513
  "ArrowUp",
469
514
  "Home",
470
515
  "End"
471
- ].includes(e.key) && me(n), e.key === "ArrowDown") o = n[r < i ? r + 1 : a ? 0 : i];
516
+ ].includes(e.key) && ve(n), e.key === "ArrowDown") o = n[r < i ? r + 1 : a ? 0 : i];
472
517
  else if (e.key === "ArrowUp") o = n[r > 0 ? r - 1 : a || r < 0 ? i : 0];
473
518
  else if (e.key === "Home") o = n[0];
474
519
  else if (e.key === "End") o = n[i];
475
520
  else if (e.key === "Tab" && t === "menu") {
476
- e.preventDefault(), ve(e.shiftKey);
521
+ e.preventDefault(), Te(e.shiftKey);
477
522
  return;
478
- } else if (t === "menu" && S.parent?.navigation.value === "menu" && P.value && e.key === ue(P.value).close) {
479
- e.preventDefault(), S.close();
523
+ } else if (t === "menu" && H(b) && M.value && e.key === pe(M.value).close) {
524
+ e.preventDefault(), b.setOpen(!1, "escape");
480
525
  return;
481
- } else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey && (e.key !== " " || Q)) e.preventDefault(), o = _e(n, r, e.key);
526
+ } else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey && (e.key !== " " || $)) e.preventDefault(), o = Se(n, r, e.key);
482
527
  else if ((e.key === "Enter" || e.key === " ") && e.target === n[r]) {
483
528
  let t = n[r];
484
529
  if (t.matches("button, a[href], input, summary")) return;
@@ -487,48 +532,49 @@ var X = (e) => e instanceof Element && !!e.closest("input, textarea, select, [co
487
532
  }
488
533
  o && (e.preventDefault(), o.focus());
489
534
  };
490
- return D(T, (e) => {
491
- let t = S.skipFocusReturn;
492
- if (S.skipFocusReturn = !1, e) {
493
- N.value = !0;
535
+ return E(x, (e) => {
536
+ let t = b.skipFocusReturn;
537
+ if (b.skipFocusReturn = !1, e) {
538
+ j.value = !0;
494
539
  return;
495
540
  }
496
541
  let n = document.activeElement;
497
- !t && n && S.floating.value?.contains(n) && S.reference.value?.focus({ preventScroll: !0 });
498
- }), D(() => T.value && S.isPositioned.value, async (e) => {
542
+ !t && n && b.floating.value?.contains(n) && b.reference.value?.focus({ preventScroll: !0 });
543
+ }), E(() => x.value && b.isPositioned.value, async (e) => {
499
544
  if (!e) return;
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());
504
- }), (n, s) => (g(), r(e, {
545
+ let t = be();
546
+ (t || I.value) && (await ee(), ye(t));
547
+ }), E(b.focusRequest, (e) => {
548
+ e && x.value && b.isPositioned.value && ye(be());
549
+ }), (n, s) => (h(), r(e, {
505
550
  to: l.teleport || "body",
506
551
  disabled: l.teleport === !1
507
- }, [N.value ? (g(), a("div", {
552
+ }, [j.value ? (h(), a("div", {
508
553
  key: 0,
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)
554
+ ref: N,
555
+ style: d(F.value),
556
+ onPointerenter: s[0] ||= (...e) => S(b).hover && S(b).hover(...e),
557
+ onPointerleave: s[1] ||= (...e) => S(b).hover && S(b).hover(...e)
513
558
  }, [
514
- B.value ? (g(), a("span", u({ key: 0 }, V, { onFocus: Y }), null, 16)) : i("", !0),
515
- o(t, u(re.value, {
559
+ L.value ? (h(), a("span", u({ key: 0 }, R, { onFocus: J }), null, 16)) : i("", !0),
560
+ o(t, u(ae.value, {
516
561
  appear: "",
517
- onBeforeLeave: H,
518
- onAfterEnter: ae,
519
- onAfterLeave: oe
562
+ onBeforeLeave: se,
563
+ onAfterEnter: W,
564
+ onAfterLeave: G
520
565
  }), {
521
- default: k(() => [C(T) ? (g(), r(b(l.as), u({ key: 0 }, I(), {
522
- id: C(E),
523
- ref: ee,
566
+ default: O(() => [S(x) ? (h(), r(y(l.as), u({ key: 0 }, ne(), {
567
+ id: S(w),
568
+ ref: P,
524
569
  role: l.role ?? void 0,
525
- tabindex: R.value ? -1 : void 0,
570
+ tabindex: I.value ? -1 : void 0,
526
571
  "data-sbf-state": "open",
527
- "data-sbf-side": C(j),
528
- "data-sbf-align": C(M),
529
- onKeydown: ye
572
+ "data-sbf-side": S(k),
573
+ "data-sbf-align": S(A),
574
+ onKeydown: De,
575
+ onClick: Ee
530
576
  }), {
531
- default: k(() => [y(n.$slots, "default")]),
577
+ default: O(() => [te(n.$slots, "default")]),
532
578
  _: 3
533
579
  }, 16, [
534
580
  "id",
@@ -539,9 +585,22 @@ var X = (e) => e instanceof Element && !!e.closest("input, textarea, select, [co
539
585
  ])) : i("", !0)]),
540
586
  _: 3
541
587
  }, 16),
542
- B.value ? (g(), a("span", u({ key: 1 }, V, { onFocus: de }), null, 16)) : i("", !0)
588
+ L.value ? (h(), a("span", u({ key: 1 }, R, { onFocus: Y }), null, 16)) : i("", !0)
543
589
  ], 36)) : i("", !0)], 8, ["to", "disabled"]));
544
590
  }
545
591
  });
546
592
  //#endregion
547
- export { q as SbContainer, Z as SbFloating, de as SbReference };
593
+ //#region src/composables/useSbFloating.ts
594
+ function _e() {
595
+ let e = c(W, null);
596
+ if (!e) throw Error("[sb-floating-panel-vue] useSbFloating() must be called in a component inside <SbContainer>.");
597
+ let { isOpen: t, open: n, close: r, toggle: i } = e;
598
+ return {
599
+ isOpen: _(t),
600
+ open: n,
601
+ close: r,
602
+ toggle: i
603
+ };
604
+ }
605
+ //#endregion
606
+ export { X as SbContainer, ge as SbFloating, me as SbReference, _e as useSbFloating };