@vielzeug/refine 2.0.2 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/combobox.cjs +1 -1
  2. package/dist/combobox.cjs.map +1 -1
  3. package/dist/combobox.js +1 -1
  4. package/dist/combobox.js.map +1 -1
  5. package/dist/core/choice-field.cjs +1 -1
  6. package/dist/core/choice-field.cjs.map +1 -1
  7. package/dist/core/choice-field.d.ts.map +1 -1
  8. package/dist/core/choice-field.js +18 -17
  9. package/dist/core/choice-field.js.map +1 -1
  10. package/dist/core/focus.cjs +1 -1
  11. package/dist/core/focus.cjs.map +1 -1
  12. package/dist/core/focus.d.ts +5 -0
  13. package/dist/core/focus.d.ts.map +1 -1
  14. package/dist/core/focus.js +12 -5
  15. package/dist/core/focus.js.map +1 -1
  16. package/dist/core/index.d.ts +3 -3
  17. package/dist/core/index.d.ts.map +1 -1
  18. package/dist/core/option-list.cjs +1 -1
  19. package/dist/core/option-list.cjs.map +1 -1
  20. package/dist/core/option-list.d.ts +5 -0
  21. package/dist/core/option-list.d.ts.map +1 -1
  22. package/dist/core/option-list.js +20 -13
  23. package/dist/core/option-list.js.map +1 -1
  24. package/dist/core/overlay.cjs +1 -1
  25. package/dist/core/overlay.cjs.map +1 -1
  26. package/dist/core/overlay.d.ts +8 -2
  27. package/dist/core/overlay.d.ts.map +1 -1
  28. package/dist/core/overlay.js +1 -1
  29. package/dist/core/overlay.js.map +1 -1
  30. package/dist/custom-elements.json +6325 -6542
  31. package/dist/inputs/select/select.d.ts.map +1 -1
  32. package/dist/menu.cjs +3 -3
  33. package/dist/menu.cjs.map +1 -1
  34. package/dist/menu.js +34 -34
  35. package/dist/menu.js.map +1 -1
  36. package/dist/overlay/menu/menu.d.ts.map +1 -1
  37. package/dist/overlay/shared/use-dialog.cjs +1 -1
  38. package/dist/overlay/shared/use-dialog.cjs.map +1 -1
  39. package/dist/overlay/shared/use-dialog.d.ts.map +1 -1
  40. package/dist/overlay/shared/use-dialog.js +5 -5
  41. package/dist/overlay/shared/use-dialog.js.map +1 -1
  42. package/dist/overlay/shared/use-floating-trigger.cjs.map +1 -1
  43. package/dist/overlay/shared/use-floating-trigger.d.ts.map +1 -1
  44. package/dist/overlay/shared/use-floating-trigger.js.map +1 -1
  45. package/dist/refine.cjs +111 -111
  46. package/dist/refine.cjs.map +1 -1
  47. package/dist/refine.iife.js +111 -111
  48. package/dist/refine.iife.js.map +1 -1
  49. package/dist/refine.js +90 -90
  50. package/dist/refine.js.map +1 -1
  51. package/dist/select.cjs +1 -1
  52. package/dist/select.cjs.map +1 -1
  53. package/dist/select.js +17 -17
  54. package/dist/select.js.map +1 -1
  55. package/package.json +38 -37
  56. package/dist/core/_internal.d.ts +0 -15
  57. package/dist/core/_internal.d.ts.map +0 -1
  58. package/dist/core/paginated-list.d.ts +0 -42
  59. package/dist/core/paginated-list.d.ts.map +0 -1
@@ -1,43 +1,44 @@
1
1
  import { createDirtyTracker as e, createField as t } from "./field-base.js";
2
- import { computed as n, signal as r } from "@vielzeug/ripple";
2
+ import { syncedSignal as n } from "./signals.js";
3
+ import { computed as r } from "@vielzeug/ripple";
3
4
  import { watch as i } from "@vielzeug/ripple/watch";
4
5
  //#region src/core/choice-field.ts
5
6
  var a = (e) => e ? Array.isArray(e) ? e.filter(Boolean) : e.split(",").map((e) => e.trim()).filter(Boolean) : [], o = (o) => {
6
- let s = r([]), c = n(() => !!o.multiple?.value), l = n(() => c.value ? s.value.join(",") : s.value[0] ?? ""), u = (e) => {
7
- let t = c.value ? e : e.slice(0, 1);
7
+ let s = r(() => !!o.multiple?.value), c = (e) => {
8
+ let t = s.value ? e : e.slice(0, 1);
8
9
  return [...new Set(t.filter(Boolean))];
9
- }, d = e(), f = (e) => {
10
- s.value = u(e);
10
+ }, l = n(o.value, o.signal, (e) => c(a(e))), u = r(() => s.value ? l.value.join(",") : l.value[0] ?? ""), d = e(), f = (e) => {
11
+ l.value = c(e);
11
12
  }, p = (e) => {
12
13
  d.markDirty(), f(e);
13
14
  }, m = () => p([]), h = (e) => {
14
- d.markDirty(), s.value = s.value.filter((t) => t !== e);
15
+ d.markDirty(), l.value = l.value.filter((t) => t !== e);
15
16
  }, g = (e) => {
16
- if (c.value) {
17
- s.value.includes(e) || p([...s.value, e]);
17
+ if (s.value) {
18
+ l.value.includes(e) || p([...l.value, e]);
18
19
  return;
19
20
  }
20
21
  p([e]);
21
22
  }, _ = (e) => {
22
- c.value && s.value.includes(e) ? h(e) : g(e);
23
+ s.value && l.value.includes(e) ? h(e) : g(e);
23
24
  }, v = (e) => {
24
25
  f(a(e));
25
- }, y = t(o), b = n(() => o.required?.value && s.value.length === 0 ? { valueMissing: !0 } : null), x = n(() => b.value ? o.requiredMessage?.value || "Please make a selection." : ""), S = i(o.value, v, { immediate: !0 });
26
- if (o.signal.addEventListener("abort", () => S.dispose(), { once: !0 }), o.multiple) {
26
+ }, y = t(o), b = r(() => o.required?.value && l.value.length === 0 ? { valueMissing: !0 } : null), x = r(() => b.value ? o.requiredMessage?.value || "Please make a selection." : "");
27
+ if (o.multiple) {
27
28
  let e = i(o.multiple, () => v(o.value.value));
28
29
  o.signal.addEventListener("abort", () => e.dispose(), { once: !0 });
29
30
  }
30
- let C = s.value, w = () => {
31
- f(d.isDirty ? C : a(o.value.value)), d.clear(), y.triggerValidation("change");
31
+ let S = l.value, C = () => {
32
+ f(d.isDirty ? S : a(o.value.value)), d.clear(), y.triggerValidation("change");
32
33
  };
33
34
  return {
34
35
  ...y,
35
36
  clear: m,
36
- formValue: l,
37
+ formValue: u,
37
38
  removeValue: h,
38
- reset: w,
39
- selectedValue: n(() => s.value[0]),
40
- selectedValues: s,
39
+ reset: C,
40
+ selectedValue: r(() => l.value[0]),
41
+ selectedValues: l,
41
42
  selectValue: g,
42
43
  setValues: p,
43
44
  toggleValue: _,
@@ -1 +1 @@
1
- {"version":3,"file":"choice-field.js","names":[],"sources":["../../src/core/choice-field.ts"],"sourcesContent":["import { computed, type Readable, signal } from '@vielzeug/ripple';\nimport { watch } from '@vielzeug/ripple/watch';\n\nimport { createDirtyTracker, createField, type FieldHandle, type FieldOptions } from './field-base';\n\n// ── Choice change event payload ──────────────────────────────────────────────────────────────\n\nexport type ChoiceChangeDetail = {\n labels: string[];\n originalEvent?: Event;\n values: string[];\n};\n\n// ── Choice field ──────────────────────────────────────────────────────────────────────\n\nexport type ChoiceFieldOptions = FieldOptions & {\n multiple?: Readable<boolean | undefined>;\n /** Marks an empty selection invalid — feeds `validity`/`validationMessage` (see below). */\n required?: Readable<boolean | undefined>;\n /** Message for the empty-selection+required case. Defaults to `'Please make a selection.'`. */\n requiredMessage?: Readable<string | undefined>;\n /** `AbortSignal` from the component lifecycle. All internal subscriptions are disposed on abort. */\n signal: AbortSignal;\n /**\n * Current selection value(s). Accepts a comma-separated string\n * (legacy/single-select form serialisation) or a `string[]` array.\n * The field normalises both formats internally via `parseChoiceValues`.\n */\n value: Readable<string | string[] | undefined>;\n};\n\nexport type ChoiceFieldHandle = FieldHandle & {\n clear: () => void;\n formValue: Readable<string>;\n removeValue: (value: string) => void;\n /**\n * Restores the selection to its native \"default\". See `DirtyTracker` for the two-state\n * rationale.\n */\n reset: () => void;\n selectedValue: Readable<string | undefined>;\n selectedValues: Readable<string[]>;\n selectValue: (value: string) => void;\n setValues: (values: string[]) => void;\n /**\n * Toggles `value` in the selection.\n * - **Multiple mode**: adds if absent, removes if present.\n * - **Single mode**: always selects `value` (acts like `selectValue` — cannot\n * deselect; use `clear()` to reset a single-select field).\n */\n toggleValue: (value: string) => void;\n /** Reactive validation message paired with `validity`. Empty string when valid. */\n validationMessage: Readable<string>;\n /**\n * Reactive `ValidityStateFlags` — `{ valueMissing: true }` while `required` and no option is\n * selected, `null` (valid) otherwise. Pass straight to `useField({ validity: choice.validity })`.\n */\n validity: Readable<ValidityStateFlags | null>;\n};\n\nconst parseChoiceValues = (raw: string | string[] | undefined): string[] => {\n if (!raw) return [];\n\n if (Array.isArray(raw)) return raw.filter(Boolean);\n\n return raw\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean);\n};\n\nexport const createChoiceField = (options: ChoiceFieldOptions): ChoiceFieldHandle => {\n const selectedValues = signal<string[]>([]);\n const isMultiple = computed(() => Boolean(options.multiple?.value));\n const formValue = computed(() =>\n isMultiple.value ? selectedValues.value.join(',') : (selectedValues.value[0] ?? ''),\n );\n\n const normalizeValues = (values: string[]): string[] => {\n const normalized = isMultiple.value ? values : values.slice(0, 1);\n\n return [...new Set(normalized.filter(Boolean))];\n };\n\n const dirtyTracker = createDirtyTracker();\n\n // The actual state setter, shared by every mutation path. Deliberately *not* where the dirty\n // tracker gets marked — `syncFromProp` (below) calls this directly to stay exempt from it,\n // since mirroring an external prop change is never \"the user changed the selection\".\n const applyValues = (values: string[]): void => {\n selectedValues.value = normalizeValues(values);\n };\n\n const setValues = (values: string[]): void => {\n dirtyTracker.markDirty();\n applyValues(values);\n };\n const clear = (): void => setValues([]);\n const removeValue = (value: string): void => {\n dirtyTracker.markDirty();\n selectedValues.value = selectedValues.value.filter((v) => v !== value);\n };\n\n const selectValue = (value: string): void => {\n if (isMultiple.value) {\n if (!selectedValues.value.includes(value)) setValues([...selectedValues.value, value]);\n\n return;\n }\n\n setValues([value]);\n };\n\n const toggleValue = (value: string): void => {\n if (isMultiple.value) {\n if (selectedValues.value.includes(value)) removeValue(value);\n else selectValue(value);\n } else {\n selectValue(value);\n }\n };\n\n const syncFromProp = (nextValue: string | string[] | undefined): void => {\n applyValues(parseChoiceValues(nextValue));\n };\n\n const field = createField(options);\n\n const validity = computed<ValidityStateFlags | null>(() =>\n options.required?.value && selectedValues.value.length === 0 ? { valueMissing: true } : null,\n );\n const validationMessage = computed(() =>\n validity.value ? options.requiredMessage?.value || 'Please make a selection.' : '',\n );\n\n const valueSub = watch(options.value, syncFromProp, { immediate: true });\n\n options.signal.addEventListener('abort', () => valueSub.dispose(), { once: true });\n\n if (options.multiple) {\n const multipleSub = watch(options.multiple, () => syncFromProp(options.value.value));\n\n options.signal.addEventListener('abort', () => multipleSub.dispose(), { once: true });\n }\n\n const initialValues = selectedValues.value;\n const reset = (): void => {\n applyValues(dirtyTracker.isDirty ? initialValues : parseChoiceValues(options.value.value));\n dirtyTracker.clear();\n field.triggerValidation('change');\n };\n\n return {\n ...field,\n clear,\n formValue,\n removeValue,\n reset,\n selectedValue: computed(() => selectedValues.value[0]),\n selectedValues,\n selectValue,\n setValues,\n toggleValue,\n validationMessage,\n validity,\n };\n};\n"],"mappings":";;;;AA4DA,IAAM,KAAqB,MACpB,IAED,MAAM,QAAQ,CAAG,IAAU,EAAI,OAAO,OAAO,IAE1C,EACJ,MAAM,GAAG,CAAC,CACV,KAAK,MAAU,EAAM,KAAK,CAAC,CAAC,CAC5B,OAAO,OAAO,IAPA,CAAC,GAUP,KAAqB,MAAmD;CACnF,IAAM,IAAiB,EAAiB,CAAC,CAAC,GACpC,IAAa,QAAe,EAAQ,EAAQ,UAAU,KAAM,GAC5D,IAAY,QAChB,EAAW,QAAQ,EAAe,MAAM,KAAK,GAAG,IAAK,EAAe,MAAM,MAAM,EAClF,GAEM,KAAmB,MAA+B;EACtD,IAAM,IAAa,EAAW,QAAQ,IAAS,EAAO,MAAM,GAAG,CAAC;EAEhE,OAAO,CAAC,GAAG,IAAI,IAAI,EAAW,OAAO,OAAO,CAAC,CAAC;CAChD,GAEM,IAAe,EAAmB,GAKlC,KAAe,MAA2B;EAC9C,EAAe,QAAQ,EAAgB,CAAM;CAC/C,GAEM,KAAa,MAA2B;EAE5C,AADA,EAAa,UAAU,GACvB,EAAY,CAAM;CACpB,GACM,UAAoB,EAAU,CAAC,CAAC,GAChC,KAAe,MAAwB;EAE3C,AADA,EAAa,UAAU,GACvB,EAAe,QAAQ,EAAe,MAAM,QAAQ,MAAM,MAAM,CAAK;CACvE,GAEM,KAAe,MAAwB;EAC3C,IAAI,EAAW,OAAO;GACpB,AAAK,EAAe,MAAM,SAAS,CAAK,KAAG,EAAU,CAAC,GAAG,EAAe,OAAO,CAAK,CAAC;GAErF;EACF;EAEA,EAAU,CAAC,CAAK,CAAC;CACnB,GAEM,KAAe,MAAwB;EAC3C,AAAI,EAAW,SACT,EAAe,MAAM,SAAS,CAAK,IAAG,EAAY,CAAK,IACtD,EAAY,CAAK;CAI1B,GAEM,KAAgB,MAAmD;EACvE,EAAY,EAAkB,CAAS,CAAC;CAC1C,GAEM,IAAQ,EAAY,CAAO,GAE3B,IAAW,QACf,EAAQ,UAAU,SAAS,EAAe,MAAM,WAAW,IAAI,EAAE,cAAc,GAAK,IAAI,IAC1F,GACM,IAAoB,QACxB,EAAS,QAAQ,EAAQ,iBAAiB,SAAS,6BAA6B,EAClF,GAEM,IAAW,EAAM,EAAQ,OAAO,GAAc,EAAE,WAAW,GAAK,CAAC;CAIvE,IAFA,EAAQ,OAAO,iBAAiB,eAAe,EAAS,QAAQ,GAAG,EAAE,MAAM,GAAK,CAAC,GAE7E,EAAQ,UAAU;EACpB,IAAM,IAAc,EAAM,EAAQ,gBAAgB,EAAa,EAAQ,MAAM,KAAK,CAAC;EAEnF,EAAQ,OAAO,iBAAiB,eAAe,EAAY,QAAQ,GAAG,EAAE,MAAM,GAAK,CAAC;CACtF;CAEA,IAAM,IAAgB,EAAe,OAC/B,UAAoB;EAGxB,AAFA,EAAY,EAAa,UAAU,IAAgB,EAAkB,EAAQ,MAAM,KAAK,CAAC,GACzF,EAAa,MAAM,GACnB,EAAM,kBAAkB,QAAQ;CAClC;CAEA,OAAO;EACL,GAAG;EACH;EACA;EACA;EACA;EACA,eAAe,QAAe,EAAe,MAAM,EAAE;EACrD;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
1
+ {"version":3,"file":"choice-field.js","names":[],"sources":["../../src/core/choice-field.ts"],"sourcesContent":["import { computed, type Readable } from '@vielzeug/ripple';\nimport { watch } from '@vielzeug/ripple/watch';\n\nimport { createDirtyTracker, createField, type FieldHandle, type FieldOptions } from './field-base';\nimport { syncedSignal } from './signals';\n\n// ── Choice change event payload ──────────────────────────────────────────────────────────────\n\nexport type ChoiceChangeDetail = {\n labels: string[];\n originalEvent?: Event;\n values: string[];\n};\n\n// ── Choice field ──────────────────────────────────────────────────────────────────────\n\nexport type ChoiceFieldOptions = FieldOptions & {\n multiple?: Readable<boolean | undefined>;\n /** Marks an empty selection invalid — feeds `validity`/`validationMessage` (see below). */\n required?: Readable<boolean | undefined>;\n /** Message for the empty-selection+required case. Defaults to `'Please make a selection.'`. */\n requiredMessage?: Readable<string | undefined>;\n /** `AbortSignal` from the component lifecycle. All internal subscriptions are disposed on abort. */\n signal: AbortSignal;\n /**\n * Current selection value(s). Accepts a comma-separated string\n * (legacy/single-select form serialisation) or a `string[]` array.\n * The field normalises both formats internally via `parseChoiceValues`.\n */\n value: Readable<string | string[] | undefined>;\n};\n\nexport type ChoiceFieldHandle = FieldHandle & {\n clear: () => void;\n formValue: Readable<string>;\n removeValue: (value: string) => void;\n /**\n * Restores the selection to its native \"default\". See `DirtyTracker` for the two-state\n * rationale.\n */\n reset: () => void;\n selectedValue: Readable<string | undefined>;\n selectedValues: Readable<string[]>;\n selectValue: (value: string) => void;\n setValues: (values: string[]) => void;\n /**\n * Toggles `value` in the selection.\n * - **Multiple mode**: adds if absent, removes if present.\n * - **Single mode**: always selects `value` (acts like `selectValue` — cannot\n * deselect; use `clear()` to reset a single-select field).\n */\n toggleValue: (value: string) => void;\n /** Reactive validation message paired with `validity`. Empty string when valid. */\n validationMessage: Readable<string>;\n /**\n * Reactive `ValidityStateFlags` — `{ valueMissing: true }` while `required` and no option is\n * selected, `null` (valid) otherwise. Pass straight to `useField({ validity: choice.validity })`.\n */\n validity: Readable<ValidityStateFlags | null>;\n};\n\nconst parseChoiceValues = (raw: string | string[] | undefined): string[] => {\n if (!raw) return [];\n\n if (Array.isArray(raw)) return raw.filter(Boolean);\n\n return raw\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean);\n};\n\nexport const createChoiceField = (options: ChoiceFieldOptions): ChoiceFieldHandle => {\n const isMultiple = computed(() => Boolean(options.multiple?.value));\n\n const normalizeValues = (values: string[]): string[] => {\n const normalized = isMultiple.value ? values : values.slice(0, 1);\n\n return [...new Set(normalized.filter(Boolean))];\n };\n\n // Kept in sync with `options.value` via the shared helper (see `checkable.ts`/`text-field.ts`\n // for the same pattern) rather than a hand-rolled signal + watch pair.\n const selectedValues = syncedSignal(options.value, options.signal, (raw) => normalizeValues(parseChoiceValues(raw)));\n const formValue = computed(() =>\n isMultiple.value ? selectedValues.value.join(',') : (selectedValues.value[0] ?? ''),\n );\n\n const dirtyTracker = createDirtyTracker();\n\n // The actual state setter, shared by every mutation path. Deliberately *not* where the dirty\n // tracker gets marked — `syncFromProp` (below) calls this directly to stay exempt from it,\n // since mirroring an external prop change is never \"the user changed the selection\".\n const applyValues = (values: string[]): void => {\n selectedValues.value = normalizeValues(values);\n };\n\n const setValues = (values: string[]): void => {\n dirtyTracker.markDirty();\n applyValues(values);\n };\n const clear = (): void => setValues([]);\n const removeValue = (value: string): void => {\n dirtyTracker.markDirty();\n selectedValues.value = selectedValues.value.filter((v) => v !== value);\n };\n\n const selectValue = (value: string): void => {\n if (isMultiple.value) {\n if (!selectedValues.value.includes(value)) setValues([...selectedValues.value, value]);\n\n return;\n }\n\n setValues([value]);\n };\n\n const toggleValue = (value: string): void => {\n if (isMultiple.value) {\n if (selectedValues.value.includes(value)) removeValue(value);\n else selectValue(value);\n } else {\n selectValue(value);\n }\n };\n\n const syncFromProp = (nextValue: string | string[] | undefined): void => {\n applyValues(parseChoiceValues(nextValue));\n };\n\n const field = createField(options);\n\n const validity = computed<ValidityStateFlags | null>(() =>\n options.required?.value && selectedValues.value.length === 0 ? { valueMissing: true } : null,\n );\n const validationMessage = computed(() =>\n validity.value ? options.requiredMessage?.value || 'Please make a selection.' : '',\n );\n\n if (options.multiple) {\n const multipleSub = watch(options.multiple, () => syncFromProp(options.value.value));\n\n options.signal.addEventListener('abort', () => multipleSub.dispose(), { once: true });\n }\n\n const initialValues = selectedValues.value;\n const reset = (): void => {\n applyValues(dirtyTracker.isDirty ? initialValues : parseChoiceValues(options.value.value));\n dirtyTracker.clear();\n field.triggerValidation('change');\n };\n\n return {\n ...field,\n clear,\n formValue,\n removeValue,\n reset,\n selectedValue: computed(() => selectedValues.value[0]),\n selectedValues,\n selectValue,\n setValues,\n toggleValue,\n validationMessage,\n validity,\n };\n};\n"],"mappings":";;;;;AA6DA,IAAM,KAAqB,MACpB,IAED,MAAM,QAAQ,CAAG,IAAU,EAAI,OAAO,OAAO,IAE1C,EACJ,MAAM,GAAG,CAAC,CACV,KAAK,MAAU,EAAM,KAAK,CAAC,CAAC,CAC5B,OAAO,OAAO,IAPA,CAAC,GAUP,KAAqB,MAAmD;CACnF,IAAM,IAAa,QAAe,EAAQ,EAAQ,UAAU,KAAM,GAE5D,KAAmB,MAA+B;EACtD,IAAM,IAAa,EAAW,QAAQ,IAAS,EAAO,MAAM,GAAG,CAAC;EAEhE,OAAO,CAAC,GAAG,IAAI,IAAI,EAAW,OAAO,OAAO,CAAC,CAAC;CAChD,GAIM,IAAiB,EAAa,EAAQ,OAAO,EAAQ,SAAS,MAAQ,EAAgB,EAAkB,CAAG,CAAC,CAAC,GAC7G,IAAY,QAChB,EAAW,QAAQ,EAAe,MAAM,KAAK,GAAG,IAAK,EAAe,MAAM,MAAM,EAClF,GAEM,IAAe,EAAmB,GAKlC,KAAe,MAA2B;EAC9C,EAAe,QAAQ,EAAgB,CAAM;CAC/C,GAEM,KAAa,MAA2B;EAE5C,AADA,EAAa,UAAU,GACvB,EAAY,CAAM;CACpB,GACM,UAAoB,EAAU,CAAC,CAAC,GAChC,KAAe,MAAwB;EAE3C,AADA,EAAa,UAAU,GACvB,EAAe,QAAQ,EAAe,MAAM,QAAQ,MAAM,MAAM,CAAK;CACvE,GAEM,KAAe,MAAwB;EAC3C,IAAI,EAAW,OAAO;GACpB,AAAK,EAAe,MAAM,SAAS,CAAK,KAAG,EAAU,CAAC,GAAG,EAAe,OAAO,CAAK,CAAC;GAErF;EACF;EAEA,EAAU,CAAC,CAAK,CAAC;CACnB,GAEM,KAAe,MAAwB;EAC3C,AAAI,EAAW,SACT,EAAe,MAAM,SAAS,CAAK,IAAG,EAAY,CAAK,IACtD,EAAY,CAAK;CAI1B,GAEM,KAAgB,MAAmD;EACvE,EAAY,EAAkB,CAAS,CAAC;CAC1C,GAEM,IAAQ,EAAY,CAAO,GAE3B,IAAW,QACf,EAAQ,UAAU,SAAS,EAAe,MAAM,WAAW,IAAI,EAAE,cAAc,GAAK,IAAI,IAC1F,GACM,IAAoB,QACxB,EAAS,QAAQ,EAAQ,iBAAiB,SAAS,6BAA6B,EAClF;CAEA,IAAI,EAAQ,UAAU;EACpB,IAAM,IAAc,EAAM,EAAQ,gBAAgB,EAAa,EAAQ,MAAM,KAAK,CAAC;EAEnF,EAAQ,OAAO,iBAAiB,eAAe,EAAY,QAAQ,GAAG,EAAE,MAAM,GAAK,CAAC;CACtF;CAEA,IAAM,IAAgB,EAAe,OAC/B,UAAoB;EAGxB,AAFA,EAAY,EAAa,UAAU,IAAgB,EAAkB,EAAQ,MAAM,KAAK,CAAC,GACzF,EAAa,MAAM,GACnB,EAAM,kBAAkB,QAAQ;CAClC;CAEA,OAAO;EACL,GAAG;EACH;EACA;EACA;EACA;EACA,eAAe,QAAe,EAAe,MAAM,EAAE;EACrD;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
@@ -1,2 +1,2 @@
1
- function e(e){let t=null,n=null,r=()=>{n!==null&&(cancelAnimationFrame(n),n=null)};return e.signal?.addEventListener(`abort`,r,{once:!0}),{applyInitialFocus(){r();let t=e.getInitialFocusSelector();if(t){let r=e.host.shadowRoot??e.host,i=null;try{i=r.querySelector(t)}catch{return}i&&(n=requestAnimationFrame(()=>{n=null,i.focus()}))}},cancelInitialFocus:r,captureReturnFocus(){t=document.activeElement},restoreFocus(){e.getReturnFocus()!==!1&&t&&(t.focus(),t=null)}}}exports.createFocusManager=e;
1
+ function e(e){let t=null,n=null,r=!1,i=()=>{n!==null&&(cancelAnimationFrame(n),n=null)},a=()=>{r||(r=!0,i(),t=null)};return e.signal?.addEventListener(`abort`,a,{once:!0}),{applyInitialFocus(){i();let t=e.getInitialFocusSelector();if(t){let r=e.host.shadowRoot??e.host,i=null;try{i=r.querySelector(t)}catch{return}i&&(n=requestAnimationFrame(()=>{n=null,i.focus()}))}},cancelInitialFocus:i,captureReturnFocus(){t=document.activeElement},dispose:a,get disposed(){return r},restoreFocus(){e.getReturnFocus()!==!1&&t&&(t.focus(),t=null)},[Symbol.dispose]:a}}exports.createFocusManager=e;
2
2
  //# sourceMappingURL=focus.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"focus.cjs","names":[],"sources":["../../src/core/focus.ts"],"sourcesContent":["// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type FocusManagerOptions = {\n /**\n * Returns the CSS selector for the element that should receive focus on open.\n *\n * **Security note:** this value is passed directly to `querySelector`. It must\n * come from trusted developer-controlled configuration, never from raw user input.\n */\n getInitialFocusSelector: () => string | undefined;\n /**\n * Returns whether focus should be restored to the previously focused element\n * on close. `undefined` or `true` means restore; `false` means skip.\n */\n getReturnFocus: () => boolean | undefined;\n /** Host element used for `querySelector` when resolving the initial focus target. */\n host: HTMLElement;\n /**\n * `AbortSignal` from the component lifecycle. Cancels a pending `applyInitialFocus()`\n * animation frame automatically on abort — prevents a stray `.focus()` call landing on an\n * element that's been disconnected in the meantime (e.g. the dialog closes and unmounts\n * before the deferred focus frame runs).\n */\n signal?: AbortSignal;\n};\n\nexport type FocusManager = {\n /** Move focus to the element matching `getInitialFocusSelector` (deferred one frame). */\n applyInitialFocus: () => void;\n /** Cancel a pending `applyInitialFocus` rAF, if any. */\n cancelInitialFocus: () => void;\n /** Capture the currently focused element so it can be restored later. */\n captureReturnFocus: () => void;\n /** Restore focus to the element captured by `captureReturnFocus`. */\n restoreFocus: () => void;\n};\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Encapsulates the three-step focus lifecycle used by dialogs and drawers:\n * 1. Capture the element that triggered the open.\n * 2. Move focus inside the overlay (optionally to a specific element).\n * 3. Restore focus when the overlay closes.\n */\nexport function createFocusManager(options: FocusManagerOptions): FocusManager {\n let returnFocusEl: HTMLElement | null = null;\n let rafHandle: ReturnType<typeof requestAnimationFrame> | null = null;\n\n const cancelInitialFocus = (): void => {\n if (rafHandle !== null) {\n cancelAnimationFrame(rafHandle);\n rafHandle = null;\n }\n };\n\n options.signal?.addEventListener('abort', cancelInitialFocus, { once: true });\n\n return {\n applyInitialFocus() {\n cancelInitialFocus();\n\n const selector = options.getInitialFocusSelector();\n\n if (selector) {\n // Query the shadow root first (all refine components render into Shadow DOM),\n // falling back to the host's light-DOM tree for non-Shadow contexts.\n const root = options.host.shadowRoot ?? options.host;\n\n let target: HTMLElement | null = null;\n\n try {\n target = root.querySelector<HTMLElement>(selector);\n } catch {\n // Malformed selector (SyntaxError) — skip initial focus rather than throw.\n return;\n }\n\n if (target) {\n rafHandle = requestAnimationFrame(() => {\n rafHandle = null;\n target.focus();\n });\n }\n }\n },\n\n cancelInitialFocus,\n\n captureReturnFocus() {\n returnFocusEl = document.activeElement as HTMLElement;\n },\n\n restoreFocus() {\n if (options.getReturnFocus() !== false && returnFocusEl) {\n returnFocusEl.focus();\n returnFocusEl = null;\n }\n },\n };\n}\n"],"mappings":"AA6CA,SAAgB,EAAmB,EAA4C,CAC7E,IAAI,EAAoC,KACpC,EAA6D,KAE3D,MAAiC,CACjC,IAAc,OAChB,qBAAqB,CAAS,EAC9B,EAAY,KAEhB,EAIA,OAFA,EAAQ,QAAQ,iBAAiB,QAAS,EAAoB,CAAE,KAAM,EAAK,CAAC,EAErE,CACL,mBAAoB,CAClB,EAAmB,EAEnB,IAAM,EAAW,EAAQ,wBAAwB,EAEjD,GAAI,EAAU,CAGZ,IAAM,EAAO,EAAQ,KAAK,YAAc,EAAQ,KAE5C,EAA6B,KAEjC,GAAI,CACF,EAAS,EAAK,cAA2B,CAAQ,CACnD,MAAQ,CAEN,MACF,CAEI,IACF,EAAY,0BAA4B,CACtC,EAAY,KACZ,EAAO,MAAM,CACf,CAAC,EAEL,CACF,EAEA,qBAEA,oBAAqB,CACnB,EAAgB,SAAS,aAC3B,EAEA,cAAe,CACT,EAAQ,eAAe,IAAM,IAAS,IACxC,EAAc,MAAM,EACpB,EAAgB,KAEpB,CACF,CACF"}
1
+ {"version":3,"file":"focus.cjs","names":[],"sources":["../../src/core/focus.ts"],"sourcesContent":["// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type FocusManagerOptions = {\n /**\n * Returns the CSS selector for the element that should receive focus on open.\n *\n * **Security note:** this value is passed directly to `querySelector`. It must\n * come from trusted developer-controlled configuration, never from raw user input.\n */\n getInitialFocusSelector: () => string | undefined;\n /**\n * Returns whether focus should be restored to the previously focused element\n * on close. `undefined` or `true` means restore; `false` means skip.\n */\n getReturnFocus: () => boolean | undefined;\n /** Host element used for `querySelector` when resolving the initial focus target. */\n host: HTMLElement;\n /**\n * `AbortSignal` from the component lifecycle. Cancels a pending `applyInitialFocus()`\n * animation frame automatically on abort — prevents a stray `.focus()` call landing on an\n * element that's been disconnected in the meantime (e.g. the dialog closes and unmounts\n * before the deferred focus frame runs).\n */\n signal?: AbortSignal;\n};\n\nexport type FocusManager = {\n [Symbol.dispose](): void;\n /** Move focus to the element matching `getInitialFocusSelector` (deferred one frame). */\n applyInitialFocus: () => void;\n /** Cancel a pending `applyInitialFocus` rAF, if any. */\n cancelInitialFocus: () => void;\n /** Capture the currently focused element so it can be restored later. */\n captureReturnFocus: () => void;\n /** Cancels any pending initial-focus frame and clears captured return-focus state. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** Restore focus to the element captured by `captureReturnFocus`. */\n restoreFocus: () => void;\n};\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Encapsulates the three-step focus lifecycle used by dialogs and drawers:\n * 1. Capture the element that triggered the open.\n * 2. Move focus inside the overlay (optionally to a specific element).\n * 3. Restore focus when the overlay closes.\n */\nexport function createFocusManager(options: FocusManagerOptions): FocusManager {\n let returnFocusEl: HTMLElement | null = null;\n let rafHandle: ReturnType<typeof requestAnimationFrame> | null = null;\n let disposed = false;\n\n const cancelInitialFocus = (): void => {\n if (rafHandle !== null) {\n cancelAnimationFrame(rafHandle);\n rafHandle = null;\n }\n };\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n cancelInitialFocus();\n returnFocusEl = null;\n };\n\n options.signal?.addEventListener('abort', dispose, { once: true });\n\n return {\n applyInitialFocus() {\n cancelInitialFocus();\n\n const selector = options.getInitialFocusSelector();\n\n if (selector) {\n // Query the shadow root first (all refine components render into Shadow DOM),\n // falling back to the host's light-DOM tree for non-Shadow contexts.\n const root = options.host.shadowRoot ?? options.host;\n\n let target: HTMLElement | null = null;\n\n try {\n target = root.querySelector<HTMLElement>(selector);\n } catch {\n // Malformed selector (SyntaxError) — skip initial focus rather than throw.\n return;\n }\n\n if (target) {\n rafHandle = requestAnimationFrame(() => {\n rafHandle = null;\n target.focus();\n });\n }\n }\n },\n cancelInitialFocus,\n\n captureReturnFocus() {\n returnFocusEl = document.activeElement as HTMLElement;\n },\n\n dispose,\n\n get disposed() {\n return disposed;\n },\n\n restoreFocus() {\n if (options.getReturnFocus() !== false && returnFocusEl) {\n returnFocusEl.focus();\n returnFocusEl = null;\n }\n },\n\n [Symbol.dispose]: dispose,\n };\n}\n"],"mappings":"AAkDA,SAAgB,EAAmB,EAA4C,CAC7E,IAAI,EAAoC,KACpC,EAA6D,KAC7D,EAAW,GAET,MAAiC,CACjC,IAAc,OAChB,qBAAqB,CAAS,EAC9B,EAAY,KAEhB,EAEM,MAAsB,CACtB,IAEJ,EAAW,GACX,EAAmB,EACnB,EAAgB,KAClB,EAIA,OAFA,EAAQ,QAAQ,iBAAiB,QAAS,EAAS,CAAE,KAAM,EAAK,CAAC,EAE1D,CACL,mBAAoB,CAClB,EAAmB,EAEnB,IAAM,EAAW,EAAQ,wBAAwB,EAEjD,GAAI,EAAU,CAGZ,IAAM,EAAO,EAAQ,KAAK,YAAc,EAAQ,KAE5C,EAA6B,KAEjC,GAAI,CACF,EAAS,EAAK,cAA2B,CAAQ,CACnD,MAAQ,CAEN,MACF,CAEI,IACF,EAAY,0BAA4B,CACtC,EAAY,KACZ,EAAO,MAAM,CACf,CAAC,EAEL,CACF,EACA,qBAEA,oBAAqB,CACnB,EAAgB,SAAS,aAC3B,EAEA,UAEA,IAAI,UAAW,CACb,OAAO,CACT,EAEA,cAAe,CACT,EAAQ,eAAe,IAAM,IAAS,IACxC,EAAc,MAAM,EACpB,EAAgB,KAEpB,GAEC,OAAO,SAAU,CACpB,CACF"}
@@ -22,12 +22,17 @@ export type FocusManagerOptions = {
22
22
  signal?: AbortSignal;
23
23
  };
24
24
  export type FocusManager = {
25
+ [Symbol.dispose](): void;
25
26
  /** Move focus to the element matching `getInitialFocusSelector` (deferred one frame). */
26
27
  applyInitialFocus: () => void;
27
28
  /** Cancel a pending `applyInitialFocus` rAF, if any. */
28
29
  cancelInitialFocus: () => void;
29
30
  /** Capture the currently focused element so it can be restored later. */
30
31
  captureReturnFocus: () => void;
32
+ /** Cancels any pending initial-focus frame and clears captured return-focus state. */
33
+ dispose(): void;
34
+ /** `true` after `dispose()` has been called. */
35
+ readonly disposed: boolean;
31
36
  /** Restore focus to the element captured by `captureReturnFocus`. */
32
37
  restoreFocus: () => void;
33
38
  };
@@ -1 +1 @@
1
- {"version":3,"file":"focus.d.ts","sourceRoot":"","sources":["../../src/core/focus.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;;;OAKG;IACH,uBAAuB,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAClD;;;OAGG;IACH,cAAc,EAAE,MAAM,OAAO,GAAG,SAAS,CAAC;IAC1C,qFAAqF;IACrF,IAAI,EAAE,WAAW,CAAC;IAClB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,yFAAyF;IACzF,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,wDAAwD;IACxD,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,yEAAyE;IACzE,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,qEAAqE;IACrE,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B,CAAC;AAIF;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAuD7E"}
1
+ {"version":3,"file":"focus.d.ts","sourceRoot":"","sources":["../../src/core/focus.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;;;OAKG;IACH,uBAAuB,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAClD;;;OAGG;IACH,cAAc,EAAE,MAAM,OAAO,GAAG,SAAS,CAAC;IAC1C,qFAAqF;IACrF,IAAI,EAAE,WAAW,CAAC;IAClB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;IACzB,yFAAyF;IACzF,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,wDAAwD;IACxD,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,yEAAyE;IACzE,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,sFAAsF;IACtF,OAAO,IAAI,IAAI,CAAC;IAChB,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,qEAAqE;IACrE,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B,CAAC;AAIF;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAuE7E"}
@@ -1,11 +1,13 @@
1
1
  //#region src/core/focus.ts
2
2
  function e(e) {
3
- let t = null, n = null, r = () => {
3
+ let t = null, n = null, r = !1, i = () => {
4
4
  n !== null && (cancelAnimationFrame(n), n = null);
5
+ }, a = () => {
6
+ r || (r = !0, i(), t = null);
5
7
  };
6
- return e.signal?.addEventListener("abort", r, { once: !0 }), {
8
+ return e.signal?.addEventListener("abort", a, { once: !0 }), {
7
9
  applyInitialFocus() {
8
- r();
10
+ i();
9
11
  let t = e.getInitialFocusSelector();
10
12
  if (t) {
11
13
  let r = e.host.shadowRoot ?? e.host, i = null;
@@ -19,13 +21,18 @@ function e(e) {
19
21
  }));
20
22
  }
21
23
  },
22
- cancelInitialFocus: r,
24
+ cancelInitialFocus: i,
23
25
  captureReturnFocus() {
24
26
  t = document.activeElement;
25
27
  },
28
+ dispose: a,
29
+ get disposed() {
30
+ return r;
31
+ },
26
32
  restoreFocus() {
27
33
  e.getReturnFocus() !== !1 && t && (t.focus(), t = null);
28
- }
34
+ },
35
+ [Symbol.dispose]: a
29
36
  };
30
37
  }
31
38
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"focus.js","names":[],"sources":["../../src/core/focus.ts"],"sourcesContent":["// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type FocusManagerOptions = {\n /**\n * Returns the CSS selector for the element that should receive focus on open.\n *\n * **Security note:** this value is passed directly to `querySelector`. It must\n * come from trusted developer-controlled configuration, never from raw user input.\n */\n getInitialFocusSelector: () => string | undefined;\n /**\n * Returns whether focus should be restored to the previously focused element\n * on close. `undefined` or `true` means restore; `false` means skip.\n */\n getReturnFocus: () => boolean | undefined;\n /** Host element used for `querySelector` when resolving the initial focus target. */\n host: HTMLElement;\n /**\n * `AbortSignal` from the component lifecycle. Cancels a pending `applyInitialFocus()`\n * animation frame automatically on abort — prevents a stray `.focus()` call landing on an\n * element that's been disconnected in the meantime (e.g. the dialog closes and unmounts\n * before the deferred focus frame runs).\n */\n signal?: AbortSignal;\n};\n\nexport type FocusManager = {\n /** Move focus to the element matching `getInitialFocusSelector` (deferred one frame). */\n applyInitialFocus: () => void;\n /** Cancel a pending `applyInitialFocus` rAF, if any. */\n cancelInitialFocus: () => void;\n /** Capture the currently focused element so it can be restored later. */\n captureReturnFocus: () => void;\n /** Restore focus to the element captured by `captureReturnFocus`. */\n restoreFocus: () => void;\n};\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Encapsulates the three-step focus lifecycle used by dialogs and drawers:\n * 1. Capture the element that triggered the open.\n * 2. Move focus inside the overlay (optionally to a specific element).\n * 3. Restore focus when the overlay closes.\n */\nexport function createFocusManager(options: FocusManagerOptions): FocusManager {\n let returnFocusEl: HTMLElement | null = null;\n let rafHandle: ReturnType<typeof requestAnimationFrame> | null = null;\n\n const cancelInitialFocus = (): void => {\n if (rafHandle !== null) {\n cancelAnimationFrame(rafHandle);\n rafHandle = null;\n }\n };\n\n options.signal?.addEventListener('abort', cancelInitialFocus, { once: true });\n\n return {\n applyInitialFocus() {\n cancelInitialFocus();\n\n const selector = options.getInitialFocusSelector();\n\n if (selector) {\n // Query the shadow root first (all refine components render into Shadow DOM),\n // falling back to the host's light-DOM tree for non-Shadow contexts.\n const root = options.host.shadowRoot ?? options.host;\n\n let target: HTMLElement | null = null;\n\n try {\n target = root.querySelector<HTMLElement>(selector);\n } catch {\n // Malformed selector (SyntaxError) — skip initial focus rather than throw.\n return;\n }\n\n if (target) {\n rafHandle = requestAnimationFrame(() => {\n rafHandle = null;\n target.focus();\n });\n }\n }\n },\n\n cancelInitialFocus,\n\n captureReturnFocus() {\n returnFocusEl = document.activeElement as HTMLElement;\n },\n\n restoreFocus() {\n if (options.getReturnFocus() !== false && returnFocusEl) {\n returnFocusEl.focus();\n returnFocusEl = null;\n }\n },\n };\n}\n"],"mappings":";AA6CA,SAAgB,EAAmB,GAA4C;CAC7E,IAAI,IAAoC,MACpC,IAA6D,MAE3D,UAAiC;EACrC,AAAI,MAAc,SAChB,qBAAqB,CAAS,GAC9B,IAAY;CAEhB;CAIA,OAFA,EAAQ,QAAQ,iBAAiB,SAAS,GAAoB,EAAE,MAAM,GAAK,CAAC,GAErE;EACL,oBAAoB;GAClB,EAAmB;GAEnB,IAAM,IAAW,EAAQ,wBAAwB;GAEjD,IAAI,GAAU;IAGZ,IAAM,IAAO,EAAQ,KAAK,cAAc,EAAQ,MAE5C,IAA6B;IAEjC,IAAI;KACF,IAAS,EAAK,cAA2B,CAAQ;IACnD,QAAQ;KAEN;IACF;IAEA,AAAI,MACF,IAAY,4BAA4B;KAEtC,AADA,IAAY,MACZ,EAAO,MAAM;IACf,CAAC;GAEL;EACF;EAEA;EAEA,qBAAqB;GACnB,IAAgB,SAAS;EAC3B;EAEA,eAAe;GACb,AAAI,EAAQ,eAAe,MAAM,MAAS,MACxC,EAAc,MAAM,GACpB,IAAgB;EAEpB;CACF;AACF"}
1
+ {"version":3,"file":"focus.js","names":[],"sources":["../../src/core/focus.ts"],"sourcesContent":["// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type FocusManagerOptions = {\n /**\n * Returns the CSS selector for the element that should receive focus on open.\n *\n * **Security note:** this value is passed directly to `querySelector`. It must\n * come from trusted developer-controlled configuration, never from raw user input.\n */\n getInitialFocusSelector: () => string | undefined;\n /**\n * Returns whether focus should be restored to the previously focused element\n * on close. `undefined` or `true` means restore; `false` means skip.\n */\n getReturnFocus: () => boolean | undefined;\n /** Host element used for `querySelector` when resolving the initial focus target. */\n host: HTMLElement;\n /**\n * `AbortSignal` from the component lifecycle. Cancels a pending `applyInitialFocus()`\n * animation frame automatically on abort — prevents a stray `.focus()` call landing on an\n * element that's been disconnected in the meantime (e.g. the dialog closes and unmounts\n * before the deferred focus frame runs).\n */\n signal?: AbortSignal;\n};\n\nexport type FocusManager = {\n [Symbol.dispose](): void;\n /** Move focus to the element matching `getInitialFocusSelector` (deferred one frame). */\n applyInitialFocus: () => void;\n /** Cancel a pending `applyInitialFocus` rAF, if any. */\n cancelInitialFocus: () => void;\n /** Capture the currently focused element so it can be restored later. */\n captureReturnFocus: () => void;\n /** Cancels any pending initial-focus frame and clears captured return-focus state. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** Restore focus to the element captured by `captureReturnFocus`. */\n restoreFocus: () => void;\n};\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Encapsulates the three-step focus lifecycle used by dialogs and drawers:\n * 1. Capture the element that triggered the open.\n * 2. Move focus inside the overlay (optionally to a specific element).\n * 3. Restore focus when the overlay closes.\n */\nexport function createFocusManager(options: FocusManagerOptions): FocusManager {\n let returnFocusEl: HTMLElement | null = null;\n let rafHandle: ReturnType<typeof requestAnimationFrame> | null = null;\n let disposed = false;\n\n const cancelInitialFocus = (): void => {\n if (rafHandle !== null) {\n cancelAnimationFrame(rafHandle);\n rafHandle = null;\n }\n };\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n cancelInitialFocus();\n returnFocusEl = null;\n };\n\n options.signal?.addEventListener('abort', dispose, { once: true });\n\n return {\n applyInitialFocus() {\n cancelInitialFocus();\n\n const selector = options.getInitialFocusSelector();\n\n if (selector) {\n // Query the shadow root first (all refine components render into Shadow DOM),\n // falling back to the host's light-DOM tree for non-Shadow contexts.\n const root = options.host.shadowRoot ?? options.host;\n\n let target: HTMLElement | null = null;\n\n try {\n target = root.querySelector<HTMLElement>(selector);\n } catch {\n // Malformed selector (SyntaxError) — skip initial focus rather than throw.\n return;\n }\n\n if (target) {\n rafHandle = requestAnimationFrame(() => {\n rafHandle = null;\n target.focus();\n });\n }\n }\n },\n cancelInitialFocus,\n\n captureReturnFocus() {\n returnFocusEl = document.activeElement as HTMLElement;\n },\n\n dispose,\n\n get disposed() {\n return disposed;\n },\n\n restoreFocus() {\n if (options.getReturnFocus() !== false && returnFocusEl) {\n returnFocusEl.focus();\n returnFocusEl = null;\n }\n },\n\n [Symbol.dispose]: dispose,\n };\n}\n"],"mappings":";AAkDA,SAAgB,EAAmB,GAA4C;CAC7E,IAAI,IAAoC,MACpC,IAA6D,MAC7D,IAAW,IAET,UAAiC;EACrC,AAAI,MAAc,SAChB,qBAAqB,CAAS,GAC9B,IAAY;CAEhB,GAEM,UAAsB;EACtB,MAEJ,IAAW,IACX,EAAmB,GACnB,IAAgB;CAClB;CAIA,OAFA,EAAQ,QAAQ,iBAAiB,SAAS,GAAS,EAAE,MAAM,GAAK,CAAC,GAE1D;EACL,oBAAoB;GAClB,EAAmB;GAEnB,IAAM,IAAW,EAAQ,wBAAwB;GAEjD,IAAI,GAAU;IAGZ,IAAM,IAAO,EAAQ,KAAK,cAAc,EAAQ,MAE5C,IAA6B;IAEjC,IAAI;KACF,IAAS,EAAK,cAA2B,CAAQ;IACnD,QAAQ;KAEN;IACF;IAEA,AAAI,MACF,IAAY,4BAA4B;KAEtC,AADA,IAAY,MACZ,EAAO,MAAM;IACf,CAAC;GAEL;EACF;EACA;EAEA,qBAAqB;GACnB,IAAgB,SAAS;EAC3B;EAEA;EAEA,IAAI,WAAW;GACb,OAAO;EACT;EAEA,eAAe;GACb,AAAI,EAAQ,eAAe,MAAM,MAAS,MACxC,EAAc,MAAM,GACpB,IAAgB;EAEpB;GAEC,OAAO,UAAU;CACpB;AACF"}
@@ -8,13 +8,13 @@ export { lifecycleSignal } from './ore';
8
8
  export { createAutoResize, type AutoResizeControl, type AutoResizeOptions } from './auto-resize';
9
9
  export { bindRefCallback, type RefCallback } from './ref-callback';
10
10
  export { setAriaReflection, type AriaReflectionProperty } from './aria-reflection';
11
+ export { createFocusManager, type FocusManager, type FocusManagerOptions } from './focus';
11
12
  export { counterClassName, createAssistiveState, createDirtyTracker, createErrorHelperState, createField, createLabelState, type AssistiveStateHandle, type AssistiveStateOptions, type ControlValidationMode, type CounterOptions, type CounterState, type DirtyTracker, type ErrorHelperOptions, type ErrorHelperState, type FieldHandle, type FieldOptions, type LabelPlacement, type LabelStateHandle, type LabelStateOptions, type ValidationTrigger, } from './field-base';
12
13
  export { createTextField, type TextFieldDetach, type TextFieldHandle, type TextFieldOptions } from './text-field';
13
14
  export { createChoiceField, type ChoiceChangeDetail, type ChoiceFieldHandle, type ChoiceFieldOptions, } from './choice-field';
14
15
  export { createCheckable, type CheckableChangePayload, type CheckableHandle, type CheckableOptions } from './checkable';
15
16
  export { createComposerControl, type ComposerControl, type ComposerControlOptions, type SendShortcut, } from './composer';
16
- export { type DialogCloseReason, type DropdownCloseReason, type OverlayCloseDetail, type OverlayOpenChangeDetail, type OverlayOpenDetail, type OverlayOpenReason, } from './overlay';
17
- export { createPaginatedList, type PaginatedListHandle, type PaginatedListOptions } from './paginated-list';
17
+ export { createOutsidePointerDismissal, restoreTriggerFocus, type DialogCloseReason, type DropdownCloseReason, type OutsidePointerDismissalOptions, type OverlayCloseDetail, type OverlayOpenChangeDetail, type OverlayOpenDetail, type OverlayOpenReason, } from './overlay';
18
18
  export { type ListKeyAction, type ListNavigationAction } from './nav';
19
19
  export { createSpinnerControl, type SpinnerControl, type SpinnerControlOptions } from './spinner';
20
20
  export { createSliderControl, type SliderControl, type SliderControlOptions } from './slider';
@@ -26,7 +26,7 @@ export { toFiniteNumber, toFiniteNumberOr, toPositiveStep } from './numbers';
26
26
  export { createDropdownPositioner, type DropdownPositionerOptions, type OverlayPositioner } from './positioner';
27
27
  export { createInteraction, dispatchKeyboardAction, type Interaction, type InteractionOptions, type KeyboardDispatchOptions, type PressTrigger, } from './keyboard';
28
28
  export { createListControl, type ListControl, type ListNavigationOptions } from './nav';
29
- export { createTypeahead, type Typeahead, type TypeaheadOptions } from './typeahead';
29
+ export { createListboxDropdown, type ListboxDropdown, type ListboxDropdownOptions, type ListboxDropdownPlacementOptions, } from './option-list';
30
30
  export { elementDirection } from './direction';
31
31
  export { parseStringTriggers } from './parse';
32
32
  export { syncedSignal } from './signals';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("../errors.cjs"),t=require("./positioner.cjs"),n=require("./nav.cjs"),r=require("./overlay.cjs");let i=require("@vielzeug/ripple");var a=a=>{if(typeof a.getBoundary!=`function`)throw new e.RefineConfigError(`createListboxDropdown: getBoundary is required`);if(typeof a.getPanel!=`function`)throw new e.RefineConfigError(`createListboxDropdown: getPanel is required`);if(typeof a.getReference!=`function`)throw new e.RefineConfigError(`createListboxDropdown: getReference is required`);let o=(0,i.signal)(!1),s=(0,i.computed)(()=>String(o.value)),c=null,l=t.createDropdownPositioner({...a.positioning,getFloating:a.getPanel,getReference:a.getReference}),u=()=>{a.getFocusedOptionElement?.()?.scrollIntoView({block:`nearest`})},d=n.createListControl({disabled:(0,i.computed)(()=>!o.value),getItemLabel:a.getItemLabel,getItems:a.getItems,isItemDisabled:a.isItemDisabled,keys:a.keys,loop:a.loop??!0,onNavigate:(e,t,n)=>{u(),t>=0&&a.onNavigate?.(e,t,n)},orientation:a.orientation,signal:a.signal}),f=(0,i.computed)(()=>{let e=d.focusedIndex.value;return a.getOptionId&&o.value&&e>=0?a.getOptionId(e):null}),p=()=>typeof a.restoreFocus==`function`?a.restoreFocus():a.restoreFocus??!0,m=(e=`programmatic`,t=!0)=>{o.value&&(o.value=!1,d.reset(),c?.(),c=null,t&&p()&&a.getTrigger&&r.restoreFocus(a.getTrigger),a.onClose?.(e))},h=(e=`programmatic`)=>{a.isDisabled?.()||o.value||(o.value=!0,l.update(),c=l.startAutoUpdate?.()??null,a.onOpen?.(e))};return r.createOutsidePointerDismissal({getTargets:()=>[a.getBoundary(),a.getPanel()],isActive:()=>o.value,onDismiss:()=>m(`outsideClick`),signal:a.signal}),a.signal.addEventListener(`abort`,()=>{o.value&&(o.value=!1,d.reset(),c?.(),c=null)},{once:!0}),{ariaActiveDescendant:f,ariaExpanded:s,close:m,focusedIndex:d.focusedIndex,getActiveItem:d.getActiveItem,handleKeydown:e=>o.value?e.key===`Escape`?(e.preventDefault(),m(`escape`),!0):d.handleKeydown(e):!1,isOpen:o,navigate:d.navigate,open:h,scrollFocusedIntoView:u,set:d.set,toggle:(e=`click`,t=`trigger`)=>{o.value?m(t):h(e)},updatePosition:l.update}};exports.createListboxDropdown=a;
1
+ const e=require("../errors.cjs"),t=require("./overlay.cjs"),n=require("./positioner.cjs"),r=require("./nav.cjs");let i=require("@vielzeug/ripple");var a=a=>{if(typeof a.getBoundary!=`function`)throw new e.RefineConfigError(`createListboxDropdown: getBoundary is required`);if(typeof a.getPanel!=`function`)throw new e.RefineConfigError(`createListboxDropdown: getPanel is required`);if(typeof a.getReference!=`function`)throw new e.RefineConfigError(`createListboxDropdown: getReference is required`);let o=(0,i.signal)(!1),s=(0,i.computed)(()=>String(o.value)),c=null,l=n.createDropdownPositioner({...a.positioning,getFloating:a.getPanel,getReference:a.getReference}),u=()=>{a.getFocusedOptionElement?.()?.scrollIntoView({block:`nearest`})},d=r.createListControl({disabled:(0,i.computed)(()=>!o.value),getItemLabel:a.getItemLabel,getItems:a.getItems,isItemDisabled:a.isItemDisabled,keys:a.keys,loop:a.loop??!0,onNavigate:(e,t,n)=>{u(),t>=0&&a.onNavigate?.(e,t,n)},orientation:a.orientation,signal:a.signal}),f=(0,i.computed)(()=>{let e=d.focusedIndex.value;return a.getOptionId&&o.value&&e>=0?a.getOptionId(e):null}),p=()=>typeof a.restoreFocus==`function`?a.restoreFocus():a.restoreFocus??!0,m=(e=`programmatic`,n=!0)=>{o.value&&(o.value=!1,d.reset(),c?.(),c=null,n&&p()&&a.getTrigger&&t.restoreTriggerFocus(a.getTrigger),a.onClose?.(e))},h=(e=`programmatic`)=>{a.isDisabled?.()||o.value||(o.value=!0,l.update(),c=l.startAutoUpdate?.()??null,a.onOpen?.(e))},g=(e=`click`,t=`trigger`)=>{o.value?m(t):h(e)};t.createOutsidePointerDismissal({getTargets:()=>[a.getBoundary(),a.getPanel()],isActive:()=>o.value,onDismiss:()=>m(`outsideClick`),signal:a.signal});let _=!1,v=()=>{_||(_=!0,o.value&&(o.value=!1,d.reset(),c?.(),c=null))};return a.signal.addEventListener(`abort`,v,{once:!0}),{ariaActiveDescendant:f,ariaExpanded:s,close:m,dispose:v,get disposed(){return _},focusedIndex:d.focusedIndex,getActiveItem:d.getActiveItem,handleKeydown:e=>o.value?e.key===`Escape`?(e.preventDefault(),m(`escape`),!0):d.handleKeydown(e):!1,isOpen:o,navigate:d.navigate,open:h,scrollFocusedIntoView:u,set:d.set,[Symbol.dispose]:v,toggle:g,updatePosition:l.update}};exports.createListboxDropdown=a;
2
2
  //# sourceMappingURL=option-list.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"option-list.cjs","names":[],"sources":["../../src/core/option-list.ts"],"sourcesContent":["import { computed, type Readable, signal } from '@vielzeug/ripple';\n\nimport { RefineConfigError } from '../errors';\nimport { createListControl, type ListKeyAction, type ListNavigationAction } from './nav';\nimport {\n createOutsidePointerDismissal,\n restoreFocus,\n type DropdownCloseReason,\n type OverlayOpenReason,\n} from './overlay';\nimport { createDropdownPositioner, type DropdownPositionerOptions } from './positioner';\n\nexport type ListboxItem = object;\n\n/** Placement-only options for the listbox dropdown positioner. */\nexport type ListboxDropdownPlacementOptions = Omit<DropdownPositionerOptions, 'getFloating' | 'getReference'>;\n\nexport type ListboxDropdownOptions<T extends ListboxItem> = {\n getBoundary: () => HTMLElement | null;\n getFocusedOptionElement?: () => HTMLElement | null;\n getItemLabel?: (item: T, index: number) => string;\n getItems: () => T[];\n getOptionId?: (index: number) => string;\n getPanel: () => HTMLElement | null;\n getReference: () => HTMLElement | null;\n getTrigger?: () => HTMLElement | null;\n isDisabled?: () => boolean;\n isItemDisabled?: (item: T, index: number) => boolean;\n keys?: Partial<Record<ListNavigationAction, string[]>>;\n loop?: boolean;\n onClose?: (reason: DropdownCloseReason) => void;\n onNavigate?: (action: ListKeyAction, index: number, event?: KeyboardEvent) => void;\n onOpen?: (reason: OverlayOpenReason) => void;\n orientation?: 'both' | 'horizontal' | 'vertical';\n positioning?: ListboxDropdownPlacementOptions;\n restoreFocus?: boolean | (() => boolean);\n signal: AbortSignal;\n};\n\nexport type ListboxDropdown<T extends ListboxItem> = {\n readonly ariaActiveDescendant: Readable<string | null>;\n readonly ariaExpanded: Readable<string>;\n close(reason?: DropdownCloseReason): void;\n readonly focusedIndex: Readable<number>;\n getActiveItem(): T | undefined;\n handleKeydown(event: KeyboardEvent): boolean;\n readonly isOpen: Readable<boolean>;\n navigate(action: ListNavigationAction): number;\n open(reason?: OverlayOpenReason): void;\n scrollFocusedIntoView(): void;\n set(index: number): number;\n toggle(openReason?: OverlayOpenReason, closeReason?: DropdownCloseReason): void;\n updatePosition(): void;\n};\n\n/**\n * Internal listbox dropdown behavior for select and combobox. It owns only\n * listbox interactions; menu and other overlay families keep their own logic.\n */\nexport const createListboxDropdown = <T extends ListboxItem>(\n options: ListboxDropdownOptions<T>,\n): ListboxDropdown<T> => {\n if (typeof options.getBoundary !== 'function') {\n throw new RefineConfigError('createListboxDropdown: getBoundary is required');\n }\n\n if (typeof options.getPanel !== 'function') {\n throw new RefineConfigError('createListboxDropdown: getPanel is required');\n }\n\n if (typeof options.getReference !== 'function') {\n throw new RefineConfigError('createListboxDropdown: getReference is required');\n }\n\n const isOpen = signal(false);\n const ariaExpanded = computed(() => String(isOpen.value));\n let stopPositioning: (() => void) | null = null;\n\n const positioner = createDropdownPositioner({\n ...options.positioning,\n getFloating: options.getPanel,\n getReference: options.getReference,\n });\n\n const scrollFocusedIntoView = (): void => {\n options.getFocusedOptionElement?.()?.scrollIntoView({ block: 'nearest' });\n };\n\n const list = createListControl<T>({\n disabled: computed(() => !isOpen.value),\n getItemLabel: options.getItemLabel,\n getItems: options.getItems,\n isItemDisabled: options.isItemDisabled,\n keys: options.keys,\n loop: options.loop ?? true,\n onNavigate: (action, index, event) => {\n scrollFocusedIntoView();\n\n if (index >= 0) options.onNavigate?.(action, index, event);\n },\n orientation: options.orientation,\n signal: options.signal,\n });\n\n const ariaActiveDescendant = computed<string | null>(() => {\n const index = list.focusedIndex.value;\n\n return options.getOptionId && isOpen.value && index >= 0 ? options.getOptionId(index) : null;\n });\n\n const shouldRestoreFocus = (): boolean => {\n return typeof options.restoreFocus === 'function' ? options.restoreFocus() : (options.restoreFocus ?? true);\n };\n\n const close = (reason: DropdownCloseReason = 'programmatic', shouldRestore = true): void => {\n if (!isOpen.value) return;\n\n isOpen.value = false;\n list.reset();\n stopPositioning?.();\n stopPositioning = null;\n\n if (shouldRestore && shouldRestoreFocus() && options.getTrigger) restoreFocus(options.getTrigger);\n\n options.onClose?.(reason);\n };\n\n const open = (reason: OverlayOpenReason = 'programmatic'): void => {\n if (options.isDisabled?.() || isOpen.value) return;\n\n isOpen.value = true;\n positioner.update();\n stopPositioning = positioner.startAutoUpdate?.() ?? null;\n options.onOpen?.(reason);\n };\n\n const toggle = (openReason: OverlayOpenReason = 'click', closeReason: DropdownCloseReason = 'trigger'): void => {\n if (isOpen.value) close(closeReason);\n else open(openReason);\n };\n\n createOutsidePointerDismissal({\n getTargets: () => [options.getBoundary(), options.getPanel()],\n isActive: () => isOpen.value,\n onDismiss: () => close('outsideClick'),\n signal: options.signal,\n });\n\n options.signal.addEventListener(\n 'abort',\n () => {\n if (!isOpen.value) return;\n\n isOpen.value = false;\n list.reset();\n stopPositioning?.();\n stopPositioning = null;\n },\n { once: true },\n );\n\n const handleKeydown = (event: KeyboardEvent): boolean => {\n if (!isOpen.value) return false;\n\n if (event.key === 'Escape') {\n event.preventDefault();\n close('escape');\n\n return true;\n }\n\n return list.handleKeydown(event);\n };\n\n return {\n ariaActiveDescendant,\n ariaExpanded,\n close,\n focusedIndex: list.focusedIndex,\n getActiveItem: list.getActiveItem,\n handleKeydown,\n isOpen,\n navigate: list.navigate,\n open,\n scrollFocusedIntoView,\n set: list.set,\n toggle,\n updatePosition: positioner.update,\n };\n};\n"],"mappings":"mJA2DA,IAAa,EACX,GACuB,CACvB,GAAI,OAAO,EAAQ,aAAgB,WACjC,MAAM,IAAI,EAAA,kBAAkB,gDAAgD,EAG9E,GAAI,OAAO,EAAQ,UAAa,WAC9B,MAAM,IAAI,EAAA,kBAAkB,6CAA6C,EAG3E,GAAI,OAAO,EAAQ,cAAiB,WAClC,MAAM,IAAI,EAAA,kBAAkB,iDAAiD,EAG/E,IAAM,GAAA,EAAS,EAAA,OAAA,CAAO,EAAK,EACrB,GAAA,EAAe,EAAA,SAAA,KAAe,OAAO,EAAO,KAAK,CAAC,EACpD,EAAuC,KAErC,EAAa,EAAA,yBAAyB,CAC1C,GAAG,EAAQ,YACX,YAAa,EAAQ,SACrB,aAAc,EAAQ,YACxB,CAAC,EAEK,MAAoC,CACxC,EAAQ,0BAA0B,CAAC,EAAE,eAAe,CAAE,MAAO,SAAU,CAAC,CAC1E,EAEM,EAAO,EAAA,kBAAqB,CAChC,UAAA,EAAU,EAAA,SAAA,KAAe,CAAC,EAAO,KAAK,EACtC,aAAc,EAAQ,aACtB,SAAU,EAAQ,SAClB,eAAgB,EAAQ,eACxB,KAAM,EAAQ,KACd,KAAM,EAAQ,MAAQ,GACtB,YAAa,EAAQ,EAAO,IAAU,CACpC,EAAsB,EAElB,GAAS,GAAG,EAAQ,aAAa,EAAQ,EAAO,CAAK,CAC3D,EACA,YAAa,EAAQ,YACrB,OAAQ,EAAQ,MAClB,CAAC,EAEK,GAAA,EAAuB,EAAA,SAAA,KAA8B,CACzD,IAAM,EAAQ,EAAK,aAAa,MAEhC,OAAO,EAAQ,aAAe,EAAO,OAAS,GAAS,EAAI,EAAQ,YAAY,CAAK,EAAI,IAC1F,CAAC,EAEK,MACG,OAAO,EAAQ,cAAiB,WAAa,EAAQ,aAAa,EAAK,EAAQ,cAAgB,GAGlG,GAAS,EAA8B,eAAgB,EAAgB,KAAe,CACrF,EAAO,QAEZ,EAAO,MAAQ,GACf,EAAK,MAAM,EACX,IAAkB,EAClB,EAAkB,KAEd,GAAiB,EAAmB,GAAK,EAAQ,YAAY,EAAA,aAAa,EAAQ,UAAU,EAEhG,EAAQ,UAAU,CAAM,EAC1B,EAEM,GAAQ,EAA4B,iBAAyB,CAC7D,EAAQ,aAAa,GAAK,EAAO,QAErC,EAAO,MAAQ,GACf,EAAW,OAAO,EAClB,EAAkB,EAAW,kBAAkB,GAAK,KACpD,EAAQ,SAAS,CAAM,EACzB,EAwCA,OAjCA,EAAA,8BAA8B,CAC5B,eAAkB,CAAC,EAAQ,YAAY,EAAG,EAAQ,SAAS,CAAC,EAC5D,aAAgB,EAAO,MACvB,cAAiB,EAAM,cAAc,EACrC,OAAQ,EAAQ,MAClB,CAAC,EAED,EAAQ,OAAO,iBACb,YACM,CACC,EAAO,QAEZ,EAAO,MAAQ,GACf,EAAK,MAAM,EACX,IAAkB,EAClB,EAAkB,KACpB,EACA,CAAE,KAAM,EAAK,CACf,EAeO,CACL,uBACA,eACA,QACA,aAAc,EAAK,aACnB,cAAe,EAAK,cACpB,cAnBqB,GAChB,EAAO,MAER,EAAM,MAAQ,UAChB,EAAM,eAAe,EACrB,EAAM,QAAQ,EAEP,IAGF,EAAK,cAAc,CAAK,EATL,GAmB1B,SACA,SAAU,EAAK,SACf,OACA,wBACA,IAAK,EAAK,IACV,QAlDc,EAAgC,QAAS,EAAmC,YAAoB,CAC1G,EAAO,MAAO,EAAM,CAAW,EAC9B,EAAK,CAAU,CACtB,EAgDE,eAAgB,EAAW,MAC7B,CACF"}
1
+ {"version":3,"file":"option-list.cjs","names":[],"sources":["../../src/core/option-list.ts"],"sourcesContent":["import { computed, type Readable, signal } from '@vielzeug/ripple';\n\nimport { RefineConfigError } from '../errors';\nimport { createListControl, type ListKeyAction, type ListNavigationAction } from './nav';\nimport {\n createOutsidePointerDismissal,\n restoreTriggerFocus,\n type DropdownCloseReason,\n type OverlayOpenReason,\n} from './overlay';\nimport { createDropdownPositioner, type DropdownPositionerOptions } from './positioner';\n\nexport type ListboxItem = object;\n\n/** Placement-only options for the listbox dropdown positioner. */\nexport type ListboxDropdownPlacementOptions = Omit<DropdownPositionerOptions, 'getFloating' | 'getReference'>;\n\nexport type ListboxDropdownOptions<T extends ListboxItem> = {\n getBoundary: () => HTMLElement | null;\n getFocusedOptionElement?: () => HTMLElement | null;\n getItemLabel?: (item: T, index: number) => string;\n getItems: () => T[];\n getOptionId?: (index: number) => string;\n getPanel: () => HTMLElement | null;\n getReference: () => HTMLElement | null;\n getTrigger?: () => HTMLElement | null;\n isDisabled?: () => boolean;\n isItemDisabled?: (item: T, index: number) => boolean;\n keys?: Partial<Record<ListNavigationAction, string[]>>;\n loop?: boolean;\n onClose?: (reason: DropdownCloseReason) => void;\n onNavigate?: (action: ListKeyAction, index: number, event?: KeyboardEvent) => void;\n onOpen?: (reason: OverlayOpenReason) => void;\n orientation?: 'both' | 'horizontal' | 'vertical';\n positioning?: ListboxDropdownPlacementOptions;\n restoreFocus?: boolean | (() => boolean);\n signal: AbortSignal;\n};\n\nexport type ListboxDropdown<T extends ListboxItem> = {\n [Symbol.dispose](): void;\n readonly ariaActiveDescendant: Readable<string | null>;\n readonly ariaExpanded: Readable<string>;\n close(reason?: DropdownCloseReason): void;\n /** Closes the dropdown (without restoring focus) and disposes list navigation state. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n readonly focusedIndex: Readable<number>;\n getActiveItem(): T | undefined;\n handleKeydown(event: KeyboardEvent): boolean;\n readonly isOpen: Readable<boolean>;\n navigate(action: ListNavigationAction): number;\n open(reason?: OverlayOpenReason): void;\n scrollFocusedIntoView(): void;\n set(index: number): number;\n toggle(openReason?: OverlayOpenReason, closeReason?: DropdownCloseReason): void;\n updatePosition(): void;\n};\n\n/**\n * Internal listbox dropdown behavior for select and combobox. It owns only\n * listbox interactions; menu and other overlay families keep their own logic.\n */\nexport const createListboxDropdown = <T extends ListboxItem>(\n options: ListboxDropdownOptions<T>,\n): ListboxDropdown<T> => {\n if (typeof options.getBoundary !== 'function') {\n throw new RefineConfigError('createListboxDropdown: getBoundary is required');\n }\n\n if (typeof options.getPanel !== 'function') {\n throw new RefineConfigError('createListboxDropdown: getPanel is required');\n }\n\n if (typeof options.getReference !== 'function') {\n throw new RefineConfigError('createListboxDropdown: getReference is required');\n }\n\n const isOpen = signal(false);\n const ariaExpanded = computed(() => String(isOpen.value));\n let stopPositioning: (() => void) | null = null;\n\n const positioner = createDropdownPositioner({\n ...options.positioning,\n getFloating: options.getPanel,\n getReference: options.getReference,\n });\n\n const scrollFocusedIntoView = (): void => {\n options.getFocusedOptionElement?.()?.scrollIntoView({ block: 'nearest' });\n };\n\n const list = createListControl<T>({\n disabled: computed(() => !isOpen.value),\n getItemLabel: options.getItemLabel,\n getItems: options.getItems,\n isItemDisabled: options.isItemDisabled,\n keys: options.keys,\n loop: options.loop ?? true,\n onNavigate: (action, index, event) => {\n scrollFocusedIntoView();\n\n if (index >= 0) options.onNavigate?.(action, index, event);\n },\n orientation: options.orientation,\n signal: options.signal,\n });\n\n const ariaActiveDescendant = computed<string | null>(() => {\n const index = list.focusedIndex.value;\n\n return options.getOptionId && isOpen.value && index >= 0 ? options.getOptionId(index) : null;\n });\n\n const shouldRestoreFocus = (): boolean => {\n return typeof options.restoreFocus === 'function' ? options.restoreFocus() : (options.restoreFocus ?? true);\n };\n\n const close = (reason: DropdownCloseReason = 'programmatic', shouldRestore = true): void => {\n if (!isOpen.value) return;\n\n isOpen.value = false;\n list.reset();\n stopPositioning?.();\n stopPositioning = null;\n\n if (shouldRestore && shouldRestoreFocus() && options.getTrigger) restoreTriggerFocus(options.getTrigger);\n\n options.onClose?.(reason);\n };\n\n const open = (reason: OverlayOpenReason = 'programmatic'): void => {\n if (options.isDisabled?.() || isOpen.value) return;\n\n isOpen.value = true;\n positioner.update();\n stopPositioning = positioner.startAutoUpdate?.() ?? null;\n options.onOpen?.(reason);\n };\n\n const toggle = (openReason: OverlayOpenReason = 'click', closeReason: DropdownCloseReason = 'trigger'): void => {\n if (isOpen.value) close(closeReason);\n else open(openReason);\n };\n\n createOutsidePointerDismissal({\n getTargets: () => [options.getBoundary(), options.getPanel()],\n isActive: () => isOpen.value,\n onDismiss: () => close('outsideClick'),\n signal: options.signal,\n });\n\n let disposed = false;\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n\n if (isOpen.value) {\n isOpen.value = false;\n list.reset();\n stopPositioning?.();\n stopPositioning = null;\n }\n };\n\n options.signal.addEventListener('abort', dispose, { once: true });\n\n const handleKeydown = (event: KeyboardEvent): boolean => {\n if (!isOpen.value) return false;\n\n if (event.key === 'Escape') {\n event.preventDefault();\n close('escape');\n\n return true;\n }\n\n return list.handleKeydown(event);\n };\n\n return {\n ariaActiveDescendant,\n ariaExpanded,\n close,\n dispose,\n get disposed() {\n return disposed;\n },\n focusedIndex: list.focusedIndex,\n getActiveItem: list.getActiveItem,\n handleKeydown,\n isOpen,\n navigate: list.navigate,\n open,\n scrollFocusedIntoView,\n set: list.set,\n [Symbol.dispose]: dispose,\n toggle,\n updatePosition: positioner.update,\n };\n};\n"],"mappings":"mJAgEA,IAAa,EACX,GACuB,CACvB,GAAI,OAAO,EAAQ,aAAgB,WACjC,MAAM,IAAI,EAAA,kBAAkB,gDAAgD,EAG9E,GAAI,OAAO,EAAQ,UAAa,WAC9B,MAAM,IAAI,EAAA,kBAAkB,6CAA6C,EAG3E,GAAI,OAAO,EAAQ,cAAiB,WAClC,MAAM,IAAI,EAAA,kBAAkB,iDAAiD,EAG/E,IAAM,GAAA,EAAS,EAAA,OAAA,CAAO,EAAK,EACrB,GAAA,EAAe,EAAA,SAAA,KAAe,OAAO,EAAO,KAAK,CAAC,EACpD,EAAuC,KAErC,EAAa,EAAA,yBAAyB,CAC1C,GAAG,EAAQ,YACX,YAAa,EAAQ,SACrB,aAAc,EAAQ,YACxB,CAAC,EAEK,MAAoC,CACxC,EAAQ,0BAA0B,CAAC,EAAE,eAAe,CAAE,MAAO,SAAU,CAAC,CAC1E,EAEM,EAAO,EAAA,kBAAqB,CAChC,UAAA,EAAU,EAAA,SAAA,KAAe,CAAC,EAAO,KAAK,EACtC,aAAc,EAAQ,aACtB,SAAU,EAAQ,SAClB,eAAgB,EAAQ,eACxB,KAAM,EAAQ,KACd,KAAM,EAAQ,MAAQ,GACtB,YAAa,EAAQ,EAAO,IAAU,CACpC,EAAsB,EAElB,GAAS,GAAG,EAAQ,aAAa,EAAQ,EAAO,CAAK,CAC3D,EACA,YAAa,EAAQ,YACrB,OAAQ,EAAQ,MAClB,CAAC,EAEK,GAAA,EAAuB,EAAA,SAAA,KAA8B,CACzD,IAAM,EAAQ,EAAK,aAAa,MAEhC,OAAO,EAAQ,aAAe,EAAO,OAAS,GAAS,EAAI,EAAQ,YAAY,CAAK,EAAI,IAC1F,CAAC,EAEK,MACG,OAAO,EAAQ,cAAiB,WAAa,EAAQ,aAAa,EAAK,EAAQ,cAAgB,GAGlG,GAAS,EAA8B,eAAgB,EAAgB,KAAe,CACrF,EAAO,QAEZ,EAAO,MAAQ,GACf,EAAK,MAAM,EACX,IAAkB,EAClB,EAAkB,KAEd,GAAiB,EAAmB,GAAK,EAAQ,YAAY,EAAA,oBAAoB,EAAQ,UAAU,EAEvG,EAAQ,UAAU,CAAM,EAC1B,EAEM,GAAQ,EAA4B,iBAAyB,CAC7D,EAAQ,aAAa,GAAK,EAAO,QAErC,EAAO,MAAQ,GACf,EAAW,OAAO,EAClB,EAAkB,EAAW,kBAAkB,GAAK,KACpD,EAAQ,SAAS,CAAM,EACzB,EAEM,GAAU,EAAgC,QAAS,EAAmC,YAAoB,CAC1G,EAAO,MAAO,EAAM,CAAW,EAC9B,EAAK,CAAU,CACtB,EAEA,EAAA,8BAA8B,CAC5B,eAAkB,CAAC,EAAQ,YAAY,EAAG,EAAQ,SAAS,CAAC,EAC5D,aAAgB,EAAO,MACvB,cAAiB,EAAM,cAAc,EACrC,OAAQ,EAAQ,MAClB,CAAC,EAED,IAAI,EAAW,GAET,MAAsB,CACtB,IAEJ,EAAW,GAEP,EAAO,QACT,EAAO,MAAQ,GACf,EAAK,MAAM,EACX,IAAkB,EAClB,EAAkB,MAEtB,EAiBA,OAfA,EAAQ,OAAO,iBAAiB,QAAS,EAAS,CAAE,KAAM,EAAK,CAAC,EAezD,CACL,uBACA,eACA,QACA,UACA,IAAI,UAAW,CACb,OAAO,CACT,EACA,aAAc,EAAK,aACnB,cAAe,EAAK,cACpB,cAvBqB,GAChB,EAAO,MAER,EAAM,MAAQ,UAChB,EAAM,eAAe,EACrB,EAAM,QAAQ,EAEP,IAGF,EAAK,cAAc,CAAK,EATL,GAuB1B,SACA,SAAU,EAAK,SACf,OACA,wBACA,IAAK,EAAK,KACT,OAAO,SAAU,EAClB,SACA,eAAgB,EAAW,MAC7B,CACF"}
@@ -27,9 +27,14 @@ export type ListboxDropdownOptions<T extends ListboxItem> = {
27
27
  signal: AbortSignal;
28
28
  };
29
29
  export type ListboxDropdown<T extends ListboxItem> = {
30
+ [Symbol.dispose](): void;
30
31
  readonly ariaActiveDescendant: Readable<string | null>;
31
32
  readonly ariaExpanded: Readable<string>;
32
33
  close(reason?: DropdownCloseReason): void;
34
+ /** Closes the dropdown (without restoring focus) and disposes list navigation state. */
35
+ dispose(): void;
36
+ /** `true` after `dispose()` has been called. */
37
+ readonly disposed: boolean;
33
38
  readonly focusedIndex: Readable<number>;
34
39
  getActiveItem(): T | undefined;
35
40
  handleKeydown(event: KeyboardEvent): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"option-list.d.ts","sourceRoot":"","sources":["../../src/core/option-list.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"option-list.d.ts","sourceRoot":"","sources":["../../src/core/option-list.ts"],"names":[],"mappings":"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"}
@@ -1,20 +1,20 @@
1
1
  import { RefineConfigError as e } from "../errors.js";
2
- import { createDropdownPositioner as t } from "./positioner.js";
3
- import { createListControl as n } from "./nav.js";
4
- import { createOutsidePointerDismissal as r, restoreFocus as i } from "./overlay.js";
2
+ import { createOutsidePointerDismissal as t, restoreTriggerFocus as n } from "./overlay.js";
3
+ import { createDropdownPositioner as r } from "./positioner.js";
4
+ import { createListControl as i } from "./nav.js";
5
5
  import { computed as a, signal as o } from "@vielzeug/ripple";
6
6
  //#region src/core/option-list.ts
7
7
  var s = (s) => {
8
8
  if (typeof s.getBoundary != "function") throw new e("createListboxDropdown: getBoundary is required");
9
9
  if (typeof s.getPanel != "function") throw new e("createListboxDropdown: getPanel is required");
10
10
  if (typeof s.getReference != "function") throw new e("createListboxDropdown: getReference is required");
11
- let c = o(!1), l = a(() => String(c.value)), u = null, d = t({
11
+ let c = o(!1), l = a(() => String(c.value)), u = null, d = r({
12
12
  ...s.positioning,
13
13
  getFloating: s.getPanel,
14
14
  getReference: s.getReference
15
15
  }), f = () => {
16
16
  s.getFocusedOptionElement?.()?.scrollIntoView({ block: "nearest" });
17
- }, p = n({
17
+ }, p = i({
18
18
  disabled: a(() => !c.value),
19
19
  getItemLabel: s.getItemLabel,
20
20
  getItems: s.getItems,
@@ -30,21 +30,29 @@ var s = (s) => {
30
30
  let e = p.focusedIndex.value;
31
31
  return s.getOptionId && c.value && e >= 0 ? s.getOptionId(e) : null;
32
32
  }), h = () => typeof s.restoreFocus == "function" ? s.restoreFocus() : s.restoreFocus ?? !0, g = (e = "programmatic", t = !0) => {
33
- c.value && (c.value = !1, p.reset(), u?.(), u = null, t && h() && s.getTrigger && i(s.getTrigger), s.onClose?.(e));
33
+ c.value && (c.value = !1, p.reset(), u?.(), u = null, t && h() && s.getTrigger && n(s.getTrigger), s.onClose?.(e));
34
34
  }, _ = (e = "programmatic") => {
35
35
  s.isDisabled?.() || c.value || (c.value = !0, d.update(), u = d.startAutoUpdate?.() ?? null, s.onOpen?.(e));
36
+ }, v = (e = "click", t = "trigger") => {
37
+ c.value ? g(t) : _(e);
36
38
  };
37
- return r({
39
+ t({
38
40
  getTargets: () => [s.getBoundary(), s.getPanel()],
39
41
  isActive: () => c.value,
40
42
  onDismiss: () => g("outsideClick"),
41
43
  signal: s.signal
42
- }), s.signal.addEventListener("abort", () => {
43
- c.value && (c.value = !1, p.reset(), u?.(), u = null);
44
- }, { once: !0 }), {
44
+ });
45
+ let y = !1, b = () => {
46
+ y || (y = !0, c.value && (c.value = !1, p.reset(), u?.(), u = null));
47
+ };
48
+ return s.signal.addEventListener("abort", b, { once: !0 }), {
45
49
  ariaActiveDescendant: m,
46
50
  ariaExpanded: l,
47
51
  close: g,
52
+ dispose: b,
53
+ get disposed() {
54
+ return y;
55
+ },
48
56
  focusedIndex: p.focusedIndex,
49
57
  getActiveItem: p.getActiveItem,
50
58
  handleKeydown: (e) => c.value ? e.key === "Escape" ? (e.preventDefault(), g("escape"), !0) : p.handleKeydown(e) : !1,
@@ -53,9 +61,8 @@ var s = (s) => {
53
61
  open: _,
54
62
  scrollFocusedIntoView: f,
55
63
  set: p.set,
56
- toggle: (e = "click", t = "trigger") => {
57
- c.value ? g(t) : _(e);
58
- },
64
+ [Symbol.dispose]: b,
65
+ toggle: v,
59
66
  updatePosition: d.update
60
67
  };
61
68
  };
@@ -1 +1 @@
1
- {"version":3,"file":"option-list.js","names":[],"sources":["../../src/core/option-list.ts"],"sourcesContent":["import { computed, type Readable, signal } from '@vielzeug/ripple';\n\nimport { RefineConfigError } from '../errors';\nimport { createListControl, type ListKeyAction, type ListNavigationAction } from './nav';\nimport {\n createOutsidePointerDismissal,\n restoreFocus,\n type DropdownCloseReason,\n type OverlayOpenReason,\n} from './overlay';\nimport { createDropdownPositioner, type DropdownPositionerOptions } from './positioner';\n\nexport type ListboxItem = object;\n\n/** Placement-only options for the listbox dropdown positioner. */\nexport type ListboxDropdownPlacementOptions = Omit<DropdownPositionerOptions, 'getFloating' | 'getReference'>;\n\nexport type ListboxDropdownOptions<T extends ListboxItem> = {\n getBoundary: () => HTMLElement | null;\n getFocusedOptionElement?: () => HTMLElement | null;\n getItemLabel?: (item: T, index: number) => string;\n getItems: () => T[];\n getOptionId?: (index: number) => string;\n getPanel: () => HTMLElement | null;\n getReference: () => HTMLElement | null;\n getTrigger?: () => HTMLElement | null;\n isDisabled?: () => boolean;\n isItemDisabled?: (item: T, index: number) => boolean;\n keys?: Partial<Record<ListNavigationAction, string[]>>;\n loop?: boolean;\n onClose?: (reason: DropdownCloseReason) => void;\n onNavigate?: (action: ListKeyAction, index: number, event?: KeyboardEvent) => void;\n onOpen?: (reason: OverlayOpenReason) => void;\n orientation?: 'both' | 'horizontal' | 'vertical';\n positioning?: ListboxDropdownPlacementOptions;\n restoreFocus?: boolean | (() => boolean);\n signal: AbortSignal;\n};\n\nexport type ListboxDropdown<T extends ListboxItem> = {\n readonly ariaActiveDescendant: Readable<string | null>;\n readonly ariaExpanded: Readable<string>;\n close(reason?: DropdownCloseReason): void;\n readonly focusedIndex: Readable<number>;\n getActiveItem(): T | undefined;\n handleKeydown(event: KeyboardEvent): boolean;\n readonly isOpen: Readable<boolean>;\n navigate(action: ListNavigationAction): number;\n open(reason?: OverlayOpenReason): void;\n scrollFocusedIntoView(): void;\n set(index: number): number;\n toggle(openReason?: OverlayOpenReason, closeReason?: DropdownCloseReason): void;\n updatePosition(): void;\n};\n\n/**\n * Internal listbox dropdown behavior for select and combobox. It owns only\n * listbox interactions; menu and other overlay families keep their own logic.\n */\nexport const createListboxDropdown = <T extends ListboxItem>(\n options: ListboxDropdownOptions<T>,\n): ListboxDropdown<T> => {\n if (typeof options.getBoundary !== 'function') {\n throw new RefineConfigError('createListboxDropdown: getBoundary is required');\n }\n\n if (typeof options.getPanel !== 'function') {\n throw new RefineConfigError('createListboxDropdown: getPanel is required');\n }\n\n if (typeof options.getReference !== 'function') {\n throw new RefineConfigError('createListboxDropdown: getReference is required');\n }\n\n const isOpen = signal(false);\n const ariaExpanded = computed(() => String(isOpen.value));\n let stopPositioning: (() => void) | null = null;\n\n const positioner = createDropdownPositioner({\n ...options.positioning,\n getFloating: options.getPanel,\n getReference: options.getReference,\n });\n\n const scrollFocusedIntoView = (): void => {\n options.getFocusedOptionElement?.()?.scrollIntoView({ block: 'nearest' });\n };\n\n const list = createListControl<T>({\n disabled: computed(() => !isOpen.value),\n getItemLabel: options.getItemLabel,\n getItems: options.getItems,\n isItemDisabled: options.isItemDisabled,\n keys: options.keys,\n loop: options.loop ?? true,\n onNavigate: (action, index, event) => {\n scrollFocusedIntoView();\n\n if (index >= 0) options.onNavigate?.(action, index, event);\n },\n orientation: options.orientation,\n signal: options.signal,\n });\n\n const ariaActiveDescendant = computed<string | null>(() => {\n const index = list.focusedIndex.value;\n\n return options.getOptionId && isOpen.value && index >= 0 ? options.getOptionId(index) : null;\n });\n\n const shouldRestoreFocus = (): boolean => {\n return typeof options.restoreFocus === 'function' ? options.restoreFocus() : (options.restoreFocus ?? true);\n };\n\n const close = (reason: DropdownCloseReason = 'programmatic', shouldRestore = true): void => {\n if (!isOpen.value) return;\n\n isOpen.value = false;\n list.reset();\n stopPositioning?.();\n stopPositioning = null;\n\n if (shouldRestore && shouldRestoreFocus() && options.getTrigger) restoreFocus(options.getTrigger);\n\n options.onClose?.(reason);\n };\n\n const open = (reason: OverlayOpenReason = 'programmatic'): void => {\n if (options.isDisabled?.() || isOpen.value) return;\n\n isOpen.value = true;\n positioner.update();\n stopPositioning = positioner.startAutoUpdate?.() ?? null;\n options.onOpen?.(reason);\n };\n\n const toggle = (openReason: OverlayOpenReason = 'click', closeReason: DropdownCloseReason = 'trigger'): void => {\n if (isOpen.value) close(closeReason);\n else open(openReason);\n };\n\n createOutsidePointerDismissal({\n getTargets: () => [options.getBoundary(), options.getPanel()],\n isActive: () => isOpen.value,\n onDismiss: () => close('outsideClick'),\n signal: options.signal,\n });\n\n options.signal.addEventListener(\n 'abort',\n () => {\n if (!isOpen.value) return;\n\n isOpen.value = false;\n list.reset();\n stopPositioning?.();\n stopPositioning = null;\n },\n { once: true },\n );\n\n const handleKeydown = (event: KeyboardEvent): boolean => {\n if (!isOpen.value) return false;\n\n if (event.key === 'Escape') {\n event.preventDefault();\n close('escape');\n\n return true;\n }\n\n return list.handleKeydown(event);\n };\n\n return {\n ariaActiveDescendant,\n ariaExpanded,\n close,\n focusedIndex: list.focusedIndex,\n getActiveItem: list.getActiveItem,\n handleKeydown,\n isOpen,\n navigate: list.navigate,\n open,\n scrollFocusedIntoView,\n set: list.set,\n toggle,\n updatePosition: positioner.update,\n };\n};\n"],"mappings":";;;;;;AA2DA,IAAa,KACX,MACuB;CACvB,IAAI,OAAO,EAAQ,eAAgB,YACjC,MAAM,IAAI,EAAkB,gDAAgD;CAG9E,IAAI,OAAO,EAAQ,YAAa,YAC9B,MAAM,IAAI,EAAkB,6CAA6C;CAG3E,IAAI,OAAO,EAAQ,gBAAiB,YAClC,MAAM,IAAI,EAAkB,iDAAiD;CAG/E,IAAM,IAAS,EAAO,EAAK,GACrB,IAAe,QAAe,OAAO,EAAO,KAAK,CAAC,GACpD,IAAuC,MAErC,IAAa,EAAyB;EAC1C,GAAG,EAAQ;EACX,aAAa,EAAQ;EACrB,cAAc,EAAQ;CACxB,CAAC,GAEK,UAAoC;EACxC,EAAQ,0BAA0B,CAAC,EAAE,eAAe,EAAE,OAAO,UAAU,CAAC;CAC1E,GAEM,IAAO,EAAqB;EAChC,UAAU,QAAe,CAAC,EAAO,KAAK;EACtC,cAAc,EAAQ;EACtB,UAAU,EAAQ;EAClB,gBAAgB,EAAQ;EACxB,MAAM,EAAQ;EACd,MAAM,EAAQ,QAAQ;EACtB,aAAa,GAAQ,GAAO,MAAU;GAGpC,AAFA,EAAsB,GAElB,KAAS,KAAG,EAAQ,aAAa,GAAQ,GAAO,CAAK;EAC3D;EACA,aAAa,EAAQ;EACrB,QAAQ,EAAQ;CAClB,CAAC,GAEK,IAAuB,QAA8B;EACzD,IAAM,IAAQ,EAAK,aAAa;EAEhC,OAAO,EAAQ,eAAe,EAAO,SAAS,KAAS,IAAI,EAAQ,YAAY,CAAK,IAAI;CAC1F,CAAC,GAEK,UACG,OAAO,EAAQ,gBAAiB,aAAa,EAAQ,aAAa,IAAK,EAAQ,gBAAgB,IAGlG,KAAS,IAA8B,gBAAgB,IAAgB,OAAe;EACrF,EAAO,UAEZ,EAAO,QAAQ,IACf,EAAK,MAAM,GACX,IAAkB,GAClB,IAAkB,MAEd,KAAiB,EAAmB,KAAK,EAAQ,cAAY,EAAa,EAAQ,UAAU,GAEhG,EAAQ,UAAU,CAAM;CAC1B,GAEM,KAAQ,IAA4B,mBAAyB;EAC7D,EAAQ,aAAa,KAAK,EAAO,UAErC,EAAO,QAAQ,IACf,EAAW,OAAO,GAClB,IAAkB,EAAW,kBAAkB,KAAK,MACpD,EAAQ,SAAS,CAAM;CACzB;CAwCA,OAjCA,EAA8B;EAC5B,kBAAkB,CAAC,EAAQ,YAAY,GAAG,EAAQ,SAAS,CAAC;EAC5D,gBAAgB,EAAO;EACvB,iBAAiB,EAAM,cAAc;EACrC,QAAQ,EAAQ;CAClB,CAAC,GAED,EAAQ,OAAO,iBACb,eACM;EACC,EAAO,UAEZ,EAAO,QAAQ,IACf,EAAK,MAAM,GACX,IAAkB,GAClB,IAAkB;CACpB,GACA,EAAE,MAAM,GAAK,CACf,GAeO;EACL;EACA;EACA;EACA,cAAc,EAAK;EACnB,eAAe,EAAK;EACpB,gBAnBqB,MAChB,EAAO,QAER,EAAM,QAAQ,YAChB,EAAM,eAAe,GACrB,EAAM,QAAQ,GAEP,MAGF,EAAK,cAAc,CAAK,IATL;EAmB1B;EACA,UAAU,EAAK;EACf;EACA;EACA,KAAK,EAAK;EACV,SAlDc,IAAgC,SAAS,IAAmC,cAAoB;GAC9G,AAAI,EAAO,QAAO,EAAM,CAAW,IAC9B,EAAK,CAAU;EACtB;EAgDE,gBAAgB,EAAW;CAC7B;AACF"}
1
+ {"version":3,"file":"option-list.js","names":[],"sources":["../../src/core/option-list.ts"],"sourcesContent":["import { computed, type Readable, signal } from '@vielzeug/ripple';\n\nimport { RefineConfigError } from '../errors';\nimport { createListControl, type ListKeyAction, type ListNavigationAction } from './nav';\nimport {\n createOutsidePointerDismissal,\n restoreTriggerFocus,\n type DropdownCloseReason,\n type OverlayOpenReason,\n} from './overlay';\nimport { createDropdownPositioner, type DropdownPositionerOptions } from './positioner';\n\nexport type ListboxItem = object;\n\n/** Placement-only options for the listbox dropdown positioner. */\nexport type ListboxDropdownPlacementOptions = Omit<DropdownPositionerOptions, 'getFloating' | 'getReference'>;\n\nexport type ListboxDropdownOptions<T extends ListboxItem> = {\n getBoundary: () => HTMLElement | null;\n getFocusedOptionElement?: () => HTMLElement | null;\n getItemLabel?: (item: T, index: number) => string;\n getItems: () => T[];\n getOptionId?: (index: number) => string;\n getPanel: () => HTMLElement | null;\n getReference: () => HTMLElement | null;\n getTrigger?: () => HTMLElement | null;\n isDisabled?: () => boolean;\n isItemDisabled?: (item: T, index: number) => boolean;\n keys?: Partial<Record<ListNavigationAction, string[]>>;\n loop?: boolean;\n onClose?: (reason: DropdownCloseReason) => void;\n onNavigate?: (action: ListKeyAction, index: number, event?: KeyboardEvent) => void;\n onOpen?: (reason: OverlayOpenReason) => void;\n orientation?: 'both' | 'horizontal' | 'vertical';\n positioning?: ListboxDropdownPlacementOptions;\n restoreFocus?: boolean | (() => boolean);\n signal: AbortSignal;\n};\n\nexport type ListboxDropdown<T extends ListboxItem> = {\n [Symbol.dispose](): void;\n readonly ariaActiveDescendant: Readable<string | null>;\n readonly ariaExpanded: Readable<string>;\n close(reason?: DropdownCloseReason): void;\n /** Closes the dropdown (without restoring focus) and disposes list navigation state. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n readonly focusedIndex: Readable<number>;\n getActiveItem(): T | undefined;\n handleKeydown(event: KeyboardEvent): boolean;\n readonly isOpen: Readable<boolean>;\n navigate(action: ListNavigationAction): number;\n open(reason?: OverlayOpenReason): void;\n scrollFocusedIntoView(): void;\n set(index: number): number;\n toggle(openReason?: OverlayOpenReason, closeReason?: DropdownCloseReason): void;\n updatePosition(): void;\n};\n\n/**\n * Internal listbox dropdown behavior for select and combobox. It owns only\n * listbox interactions; menu and other overlay families keep their own logic.\n */\nexport const createListboxDropdown = <T extends ListboxItem>(\n options: ListboxDropdownOptions<T>,\n): ListboxDropdown<T> => {\n if (typeof options.getBoundary !== 'function') {\n throw new RefineConfigError('createListboxDropdown: getBoundary is required');\n }\n\n if (typeof options.getPanel !== 'function') {\n throw new RefineConfigError('createListboxDropdown: getPanel is required');\n }\n\n if (typeof options.getReference !== 'function') {\n throw new RefineConfigError('createListboxDropdown: getReference is required');\n }\n\n const isOpen = signal(false);\n const ariaExpanded = computed(() => String(isOpen.value));\n let stopPositioning: (() => void) | null = null;\n\n const positioner = createDropdownPositioner({\n ...options.positioning,\n getFloating: options.getPanel,\n getReference: options.getReference,\n });\n\n const scrollFocusedIntoView = (): void => {\n options.getFocusedOptionElement?.()?.scrollIntoView({ block: 'nearest' });\n };\n\n const list = createListControl<T>({\n disabled: computed(() => !isOpen.value),\n getItemLabel: options.getItemLabel,\n getItems: options.getItems,\n isItemDisabled: options.isItemDisabled,\n keys: options.keys,\n loop: options.loop ?? true,\n onNavigate: (action, index, event) => {\n scrollFocusedIntoView();\n\n if (index >= 0) options.onNavigate?.(action, index, event);\n },\n orientation: options.orientation,\n signal: options.signal,\n });\n\n const ariaActiveDescendant = computed<string | null>(() => {\n const index = list.focusedIndex.value;\n\n return options.getOptionId && isOpen.value && index >= 0 ? options.getOptionId(index) : null;\n });\n\n const shouldRestoreFocus = (): boolean => {\n return typeof options.restoreFocus === 'function' ? options.restoreFocus() : (options.restoreFocus ?? true);\n };\n\n const close = (reason: DropdownCloseReason = 'programmatic', shouldRestore = true): void => {\n if (!isOpen.value) return;\n\n isOpen.value = false;\n list.reset();\n stopPositioning?.();\n stopPositioning = null;\n\n if (shouldRestore && shouldRestoreFocus() && options.getTrigger) restoreTriggerFocus(options.getTrigger);\n\n options.onClose?.(reason);\n };\n\n const open = (reason: OverlayOpenReason = 'programmatic'): void => {\n if (options.isDisabled?.() || isOpen.value) return;\n\n isOpen.value = true;\n positioner.update();\n stopPositioning = positioner.startAutoUpdate?.() ?? null;\n options.onOpen?.(reason);\n };\n\n const toggle = (openReason: OverlayOpenReason = 'click', closeReason: DropdownCloseReason = 'trigger'): void => {\n if (isOpen.value) close(closeReason);\n else open(openReason);\n };\n\n createOutsidePointerDismissal({\n getTargets: () => [options.getBoundary(), options.getPanel()],\n isActive: () => isOpen.value,\n onDismiss: () => close('outsideClick'),\n signal: options.signal,\n });\n\n let disposed = false;\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n\n if (isOpen.value) {\n isOpen.value = false;\n list.reset();\n stopPositioning?.();\n stopPositioning = null;\n }\n };\n\n options.signal.addEventListener('abort', dispose, { once: true });\n\n const handleKeydown = (event: KeyboardEvent): boolean => {\n if (!isOpen.value) return false;\n\n if (event.key === 'Escape') {\n event.preventDefault();\n close('escape');\n\n return true;\n }\n\n return list.handleKeydown(event);\n };\n\n return {\n ariaActiveDescendant,\n ariaExpanded,\n close,\n dispose,\n get disposed() {\n return disposed;\n },\n focusedIndex: list.focusedIndex,\n getActiveItem: list.getActiveItem,\n handleKeydown,\n isOpen,\n navigate: list.navigate,\n open,\n scrollFocusedIntoView,\n set: list.set,\n [Symbol.dispose]: dispose,\n toggle,\n updatePosition: positioner.update,\n };\n};\n"],"mappings":";;;;;;AAgEA,IAAa,KACX,MACuB;CACvB,IAAI,OAAO,EAAQ,eAAgB,YACjC,MAAM,IAAI,EAAkB,gDAAgD;CAG9E,IAAI,OAAO,EAAQ,YAAa,YAC9B,MAAM,IAAI,EAAkB,6CAA6C;CAG3E,IAAI,OAAO,EAAQ,gBAAiB,YAClC,MAAM,IAAI,EAAkB,iDAAiD;CAG/E,IAAM,IAAS,EAAO,EAAK,GACrB,IAAe,QAAe,OAAO,EAAO,KAAK,CAAC,GACpD,IAAuC,MAErC,IAAa,EAAyB;EAC1C,GAAG,EAAQ;EACX,aAAa,EAAQ;EACrB,cAAc,EAAQ;CACxB,CAAC,GAEK,UAAoC;EACxC,EAAQ,0BAA0B,CAAC,EAAE,eAAe,EAAE,OAAO,UAAU,CAAC;CAC1E,GAEM,IAAO,EAAqB;EAChC,UAAU,QAAe,CAAC,EAAO,KAAK;EACtC,cAAc,EAAQ;EACtB,UAAU,EAAQ;EAClB,gBAAgB,EAAQ;EACxB,MAAM,EAAQ;EACd,MAAM,EAAQ,QAAQ;EACtB,aAAa,GAAQ,GAAO,MAAU;GAGpC,AAFA,EAAsB,GAElB,KAAS,KAAG,EAAQ,aAAa,GAAQ,GAAO,CAAK;EAC3D;EACA,aAAa,EAAQ;EACrB,QAAQ,EAAQ;CAClB,CAAC,GAEK,IAAuB,QAA8B;EACzD,IAAM,IAAQ,EAAK,aAAa;EAEhC,OAAO,EAAQ,eAAe,EAAO,SAAS,KAAS,IAAI,EAAQ,YAAY,CAAK,IAAI;CAC1F,CAAC,GAEK,UACG,OAAO,EAAQ,gBAAiB,aAAa,EAAQ,aAAa,IAAK,EAAQ,gBAAgB,IAGlG,KAAS,IAA8B,gBAAgB,IAAgB,OAAe;EACrF,EAAO,UAEZ,EAAO,QAAQ,IACf,EAAK,MAAM,GACX,IAAkB,GAClB,IAAkB,MAEd,KAAiB,EAAmB,KAAK,EAAQ,cAAY,EAAoB,EAAQ,UAAU,GAEvG,EAAQ,UAAU,CAAM;CAC1B,GAEM,KAAQ,IAA4B,mBAAyB;EAC7D,EAAQ,aAAa,KAAK,EAAO,UAErC,EAAO,QAAQ,IACf,EAAW,OAAO,GAClB,IAAkB,EAAW,kBAAkB,KAAK,MACpD,EAAQ,SAAS,CAAM;CACzB,GAEM,KAAU,IAAgC,SAAS,IAAmC,cAAoB;EAC9G,AAAI,EAAO,QAAO,EAAM,CAAW,IAC9B,EAAK,CAAU;CACtB;CAEA,EAA8B;EAC5B,kBAAkB,CAAC,EAAQ,YAAY,GAAG,EAAQ,SAAS,CAAC;EAC5D,gBAAgB,EAAO;EACvB,iBAAiB,EAAM,cAAc;EACrC,QAAQ,EAAQ;CAClB,CAAC;CAED,IAAI,IAAW,IAET,UAAsB;EACtB,MAEJ,IAAW,IAEP,EAAO,UACT,EAAO,QAAQ,IACf,EAAK,MAAM,GACX,IAAkB,GAClB,IAAkB;CAEtB;CAiBA,OAfA,EAAQ,OAAO,iBAAiB,SAAS,GAAS,EAAE,MAAM,GAAK,CAAC,GAezD;EACL;EACA;EACA;EACA;EACA,IAAI,WAAW;GACb,OAAO;EACT;EACA,cAAc,EAAK;EACnB,eAAe,EAAK;EACpB,gBAvBqB,MAChB,EAAO,QAER,EAAM,QAAQ,YAChB,EAAM,eAAe,GACrB,EAAM,QAAQ,GAEP,MAGF,EAAK,cAAc,CAAK,IATL;EAuB1B;EACA,UAAU,EAAK;EACf;EACA;EACA,KAAK,EAAK;GACT,OAAO,UAAU;EAClB;EACA,gBAAgB,EAAW;CAC7B;AACF"}
@@ -1,2 +1,2 @@
1
- var e=e=>{let t=t=>{if(!e.isActive())return;let n=t.composedPath(),r=n[0]instanceof Node?n[0]:t.target instanceof Node?t.target:null;e.getTargets().some(e=>e!=null&&(n.includes(e)||!!(r&&e.contains(r))))||e.onDismiss()},n=()=>{document.removeEventListener(`pointerdown`,t,{capture:!0})};return document.addEventListener(`pointerdown`,t,{capture:!0}),e.signal.addEventListener(`abort`,n,{once:!0}),n},t=e=>{e()?.focus()};exports.createOutsidePointerDismissal=e,exports.restoreFocus=t;
1
+ var e=e=>{let t=t=>{if(!e.isActive())return;let n=t.composedPath(),r=n[0]instanceof Node?n[0]:t.target instanceof Node?t.target:null;e.getTargets().some(e=>e!=null&&(n.includes(e)||!!(r&&e.contains(r))))||e.onDismiss()},n=()=>{document.removeEventListener(`pointerdown`,t,{capture:!0})};return document.addEventListener(`pointerdown`,t,{capture:!0}),e.signal.addEventListener(`abort`,n,{once:!0}),n},t=e=>{e()?.focus()};exports.createOutsidePointerDismissal=e,exports.restoreTriggerFocus=t;
2
2
  //# sourceMappingURL=overlay.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"overlay.cjs","names":[],"sources":["../../src/core/overlay.ts"],"sourcesContent":["/**\n * Why an overlay was opened.\n * - `'click'` — user clicked the trigger element\n * - `'focus'` — focus entered the trigger (tooltip, popover)\n * - `'hover'` — pointer entered the trigger (tooltip)\n * - `'keyboard'` — keyboard shortcut or Enter/Space on a trigger\n * - `'programmatic'` — opened via the JS API without a user gesture\n * - `'trigger'` — opened via the Invoker Commands API (`command=\"show-modal\"`)\n */\nexport type OverlayOpenReason = 'click' | 'focus' | 'hover' | 'keyboard' | 'programmatic' | 'trigger';\n\n/** Close reasons valid for modal dialogs (includes 'swipe' for drawer). */\nexport type DialogCloseReason = 'escape' | 'outsideClick' | 'programmatic' | 'swipe' | 'trigger';\n/** Close reasons valid for dropdown overlays (no swipe). */\nexport type DropdownCloseReason = Exclude<DialogCloseReason, 'swipe'>;\n\n/** Detail shape for overlay open events emitted by components. */\nexport type OverlayOpenDetail = { reason: OverlayOpenReason };\n/** Detail shape for overlay close events emitted by components. */\nexport type OverlayCloseDetail = { reason: DialogCloseReason };\n/** Detail shape for state changes emitted by overlay components. */\nexport type OverlayOpenChangeDetail = {\n open: boolean;\n reason: DialogCloseReason | OverlayOpenReason;\n};\n\nexport type OutsidePointerDismissalOptions = {\n getTargets: () => Array<Element | null | undefined>;\n isActive: () => boolean;\n onDismiss: () => void;\n signal: AbortSignal;\n};\n\n/**\n * Dismisses an active floating surface when a pointer begins outside all of its\n * supplied boundary elements. Components retain ownership of their open state.\n */\nexport const createOutsidePointerDismissal = (options: OutsidePointerDismissalOptions): (() => void) => {\n const onPointerDown = (event: PointerEvent): void => {\n if (!options.isActive()) return;\n\n const path = event.composedPath();\n const target = path[0] instanceof Node ? path[0] : event.target instanceof Node ? event.target : null;\n const inside = options.getTargets().some((element) => {\n return (\n element !== null &&\n element !== undefined &&\n (path.includes(element) || Boolean(target && element.contains(target)))\n );\n });\n\n if (!inside) options.onDismiss();\n };\n\n const dispose = (): void => {\n document.removeEventListener('pointerdown', onPointerDown, { capture: true });\n };\n\n document.addEventListener('pointerdown', onPointerDown, { capture: true });\n options.signal.addEventListener('abort', dispose, { once: true });\n\n return dispose;\n};\n\n/** Restores focus to a trigger when the owning overlay chooses to do so. */\nexport const restoreFocus = (getTrigger: () => HTMLElement | null | undefined): void => {\n getTrigger()?.focus();\n};\n"],"mappings":"AAqCA,IAAa,EAAiC,GAA0D,CACtG,IAAM,EAAiB,GAA8B,CACnD,GAAI,CAAC,EAAQ,SAAS,EAAG,OAEzB,IAAM,EAAO,EAAM,aAAa,EAC1B,EAAS,EAAK,aAAc,KAAO,EAAK,GAAK,EAAM,kBAAkB,KAAO,EAAM,OAAS,KAClF,EAAQ,WAAW,CAAC,CAAC,KAAM,GAEtC,GAAY,OAEX,EAAK,SAAS,CAAO,GAAK,GAAQ,GAAU,EAAQ,SAAS,CAAM,GAInE,GAAQ,EAAQ,UAAU,CACjC,EAEM,MAAsB,CAC1B,SAAS,oBAAoB,cAAe,EAAe,CAAE,QAAS,EAAK,CAAC,CAC9E,EAKA,OAHA,SAAS,iBAAiB,cAAe,EAAe,CAAE,QAAS,EAAK,CAAC,EACzE,EAAQ,OAAO,iBAAiB,QAAS,EAAS,CAAE,KAAM,EAAK,CAAC,EAEzD,CACT,EAGa,EAAgB,GAA2D,CACtF,EAAW,CAAC,EAAE,MAAM,CACtB"}
1
+ {"version":3,"file":"overlay.cjs","names":[],"sources":["../../src/core/overlay.ts"],"sourcesContent":["/**\n * Why an overlay was opened.\n * - `'click'` — user clicked the trigger element\n * - `'focus'` — focus entered the trigger (tooltip, popover)\n * - `'hover'` — pointer entered the trigger (tooltip)\n * - `'keyboard'` — keyboard shortcut or Enter/Space on a trigger\n * - `'programmatic'` — opened via the JS API without a user gesture\n * - `'trigger'` — opened via the Invoker Commands API (`command=\"show-modal\"`)\n */\nexport type OverlayOpenReason = 'click' | 'focus' | 'hover' | 'keyboard' | 'programmatic' | 'trigger';\n\n/** Close reasons valid for modal dialogs (includes 'swipe' for drawer). */\nexport type DialogCloseReason = 'escape' | 'outsideClick' | 'programmatic' | 'swipe' | 'trigger';\n/** Close reasons valid for dropdown overlays (no swipe). */\nexport type DropdownCloseReason = Exclude<DialogCloseReason, 'swipe'>;\n\n/** Detail shape for overlay open events emitted by components. */\nexport type OverlayOpenDetail = { reason: OverlayOpenReason };\n/** Detail shape for overlay close events emitted by components. */\nexport type OverlayCloseDetail = { reason: DialogCloseReason };\n/** Detail shape for state changes emitted by overlay components. */\nexport type OverlayOpenChangeDetail = {\n open: boolean;\n reason: DialogCloseReason | OverlayOpenReason;\n};\n\nexport type OutsidePointerDismissalOptions = {\n getTargets: () => Array<Element | null | undefined>;\n isActive: () => boolean;\n onDismiss: () => void;\n signal: AbortSignal;\n};\n\n/**\n * Dismisses an active floating surface when a pointer begins outside all of its\n * supplied boundary elements. Components retain ownership of their open state.\n */\nexport const createOutsidePointerDismissal = (options: OutsidePointerDismissalOptions): (() => void) => {\n const onPointerDown = (event: PointerEvent): void => {\n if (!options.isActive()) return;\n\n const path = event.composedPath();\n const target = path[0] instanceof Node ? path[0] : event.target instanceof Node ? event.target : null;\n const inside = options.getTargets().some((element) => {\n return (\n element !== null &&\n element !== undefined &&\n (path.includes(element) || Boolean(target && element.contains(target)))\n );\n });\n\n if (!inside) options.onDismiss();\n };\n\n const dispose = (): void => {\n document.removeEventListener('pointerdown', onPointerDown, { capture: true });\n };\n\n document.addEventListener('pointerdown', onPointerDown, { capture: true });\n options.signal.addEventListener('abort', dispose, { once: true });\n\n return dispose;\n};\n\n/**\n * Restores focus to a dropdown/floating-panel trigger when the owning overlay closes.\n *\n * Named distinctly from `FocusManager.restoreFocus()` (`./focus.ts`) — that method restores a\n * captured dialog-opener element from internal state; this function takes the trigger directly,\n * for dropdown-style overlays (menu) that never captured one.\n */\nexport const restoreTriggerFocus = (getTrigger: () => HTMLElement | null | undefined): void => {\n getTrigger()?.focus();\n};\n"],"mappings":"AAqCA,IAAa,EAAiC,GAA0D,CACtG,IAAM,EAAiB,GAA8B,CACnD,GAAI,CAAC,EAAQ,SAAS,EAAG,OAEzB,IAAM,EAAO,EAAM,aAAa,EAC1B,EAAS,EAAK,aAAc,KAAO,EAAK,GAAK,EAAM,kBAAkB,KAAO,EAAM,OAAS,KAClF,EAAQ,WAAW,CAAC,CAAC,KAAM,GAEtC,GAAY,OAEX,EAAK,SAAS,CAAO,GAAK,GAAQ,GAAU,EAAQ,SAAS,CAAM,GAInE,GAAQ,EAAQ,UAAU,CACjC,EAEM,MAAsB,CAC1B,SAAS,oBAAoB,cAAe,EAAe,CAAE,QAAS,EAAK,CAAC,CAC9E,EAKA,OAHA,SAAS,iBAAiB,cAAe,EAAe,CAAE,QAAS,EAAK,CAAC,EACzE,EAAQ,OAAO,iBAAiB,QAAS,EAAS,CAAE,KAAM,EAAK,CAAC,EAEzD,CACT,EASa,EAAuB,GAA2D,CAC7F,EAAW,CAAC,EAAE,MAAM,CACtB"}
@@ -36,6 +36,12 @@ export type OutsidePointerDismissalOptions = {
36
36
  * supplied boundary elements. Components retain ownership of their open state.
37
37
  */
38
38
  export declare const createOutsidePointerDismissal: (options: OutsidePointerDismissalOptions) => (() => void);
39
- /** Restores focus to a trigger when the owning overlay chooses to do so. */
40
- export declare const restoreFocus: (getTrigger: () => HTMLElement | null | undefined) => void;
39
+ /**
40
+ * Restores focus to a dropdown/floating-panel trigger when the owning overlay closes.
41
+ *
42
+ * Named distinctly from `FocusManager.restoreFocus()` (`./focus.ts`) — that method restores a
43
+ * captured dialog-opener element from internal state; this function takes the trigger directly,
44
+ * for dropdown-style overlays (menu) that never captured one.
45
+ */
46
+ export declare const restoreTriggerFocus: (getTrigger: () => HTMLElement | null | undefined) => void;
41
47
  //# sourceMappingURL=overlay.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../src/core/overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,cAAc,GAAG,SAAS,CAAC;AAEtG,2EAA2E;AAC3E,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,cAAc,GAAG,cAAc,GAAG,OAAO,GAAG,SAAS,CAAC;AACjG,4DAA4D;AAC5D,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;AAEtE,kEAAkE;AAClE,MAAM,MAAM,iBAAiB,GAAG;IAAE,MAAM,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAC9D,mEAAmE;AACnE,MAAM,MAAM,kBAAkB,GAAG;IAAE,MAAM,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAC/D,oEAAoE;AACpE,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,CAAC;CAC/C,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,UAAU,EAAE,MAAM,KAAK,CAAC,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACpD,QAAQ,EAAE,MAAM,OAAO,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,MAAM,EAAE,WAAW,CAAC;CACrB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,6BAA6B,GAAI,SAAS,8BAA8B,KAAG,CAAC,MAAM,IAAI,CAyBlG,CAAC;AAEF,4EAA4E;AAC5E,eAAO,MAAM,YAAY,GAAI,YAAY,MAAM,WAAW,GAAG,IAAI,GAAG,SAAS,KAAG,IAE/E,CAAC"}
1
+ {"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../src/core/overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,cAAc,GAAG,SAAS,CAAC;AAEtG,2EAA2E;AAC3E,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,cAAc,GAAG,cAAc,GAAG,OAAO,GAAG,SAAS,CAAC;AACjG,4DAA4D;AAC5D,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;AAEtE,kEAAkE;AAClE,MAAM,MAAM,iBAAiB,GAAG;IAAE,MAAM,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAC9D,mEAAmE;AACnE,MAAM,MAAM,kBAAkB,GAAG;IAAE,MAAM,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAC/D,oEAAoE;AACpE,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,CAAC;CAC/C,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,UAAU,EAAE,MAAM,KAAK,CAAC,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACpD,QAAQ,EAAE,MAAM,OAAO,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,MAAM,EAAE,WAAW,CAAC;CACrB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,6BAA6B,GAAI,SAAS,8BAA8B,KAAG,CAAC,MAAM,IAAI,CAyBlG,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAAI,YAAY,MAAM,WAAW,GAAG,IAAI,GAAG,SAAS,KAAG,IAEtF,CAAC"}
@@ -12,6 +12,6 @@ var e = (e) => {
12
12
  e()?.focus();
13
13
  };
14
14
  //#endregion
15
- export { e as createOutsidePointerDismissal, t as restoreFocus };
15
+ export { e as createOutsidePointerDismissal, t as restoreTriggerFocus };
16
16
 
17
17
  //# sourceMappingURL=overlay.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"overlay.js","names":[],"sources":["../../src/core/overlay.ts"],"sourcesContent":["/**\n * Why an overlay was opened.\n * - `'click'` — user clicked the trigger element\n * - `'focus'` — focus entered the trigger (tooltip, popover)\n * - `'hover'` — pointer entered the trigger (tooltip)\n * - `'keyboard'` — keyboard shortcut or Enter/Space on a trigger\n * - `'programmatic'` — opened via the JS API without a user gesture\n * - `'trigger'` — opened via the Invoker Commands API (`command=\"show-modal\"`)\n */\nexport type OverlayOpenReason = 'click' | 'focus' | 'hover' | 'keyboard' | 'programmatic' | 'trigger';\n\n/** Close reasons valid for modal dialogs (includes 'swipe' for drawer). */\nexport type DialogCloseReason = 'escape' | 'outsideClick' | 'programmatic' | 'swipe' | 'trigger';\n/** Close reasons valid for dropdown overlays (no swipe). */\nexport type DropdownCloseReason = Exclude<DialogCloseReason, 'swipe'>;\n\n/** Detail shape for overlay open events emitted by components. */\nexport type OverlayOpenDetail = { reason: OverlayOpenReason };\n/** Detail shape for overlay close events emitted by components. */\nexport type OverlayCloseDetail = { reason: DialogCloseReason };\n/** Detail shape for state changes emitted by overlay components. */\nexport type OverlayOpenChangeDetail = {\n open: boolean;\n reason: DialogCloseReason | OverlayOpenReason;\n};\n\nexport type OutsidePointerDismissalOptions = {\n getTargets: () => Array<Element | null | undefined>;\n isActive: () => boolean;\n onDismiss: () => void;\n signal: AbortSignal;\n};\n\n/**\n * Dismisses an active floating surface when a pointer begins outside all of its\n * supplied boundary elements. Components retain ownership of their open state.\n */\nexport const createOutsidePointerDismissal = (options: OutsidePointerDismissalOptions): (() => void) => {\n const onPointerDown = (event: PointerEvent): void => {\n if (!options.isActive()) return;\n\n const path = event.composedPath();\n const target = path[0] instanceof Node ? path[0] : event.target instanceof Node ? event.target : null;\n const inside = options.getTargets().some((element) => {\n return (\n element !== null &&\n element !== undefined &&\n (path.includes(element) || Boolean(target && element.contains(target)))\n );\n });\n\n if (!inside) options.onDismiss();\n };\n\n const dispose = (): void => {\n document.removeEventListener('pointerdown', onPointerDown, { capture: true });\n };\n\n document.addEventListener('pointerdown', onPointerDown, { capture: true });\n options.signal.addEventListener('abort', dispose, { once: true });\n\n return dispose;\n};\n\n/** Restores focus to a trigger when the owning overlay chooses to do so. */\nexport const restoreFocus = (getTrigger: () => HTMLElement | null | undefined): void => {\n getTrigger()?.focus();\n};\n"],"mappings":";AAqCA,IAAa,KAAiC,MAA0D;CACtG,IAAM,KAAiB,MAA8B;EACnD,IAAI,CAAC,EAAQ,SAAS,GAAG;EAEzB,IAAM,IAAO,EAAM,aAAa,GAC1B,IAAS,EAAK,cAAc,OAAO,EAAK,KAAK,EAAM,kBAAkB,OAAO,EAAM,SAAS;EASjG,AARe,EAAQ,WAAW,CAAC,CAAC,MAAM,MAEtC,KAAY,SAEX,EAAK,SAAS,CAAO,KAAK,GAAQ,KAAU,EAAQ,SAAS,CAAM,GAInE,KAAQ,EAAQ,UAAU;CACjC,GAEM,UAAsB;EAC1B,SAAS,oBAAoB,eAAe,GAAe,EAAE,SAAS,GAAK,CAAC;CAC9E;CAKA,OAHA,SAAS,iBAAiB,eAAe,GAAe,EAAE,SAAS,GAAK,CAAC,GACzE,EAAQ,OAAO,iBAAiB,SAAS,GAAS,EAAE,MAAM,GAAK,CAAC,GAEzD;AACT,GAGa,KAAgB,MAA2D;CACtF,EAAW,CAAC,EAAE,MAAM;AACtB"}
1
+ {"version":3,"file":"overlay.js","names":[],"sources":["../../src/core/overlay.ts"],"sourcesContent":["/**\n * Why an overlay was opened.\n * - `'click'` — user clicked the trigger element\n * - `'focus'` — focus entered the trigger (tooltip, popover)\n * - `'hover'` — pointer entered the trigger (tooltip)\n * - `'keyboard'` — keyboard shortcut or Enter/Space on a trigger\n * - `'programmatic'` — opened via the JS API without a user gesture\n * - `'trigger'` — opened via the Invoker Commands API (`command=\"show-modal\"`)\n */\nexport type OverlayOpenReason = 'click' | 'focus' | 'hover' | 'keyboard' | 'programmatic' | 'trigger';\n\n/** Close reasons valid for modal dialogs (includes 'swipe' for drawer). */\nexport type DialogCloseReason = 'escape' | 'outsideClick' | 'programmatic' | 'swipe' | 'trigger';\n/** Close reasons valid for dropdown overlays (no swipe). */\nexport type DropdownCloseReason = Exclude<DialogCloseReason, 'swipe'>;\n\n/** Detail shape for overlay open events emitted by components. */\nexport type OverlayOpenDetail = { reason: OverlayOpenReason };\n/** Detail shape for overlay close events emitted by components. */\nexport type OverlayCloseDetail = { reason: DialogCloseReason };\n/** Detail shape for state changes emitted by overlay components. */\nexport type OverlayOpenChangeDetail = {\n open: boolean;\n reason: DialogCloseReason | OverlayOpenReason;\n};\n\nexport type OutsidePointerDismissalOptions = {\n getTargets: () => Array<Element | null | undefined>;\n isActive: () => boolean;\n onDismiss: () => void;\n signal: AbortSignal;\n};\n\n/**\n * Dismisses an active floating surface when a pointer begins outside all of its\n * supplied boundary elements. Components retain ownership of their open state.\n */\nexport const createOutsidePointerDismissal = (options: OutsidePointerDismissalOptions): (() => void) => {\n const onPointerDown = (event: PointerEvent): void => {\n if (!options.isActive()) return;\n\n const path = event.composedPath();\n const target = path[0] instanceof Node ? path[0] : event.target instanceof Node ? event.target : null;\n const inside = options.getTargets().some((element) => {\n return (\n element !== null &&\n element !== undefined &&\n (path.includes(element) || Boolean(target && element.contains(target)))\n );\n });\n\n if (!inside) options.onDismiss();\n };\n\n const dispose = (): void => {\n document.removeEventListener('pointerdown', onPointerDown, { capture: true });\n };\n\n document.addEventListener('pointerdown', onPointerDown, { capture: true });\n options.signal.addEventListener('abort', dispose, { once: true });\n\n return dispose;\n};\n\n/**\n * Restores focus to a dropdown/floating-panel trigger when the owning overlay closes.\n *\n * Named distinctly from `FocusManager.restoreFocus()` (`./focus.ts`) — that method restores a\n * captured dialog-opener element from internal state; this function takes the trigger directly,\n * for dropdown-style overlays (menu) that never captured one.\n */\nexport const restoreTriggerFocus = (getTrigger: () => HTMLElement | null | undefined): void => {\n getTrigger()?.focus();\n};\n"],"mappings":";AAqCA,IAAa,KAAiC,MAA0D;CACtG,IAAM,KAAiB,MAA8B;EACnD,IAAI,CAAC,EAAQ,SAAS,GAAG;EAEzB,IAAM,IAAO,EAAM,aAAa,GAC1B,IAAS,EAAK,cAAc,OAAO,EAAK,KAAK,EAAM,kBAAkB,OAAO,EAAM,SAAS;EASjG,AARe,EAAQ,WAAW,CAAC,CAAC,MAAM,MAEtC,KAAY,SAEX,EAAK,SAAS,CAAO,KAAK,GAAQ,KAAU,EAAQ,SAAS,CAAM,GAInE,KAAQ,EAAQ,UAAU;CACjC,GAEM,UAAsB;EAC1B,SAAS,oBAAoB,eAAe,GAAe,EAAE,SAAS,GAAK,CAAC;CAC9E;CAKA,OAHA,SAAS,iBAAiB,eAAe,GAAe,EAAE,SAAS,GAAK,CAAC,GACzE,EAAQ,OAAO,iBAAiB,SAAS,GAAS,EAAE,MAAM,GAAK,CAAC,GAEzD;AACT,GASa,KAAuB,MAA2D;CAC7F,EAAW,CAAC,EAAE,MAAM;AACtB"}