@pzerelles/headlessui-svelte 2.1.2-next.22 → 2.1.2-next.24

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 (132) hide show
  1. package/dist/button/Button.svelte +84 -54
  2. package/dist/button/Button.svelte.d.ts +7 -4
  3. package/dist/checkbox/Checkbox.svelte +173 -120
  4. package/dist/checkbox/Checkbox.svelte.d.ts +7 -4
  5. package/dist/close-button/CloseButton.svelte +12 -6
  6. package/dist/close-button/CloseButton.svelte.d.ts +13 -10
  7. package/dist/combobox/Combobox.svelte +50 -3
  8. package/dist/data-interactive/DataInteractive.svelte +55 -29
  9. package/dist/data-interactive/DataInteractive.svelte.d.ts +7 -5
  10. package/dist/description/Description.svelte +39 -24
  11. package/dist/description/Description.svelte.d.ts +8 -5
  12. package/dist/description/context.svelte.js +13 -15
  13. package/dist/dialog/Dialog.svelte +358 -38
  14. package/dist/dialog/Dialog.svelte.d.ts +10 -7
  15. package/dist/dialog/DialogBackdrop.svelte +30 -13
  16. package/dist/dialog/DialogBackdrop.svelte.d.ts +7 -4
  17. package/dist/dialog/DialogPanel.svelte +49 -26
  18. package/dist/dialog/DialogPanel.svelte.d.ts +7 -4
  19. package/dist/dialog/DialogTitle.svelte +38 -23
  20. package/dist/dialog/DialogTitle.svelte.d.ts +7 -4
  21. package/dist/field/Field.svelte +50 -34
  22. package/dist/field/Field.svelte.d.ts +7 -4
  23. package/dist/fieldset/Fieldset.svelte +50 -29
  24. package/dist/fieldset/Fieldset.svelte.d.ts +7 -4
  25. package/dist/focus-trap/FocusTrap.svelte +419 -283
  26. package/dist/focus-trap/FocusTrap.svelte.d.ts +7 -4
  27. package/dist/hooks/use-disabled.d.ts +4 -1
  28. package/dist/hooks/use-disabled.js +10 -5
  29. package/dist/input/Input.svelte +84 -53
  30. package/dist/input/Input.svelte.d.ts +7 -4
  31. package/dist/internal/FloatingProvider.svelte +14 -9
  32. package/dist/internal/FocusSentinel.svelte +16 -8
  33. package/dist/internal/ForcePortalRoot.svelte +7 -3
  34. package/dist/internal/FormFields.svelte +47 -34
  35. package/dist/internal/FormFieldsProvider.svelte +9 -5
  36. package/dist/internal/FormResolver.svelte +20 -15
  37. package/dist/internal/Hidden.svelte +50 -29
  38. package/dist/internal/Hidden.svelte.d.ts +7 -4
  39. package/dist/internal/MainTreeProvider.svelte +89 -36
  40. package/dist/internal/Portal.svelte +18 -14
  41. package/dist/internal/floating-provider.svelte.js +1 -1
  42. package/dist/internal/floating.svelte.d.ts +5 -5
  43. package/dist/internal/floating.svelte.js +17 -17
  44. package/dist/label/Label.svelte +93 -58
  45. package/dist/label/Label.svelte.d.ts +7 -4
  46. package/dist/legend/Legend.svelte +12 -3
  47. package/dist/listbox/Listbox.svelte +525 -387
  48. package/dist/listbox/Listbox.svelte.d.ts +7 -5
  49. package/dist/listbox/ListboxButton.svelte +173 -127
  50. package/dist/listbox/ListboxButton.svelte.d.ts +7 -5
  51. package/dist/listbox/ListboxOption.svelte +170 -129
  52. package/dist/listbox/ListboxOption.svelte.d.ts +7 -5
  53. package/dist/listbox/ListboxOptions.svelte +400 -304
  54. package/dist/listbox/ListboxOptions.svelte.d.ts +7 -5
  55. package/dist/listbox/ListboxSelectedOption.svelte +38 -15
  56. package/dist/listbox/ListboxSelectedOption.svelte.d.ts +7 -4
  57. package/dist/listbox/index.d.ts +4 -4
  58. package/dist/listbox/index.js +1 -1
  59. package/dist/menu/Menu.svelte +78 -57
  60. package/dist/menu/Menu.svelte.d.ts +7 -5
  61. package/dist/menu/MenuButton.svelte +157 -117
  62. package/dist/menu/MenuButton.svelte.d.ts +7 -5
  63. package/dist/menu/MenuHeading.svelte +32 -14
  64. package/dist/menu/MenuHeading.svelte.d.ts +7 -5
  65. package/dist/menu/MenuItem.svelte +142 -107
  66. package/dist/menu/MenuItem.svelte.d.ts +8 -8
  67. package/dist/menu/MenuItems.svelte +301 -229
  68. package/dist/menu/MenuItems.svelte.d.ts +7 -5
  69. package/dist/menu/MenuSection.svelte +24 -9
  70. package/dist/menu/MenuSection.svelte.d.ts +7 -5
  71. package/dist/menu/MenuSeparator.svelte +17 -4
  72. package/dist/menu/MenuSeparator.svelte.d.ts +7 -6
  73. package/dist/menu/context.svelte.d.ts +1 -29
  74. package/dist/menu/context.svelte.js +29 -27
  75. package/dist/menu/index.d.ts +7 -7
  76. package/dist/popover/Popover.svelte +216 -150
  77. package/dist/popover/Popover.svelte.d.ts +7 -4
  78. package/dist/popover/PopoverBackdrop.svelte +67 -41
  79. package/dist/popover/PopoverBackdrop.svelte.d.ts +7 -4
  80. package/dist/popover/PopoverButton.svelte +292 -212
  81. package/dist/popover/PopoverButton.svelte.d.ts +7 -4
  82. package/dist/popover/PopoverGroup.svelte +62 -35
  83. package/dist/popover/PopoverGroup.svelte.d.ts +7 -4
  84. package/dist/popover/PopoverPanel.svelte +311 -229
  85. package/dist/popover/PopoverPanel.svelte.d.ts +7 -4
  86. package/dist/portal/InternalPortal.svelte +141 -85
  87. package/dist/portal/InternalPortal.svelte.d.ts +7 -4
  88. package/dist/portal/Portal.svelte +5 -2
  89. package/dist/portal/PortalGroup.svelte +30 -9
  90. package/dist/portal/PortalGroup.svelte.d.ts +7 -4
  91. package/dist/select/Select.svelte +98 -68
  92. package/dist/select/Select.svelte.d.ts +7 -4
  93. package/dist/switch/Switch.svelte +179 -132
  94. package/dist/switch/Switch.svelte.d.ts +7 -4
  95. package/dist/switch/SwitchGroup.svelte +44 -31
  96. package/dist/switch/SwitchGroup.svelte.d.ts +7 -4
  97. package/dist/tabs/Tab.svelte +194 -143
  98. package/dist/tabs/Tab.svelte.d.ts +7 -4
  99. package/dist/tabs/TabGroup.svelte +81 -214
  100. package/dist/tabs/TabGroup.svelte.d.ts +7 -24
  101. package/dist/tabs/TabList.svelte +31 -11
  102. package/dist/tabs/TabList.svelte.d.ts +7 -4
  103. package/dist/tabs/TabPanel.svelte +67 -43
  104. package/dist/tabs/TabPanel.svelte.d.ts +7 -4
  105. package/dist/tabs/TabPanels.svelte +18 -7
  106. package/dist/tabs/TabPanels.svelte.d.ts +7 -4
  107. package/dist/tabs/context.svelte.d.ts +31 -0
  108. package/dist/tabs/context.svelte.js +134 -0
  109. package/dist/textarea/Textarea.svelte +84 -53
  110. package/dist/textarea/Textarea.svelte.d.ts +7 -4
  111. package/dist/transition/InternalTransitionChild.svelte +259 -170
  112. package/dist/transition/InternalTransitionChild.svelte.d.ts +7 -4
  113. package/dist/transition/Transition.svelte +96 -66
  114. package/dist/transition/Transition.svelte.d.ts +7 -4
  115. package/dist/transition/TransitionChild.svelte +31 -11
  116. package/dist/transition/TransitionChild.svelte.d.ts +7 -4
  117. package/dist/utils/ElementOrComponent.svelte +43 -23
  118. package/dist/utils/ElementOrComponent.svelte.d.ts +10 -4
  119. package/dist/utils/Generic.svelte +36 -22
  120. package/dist/utils/Generic.svelte.d.ts +7 -4
  121. package/dist/utils/StableCollection.svelte +54 -36
  122. package/dist/utils/floating-ui/svelte/components/FloatingNode.svelte +27 -12
  123. package/dist/utils/floating-ui/svelte/components/FloatingTree.svelte +88 -44
  124. package/dist/utils/floating-ui/svelte/hooks/useFloating.svelte.js +7 -7
  125. package/dist/utils/floating-ui/svelte/hooks/useFloatingRootContext.svelte.js +1 -1
  126. package/dist/utils/floating-ui/svelte/types.d.ts +4 -4
  127. package/dist/utils/floating-ui/svelte-dom/types.d.ts +2 -2
  128. package/dist/utils/floating-ui/svelte-dom/useFloating.svelte.js +6 -6
  129. package/dist/utils/types.d.ts +11 -4
  130. package/package.json +2 -2
  131. package/dist/dialog/InternalDialog.svelte +0 -233
  132. package/dist/dialog/InternalDialog.svelte.d.ts +0 -42
@@ -1,18 +1,19 @@
1
- import type { Snippet } from "svelte";
2
1
  import type { ElementType, Props } from "../utils/types.js";
3
2
  declare const DEFAULT_SEPARATOR_TAG: "div";
4
3
  type SeparatorRenderPropArg = {};
5
4
  type SeparatorPropsWeControl = "role";
6
5
  export type MenuSeparatorProps<TTag extends ElementType = typeof DEFAULT_SEPARATOR_TAG> = Props<TTag, SeparatorRenderPropArg, SeparatorPropsWeControl>;
7
- export type MenuSeparatorChildren = Snippet<[SeparatorRenderPropArg]>;
8
6
  declare class __sveltets_Render<TTag extends ElementType = typeof DEFAULT_SEPARATOR_TAG> {
9
7
  props(): {
10
8
  as?: TTag | undefined;
11
- } & (Exclude<keyof import("../utils/types.js").PropsOf<TTag>, ("as" | "children" | "refName" | "class") | "role"> extends infer T extends keyof import("../utils/types.js").PropsOf<TTag> ? { [P in T]: import("../utils/types.js").PropsOf<TTag>[P]; } : never) & {
12
- children?: Snippet<[SeparatorRenderPropArg, Record<string, any>]> | undefined;
9
+ } & (Exclude<keyof import("../utils/types.js").PropsOf<TTag>, ("as" | "children" | "class") | "role"> extends infer T extends keyof import("../utils/types.js").PropsOf<TTag> ? { [P in T]: import("../utils/types.js").PropsOf<TTag>[P]; } : never) & {
10
+ children?: import("svelte").Snippet<[{
11
+ slot: SeparatorRenderPropArg;
12
+ props: Record<string, any>;
13
+ }]> | undefined;
13
14
  ref?: HTMLElement;
14
- } & (true extends (import("../utils/types.js").PropsOf<TTag> extends infer T_1 ? T_1 extends import("../utils/types.js").PropsOf<TTag> ? T_1 extends never ? never : "class" extends infer T_2 ? T_2 extends "class" ? T_2 extends keyof T_1 ? true : never : never : never : never : never) ? {
15
- class?: import("../utils/types.js").PropsOf<TTag>["class"] | ((bag: SeparatorRenderPropArg) => string) | undefined;
15
+ } & (true extends (import("svelte/elements").SvelteHTMLElements[TTag] extends infer T_1 ? T_1 extends import("svelte/elements").SvelteHTMLElements[TTag] ? T_1 extends never ? never : "class" extends infer T_2 ? T_2 extends "class" ? T_2 extends keyof T_1 ? true : never : never : never : never : never) ? {
16
+ class?: string | ((bag: SeparatorRenderPropArg) => string) | null | undefined;
16
17
  } : {});
17
18
  events(): {} & {
18
19
  [evt: string]: CustomEvent<any>;
@@ -45,32 +45,4 @@ export type MenuContext = StateDefinition & {
45
45
  setItemsElement(element: HTMLElement | null): void;
46
46
  };
47
47
  export declare function useMenuContext(component: string): MenuContext;
48
- export declare const stateReducer: (initialState: StateDefinition) => {
49
- readonly menuState: MenuStates;
50
- readonly buttonElement: HTMLButtonElement | null;
51
- readonly itemsElement: HTMLElement | null;
52
- readonly items: {
53
- id: string;
54
- dataRef: MenuItemDataRef;
55
- }[];
56
- readonly searchQuery: string;
57
- readonly activeItemIndex: number | null;
58
- readonly activationTrigger: ActivationTrigger;
59
- readonly __demoMode: boolean;
60
- closeMenu(): StateDefinition;
61
- openMenu(): StateDefinition;
62
- goToItem(action: {
63
- focus: Focus.Specific;
64
- id: string;
65
- trigger?: ActivationTrigger;
66
- } | {
67
- focus: Exclude<Focus, Focus.Specific>;
68
- trigger?: ActivationTrigger;
69
- }): StateDefinition;
70
- search(value: string): StateDefinition;
71
- clearSearch(): StateDefinition;
72
- registerItem(id: string, dataRef: MenuItemDataRef): StateDefinition;
73
- unregisterItem(id: string): StateDefinition;
74
- setButtonElement(element: HTMLButtonElement | null): StateDefinition;
75
- setItemsElement(element: HTMLElement | null): StateDefinition;
76
- };
48
+ export declare const createMenuContext: (initialState: StateDefinition) => MenuContext;
@@ -1,6 +1,6 @@
1
1
  import { calculateActiveIndex, Focus } from "../utils/calculate-active-index.js";
2
2
  import { sortByDomNode } from "../utils/focus-management.js";
3
- import { getContext } from "svelte";
3
+ import { getContext, setContext } from "svelte";
4
4
  export var MenuStates;
5
5
  (function (MenuStates) {
6
6
  MenuStates[MenuStates["Open"] = 0] = "Open";
@@ -14,7 +14,7 @@ export var ActivationTrigger;
14
14
  export function useMenuContext(component) {
15
15
  const context = getContext("MenuContext");
16
16
  if (!context) {
17
- let err = new Error(`<${component} /> is missing a parent <Menu /> component.`);
17
+ const err = new Error(`<${component} /> is missing a parent <Menu /> component.`);
18
18
  if (Error.captureStackTrace)
19
19
  Error.captureStackTrace(err, useMenuContext);
20
20
  throw err;
@@ -22,8 +22,8 @@ export function useMenuContext(component) {
22
22
  return context;
23
23
  }
24
24
  function adjustOrderedState(state, adjustment = (i) => i) {
25
- let currentActiveItem = state.activeItemIndex !== null ? state.items[state.activeItemIndex] : null;
26
- let sortedItems = sortByDomNode(adjustment(state.items.slice()), (item) => item.dataRef.current.domRef.current);
25
+ const currentActiveItem = state.activeItemIndex !== null ? state.items[state.activeItemIndex] : null;
26
+ const sortedItems = sortByDomNode(adjustment(state.items.slice()), (item) => item.dataRef.current.domRef.current);
27
27
  // If we inserted an item before the current active item then the active item index
28
28
  // would be wrong. To fix this, we will re-lookup the correct index.
29
29
  let adjustedActiveItemIndex = currentActiveItem ? sortedItems.indexOf(currentActiveItem) : null;
@@ -36,9 +36,9 @@ function adjustOrderedState(state, adjustment = (i) => i) {
36
36
  activeItemIndex: adjustedActiveItemIndex,
37
37
  };
38
38
  }
39
- export const stateReducer = (initialState) => {
40
- let _state = $state(initialState);
41
- return {
39
+ export const createMenuContext = (initialState) => {
40
+ const _state = $state(initialState);
41
+ const context = {
42
42
  get menuState() {
43
43
  return _state.menuState;
44
44
  },
@@ -104,17 +104,17 @@ export const stateReducer = (initialState) => {
104
104
  // or if the previous DOM node is already the first DOM node, then we don't
105
105
  // have to sort all the DOM nodes.
106
106
  else if (action.focus === Focus.Previous) {
107
- let activeItemIdx = _state.activeItemIndex;
107
+ const activeItemIdx = _state.activeItemIndex;
108
108
  if (activeItemIdx !== null) {
109
- let currentDom = _state.items[activeItemIdx].dataRef.current.domRef;
110
- let previousItemIndex = calculateActiveIndex(action, {
109
+ const currentDom = _state.items[activeItemIdx].dataRef.current.domRef;
110
+ const previousItemIndex = calculateActiveIndex(action, {
111
111
  resolveItems: () => _state.items,
112
112
  resolveActiveIndex: () => _state.activeItemIndex,
113
113
  resolveId: (item) => item.id,
114
114
  resolveDisabled: (item) => item.dataRef.current.disabled,
115
115
  });
116
116
  if (previousItemIndex !== null) {
117
- let previousDom = _state.items[previousItemIndex].dataRef.current.domRef;
117
+ const previousDom = _state.items[previousItemIndex].dataRef.current.domRef;
118
118
  if (
119
119
  // Next to each other
120
120
  currentDom.current?.previousElementSibling === previousDom.current ||
@@ -132,17 +132,17 @@ export const stateReducer = (initialState) => {
132
132
  // if the next DOM node is already the last DOM node, then we don't have to
133
133
  // sort all the DOM nodes.
134
134
  else if (action.focus === Focus.Next) {
135
- let activeItemIdx = _state.activeItemIndex;
135
+ const activeItemIdx = _state.activeItemIndex;
136
136
  if (activeItemIdx !== null) {
137
- let currentDom = _state.items[activeItemIdx].dataRef.current.domRef;
138
- let nextItemIndex = calculateActiveIndex(action, {
137
+ const currentDom = _state.items[activeItemIdx].dataRef.current.domRef;
138
+ const nextItemIndex = calculateActiveIndex(action, {
139
139
  resolveItems: () => _state.items,
140
140
  resolveActiveIndex: () => _state.activeItemIndex,
141
141
  resolveId: (item) => item.id,
142
142
  resolveDisabled: (item) => item.dataRef.current.disabled,
143
143
  });
144
144
  if (nextItemIndex !== null) {
145
- let nextDom = _state.items[nextItemIndex].dataRef.current.domRef;
145
+ const nextDom = _state.items[nextItemIndex].dataRef.current.domRef;
146
146
  if (
147
147
  // Next to each other
148
148
  currentDom.current?.nextElementSibling === nextDom.current ||
@@ -157,8 +157,8 @@ export const stateReducer = (initialState) => {
157
157
  // Slow path:
158
158
  //
159
159
  // Ensure all the items are correctly sorted according to DOM position
160
- let adjustedState = adjustOrderedState(_state);
161
- let activeItemIndex = calculateActiveIndex(action, {
160
+ const adjustedState = adjustOrderedState(_state);
161
+ const activeItemIndex = calculateActiveIndex(action, {
162
162
  resolveItems: () => adjustedState.items,
163
163
  resolveActiveIndex: () => adjustedState.activeItemIndex,
164
164
  resolveId: (item) => item.id,
@@ -169,16 +169,16 @@ export const stateReducer = (initialState) => {
169
169
  return _state;
170
170
  },
171
171
  search(value) {
172
- let wasAlreadySearching = _state.searchQuery !== "";
173
- let offset = wasAlreadySearching ? 0 : 1;
174
- let searchQuery = _state.searchQuery + value.toLowerCase();
175
- let reOrderedItems = _state.activeItemIndex !== null
172
+ const wasAlreadySearching = _state.searchQuery !== "";
173
+ const offset = wasAlreadySearching ? 0 : 1;
174
+ const searchQuery = _state.searchQuery + value.toLowerCase();
175
+ const reOrderedItems = _state.activeItemIndex !== null
176
176
  ? _state.items
177
177
  .slice(_state.activeItemIndex + offset)
178
178
  .concat(_state.items.slice(0, _state.activeItemIndex + offset))
179
179
  : _state.items;
180
- let matchingItem = reOrderedItems.find((item) => item.dataRef.current.textValue?.startsWith(searchQuery) && !item.dataRef.current.disabled);
181
- let matchIdx = matchingItem ? _state.items.indexOf(matchingItem) : -1;
180
+ const matchingItem = reOrderedItems.find((item) => item.dataRef.current.textValue?.startsWith(searchQuery) && !item.dataRef.current.disabled);
181
+ const matchIdx = matchingItem ? _state.items.indexOf(matchingItem) : -1;
182
182
  if (matchIdx === -1 || matchIdx === _state.activeItemIndex) {
183
183
  _state.searchQuery = searchQuery;
184
184
  return _state;
@@ -195,15 +195,15 @@ export const stateReducer = (initialState) => {
195
195
  return _state;
196
196
  },
197
197
  registerItem(id, dataRef) {
198
- let item = { id, dataRef };
199
- let adjustedState = adjustOrderedState(_state, (items) => [...items, item]);
198
+ const item = { id, dataRef };
199
+ const adjustedState = adjustOrderedState(_state, (items) => [...items, item]);
200
200
  _state.items = adjustedState.items;
201
201
  _state.activeItemIndex = adjustedState.activeItemIndex;
202
202
  return _state;
203
203
  },
204
204
  unregisterItem(id) {
205
- let adjustedState = adjustOrderedState(_state, (items) => {
206
- let idx = items.findIndex((a) => a.id === id);
205
+ const adjustedState = adjustOrderedState(_state, (items) => {
206
+ const idx = items.findIndex((a) => a.id === id);
207
207
  if (idx !== -1)
208
208
  items.splice(idx, 1);
209
209
  return items;
@@ -226,4 +226,6 @@ export const stateReducer = (initialState) => {
226
226
  return _state;
227
227
  },
228
228
  };
229
+ setContext("MenuContext", context);
230
+ return context;
229
231
  };
@@ -1,7 +1,7 @@
1
- export { default as Menu, type MenuProps, type MenuChildren } from "./Menu.svelte";
2
- export { default as MenuButton, type MenuButtonProps, type MenuButtonChildren } from "./MenuButton.svelte";
3
- export { default as MenuHeading, type MenuHeadingProps, type MenuHeadingChildren } from "./MenuHeading.svelte";
4
- export { default as MenuItem, type MenuItemProps, type MenuItemChildren } from "./MenuItem.svelte";
5
- export { default as MenuItems, type MenuItemsProps, type MenuItemsChildren } from "./MenuItems.svelte";
6
- export { default as MenuSection, type MenuSectionProps, type MenuSectionChildren } from "./MenuSection.svelte";
7
- export { default as MenuSeparator, type MenuSeparatorProps, type MenuSeparatorChildren } from "./MenuSeparator.svelte";
1
+ export { default as Menu, type MenuProps } from "./Menu.svelte";
2
+ export { default as MenuButton, type MenuButtonProps } from "./MenuButton.svelte";
3
+ export { default as MenuHeading, type MenuHeadingProps } from "./MenuHeading.svelte";
4
+ export { default as MenuItem, type MenuItemProps } from "./MenuItem.svelte";
5
+ export { default as MenuItems, type MenuItemsProps } from "./MenuItems.svelte";
6
+ export { default as MenuSection, type MenuSectionProps } from "./MenuSection.svelte";
7
+ export { default as MenuSeparator, type MenuSeparatorProps } from "./MenuSeparator.svelte";
@@ -1,159 +1,225 @@
1
- <script lang="ts" module>export const DEFAULT_POPOVER_TAG = "div";
2
- </script>
1
+ <script lang="ts" module>
2
+ import type { ElementType, Props } from "../utils/types.js"
3
3
 
4
- <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_POPOVER_TAG">import { getOwnerDocument } from "../utils/owner.js";
5
- import { setContext, untrack } from "svelte";
6
- import {
7
- createPopoverContext,
8
- PopoverStates,
9
- usePopoverGroupContext
10
- } from "./context.svelte.js";
11
- import { FocusableMode, getFocusableElements, isFocusableElement } from "../utils/focus-management.js";
12
- import { useNestedPortals } from "../portal/InternalPortal.svelte";
13
- import MainTreeProvider, { useMainTreeNode } from "../internal/MainTreeProvider.svelte";
14
- import { useRootContainers } from "../hooks/use-root-containers.svelte.js";
15
- import { useEventListener } from "../hooks/use-event-listener.svelte.js";
16
- import { useOutsideClick } from "../hooks/use-outside-click.svelte.js";
17
- import { useFloatingProvider } from "../internal/floating-provider.svelte.js";
18
- import { createCloseContext } from "../internal/close-provider.js";
19
- import { createOpenClosedContext, State } from "../internal/open-closed.js";
20
- import ElementOrComponent from "../utils/ElementOrComponent.svelte";
21
- let { ref = $bindable(), __demoMode = false, ...theirProps } = $props();
22
- let buttons = $state([]);
23
- const context = createPopoverContext({
24
- __demoMode,
25
- popoverState: __demoMode ? PopoverStates.Open : PopoverStates.Closed,
26
- buttons
27
- });
28
- const {
29
- popoverState,
30
- button,
31
- buttonId,
32
- panel,
33
- panelId,
34
- beforePanelSentinel,
35
- afterPanelSentinel,
36
- afterButtonSentinel
37
- } = $derived(context);
38
- const ownerDocument = $derived(getOwnerDocument(ref ?? button));
39
- const isPortalled = $derived.by(() => {
40
- if (!button) return false;
41
- if (!panel) return false;
42
- return untrack(() => {
43
- for (let root2 of document.querySelectorAll("body > *")) {
44
- if (Number(root2?.contains(button)) ^ Number(root2?.contains(panel))) {
45
- return true;
46
- }
47
- }
48
- let elements = getFocusableElements();
49
- let buttonIdx = elements.indexOf(button);
50
- let beforeIdx = (buttonIdx + elements.length - 1) % elements.length;
51
- let afterIdx = (buttonIdx + 1) % elements.length;
52
- let beforeElement = elements[beforeIdx];
53
- let afterElement = elements[afterIdx];
54
- if (!panel.contains(beforeElement) && !panel.contains(afterElement)) {
55
- return true;
56
- }
57
- return false;
58
- });
59
- });
60
- const registerBag = $derived({
61
- buttonId,
62
- panelId,
63
- close: () => context.closePopover()
64
- });
65
- const groupContext = usePopoverGroupContext();
66
- const registerPopover = $derived(groupContext?.registerPopover);
67
- const isFocusWithinPopoverGroup = () => {
68
- return groupContext?.isFocusWithinPopoverGroup() ?? (ownerDocument?.activeElement && (button?.contains(ownerDocument.activeElement) || panel?.contains(ownerDocument.activeElement)));
69
- };
70
- $effect(() => registerPopover?.(registerBag));
71
- const nestedPortals = useNestedPortals();
72
- const { portals } = $derived(nestedPortals);
73
- const mainTreeNode = useMainTreeNode({
74
- get fallbackMainTreeNode() {
75
- return button;
76
- }
77
- });
78
- const root = useRootContainers({
79
- get mainTreeNode() {
80
- return mainTreeNode.node;
81
- },
82
- get portals() {
83
- return portals;
84
- },
85
- get defaultContainers() {
86
- return [button, panel];
4
+ export const DEFAULT_POPOVER_TAG = "div" as const
5
+ type PopoverRenderPropArg = {
6
+ open: boolean
7
+ close(focusableElement?: HTMLElement | MouseEvent<HTMLElement>): void
87
8
  }
88
- });
89
- useEventListener({
90
- get element() {
91
- return ownerDocument?.defaultView;
92
- },
93
- type: "focus",
94
- listener: (event) => {
95
- if (event.target === window) return;
96
- if (!(event.target instanceof HTMLElement)) return;
97
- if (popoverState !== PopoverStates.Open) return;
98
- if (isFocusWithinPopoverGroup()) return;
99
- if (!button) return;
100
- if (!panel) return;
101
- if (root.contains(event.target)) return;
102
- if (beforePanelSentinel?.contains?.(event.target)) return;
103
- if (afterPanelSentinel?.contains?.(event.target)) return;
104
- if (afterButtonSentinel?.contains?.(event.target)) return;
105
- context.closePopover();
106
- },
107
- options: true
108
- });
109
- const outsideClickEnabled = $derived(popoverState === PopoverStates.Open);
110
- useOutsideClick({
111
- get enabled() {
112
- return outsideClickEnabled;
113
- },
114
- get containers() {
115
- return root.resolvedContainers;
116
- },
117
- cb: (event, target) => {
118
- context.closePopover();
119
- if (!isFocusableElement(target, FocusableMode.Loose)) {
120
- event.preventDefault();
121
- button?.focus();
9
+ type PopoverPropsWeControl = never
10
+
11
+ export type PopoverProps<TTag extends ElementType = typeof DEFAULT_POPOVER_TAG> = Props<
12
+ TTag,
13
+ PopoverRenderPropArg,
14
+ PopoverPropsWeControl,
15
+ {
16
+ __demoMode?: boolean
122
17
  }
18
+ >
19
+ </script>
20
+
21
+ <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_POPOVER_TAG">
22
+ import { getOwnerDocument } from "../utils/owner.js"
23
+
24
+ import { setContext, untrack } from "svelte"
25
+ import {
26
+ createPopoverContext,
27
+ PopoverStates,
28
+ usePopoverGroupContext,
29
+ type MouseEvent,
30
+ type PopoverAPIContext,
31
+ type PopoverPanelContext,
32
+ } from "./context.svelte.js"
33
+ import { FocusableMode, getFocusableElements, isFocusableElement } from "../utils/focus-management.js"
34
+ import { useNestedPortals } from "../portal/InternalPortal.svelte"
35
+ import MainTreeProvider, { useMainTreeNode } from "../internal/MainTreeProvider.svelte"
36
+ import { useRootContainers } from "../hooks/use-root-containers.svelte.js"
37
+ import { useEventListener } from "../hooks/use-event-listener.svelte.js"
38
+ import { useOutsideClick } from "../hooks/use-outside-click.svelte.js"
39
+ import { useFloatingProvider } from "../internal/floating-provider.svelte.js"
40
+ import { createCloseContext } from "../internal/close-provider.js"
41
+ import { createOpenClosedContext, State } from "../internal/open-closed.js"
42
+ import ElementOrComponent from "../utils/ElementOrComponent.svelte"
43
+
44
+ let { ref = $bindable(), __demoMode = false, ...theirProps }: { as?: TTag } & PopoverProps<TTag> = $props()
45
+
46
+ let buttons = $state([])
47
+ const context = createPopoverContext({
48
+ __demoMode,
49
+ popoverState: __demoMode ? PopoverStates.Open : PopoverStates.Closed,
50
+ buttons,
51
+ })
52
+ const {
53
+ popoverState,
54
+ button,
55
+ buttonId,
56
+ panel,
57
+ panelId,
58
+ beforePanelSentinel,
59
+ afterPanelSentinel,
60
+ afterButtonSentinel,
61
+ } = $derived(context)
62
+
63
+ const ownerDocument = $derived(getOwnerDocument(ref ?? button))
64
+
65
+ const isPortalled = $derived.by(() => {
66
+ if (!button) return false
67
+ if (!panel) return false
68
+
69
+ return untrack(() => {
70
+ // We are part of a different "root" tree, so therefore we can consider it portalled. This is a
71
+ // heuristic because 3rd party tools could use some form of portal, typically rendered at the
72
+ // end of the body but we don't have an actual reference to that.
73
+ for (let root of document.querySelectorAll("body > *")) {
74
+ if (Number(root?.contains(button)) ^ Number(root?.contains(panel))) {
75
+ return true
76
+ }
77
+ }
78
+
79
+ // Use another heuristic to try and calculate whether or not the focusable
80
+ // elements are near each other (aka, following the default focus/tab order
81
+ // from the browser). If they are then it doesn't really matter if they are
82
+ // portalled or not because we can follow the default tab order. But if they
83
+ // are not, then we can consider it being portalled so that we can ensure
84
+ // that tab and shift+tab (hopefully) go to the correct spot.
85
+ let elements = getFocusableElements()
86
+ let buttonIdx = elements.indexOf(button)
87
+
88
+ let beforeIdx = (buttonIdx + elements.length - 1) % elements.length
89
+ let afterIdx = (buttonIdx + 1) % elements.length
90
+
91
+ let beforeElement = elements[beforeIdx]
92
+ let afterElement = elements[afterIdx]
93
+
94
+ if (!panel.contains(beforeElement) && !panel.contains(afterElement)) {
95
+ return true
96
+ }
97
+
98
+ // It may or may not be portalled, but we don't really know.
99
+ return false
100
+ })
101
+ })
102
+
103
+ const registerBag = $derived({
104
+ buttonId,
105
+ panelId,
106
+ close: () => context.closePopover(),
107
+ })
108
+
109
+ const groupContext = usePopoverGroupContext()
110
+ const registerPopover = $derived(groupContext?.registerPopover)
111
+ const isFocusWithinPopoverGroup = () => {
112
+ return (
113
+ groupContext?.isFocusWithinPopoverGroup() ??
114
+ (ownerDocument?.activeElement &&
115
+ (button?.contains(ownerDocument.activeElement) || panel?.contains(ownerDocument.activeElement)))
116
+ )
123
117
  }
124
- });
125
- const close = (focusableElement) => {
126
- context.closePopover();
127
- const restoreElement = (() => {
128
- if (!focusableElement) return button;
129
- if (focusableElement instanceof HTMLElement) return focusableElement;
130
- return button;
131
- })();
132
- restoreElement?.focus();
133
- };
134
- const api = {
135
- close,
136
- get isPortalled() {
137
- return isPortalled;
138
- }
139
- };
140
- setContext("PopoverAPIContext", api);
141
- const slot = $derived({
142
- open: popoverState === PopoverStates.Open,
143
- close
144
- });
145
- useFloatingProvider();
146
- setContext("PopoverPanelContext", void 0);
147
- createCloseContext({
148
- get close() {
149
- return close;
118
+
119
+ $effect(() => registerPopover?.(registerBag))
120
+
121
+ const nestedPortals = useNestedPortals()
122
+ const { portals } = $derived(nestedPortals)
123
+ const mainTreeNode = useMainTreeNode({
124
+ get fallbackMainTreeNode() {
125
+ return button
126
+ },
127
+ })
128
+ const root = useRootContainers({
129
+ get mainTreeNode() {
130
+ return mainTreeNode.node
131
+ },
132
+ get portals() {
133
+ return portals
134
+ },
135
+ get defaultContainers() {
136
+ return [button, panel]
137
+ },
138
+ })
139
+
140
+ // Handle focus out
141
+ useEventListener({
142
+ get element() {
143
+ return ownerDocument?.defaultView
144
+ },
145
+ type: "focus",
146
+ listener: (event) => {
147
+ if (event.target === window) return
148
+ if (!(event.target instanceof HTMLElement)) return
149
+ if (popoverState !== PopoverStates.Open) return
150
+ if (isFocusWithinPopoverGroup()) return
151
+ if (!button) return
152
+ if (!panel) return
153
+ if (root.contains(event.target)) return
154
+ if (beforePanelSentinel?.contains?.(event.target)) return
155
+ if (afterPanelSentinel?.contains?.(event.target)) return
156
+ if (afterButtonSentinel?.contains?.(event.target)) return
157
+
158
+ context.closePopover()
159
+ },
160
+ options: true,
161
+ })
162
+
163
+ // Handle outside click
164
+ const outsideClickEnabled = $derived(popoverState === PopoverStates.Open)
165
+ useOutsideClick({
166
+ get enabled() {
167
+ return outsideClickEnabled
168
+ },
169
+ get containers() {
170
+ return root.resolvedContainers
171
+ },
172
+ cb: (event, target) => {
173
+ context.closePopover()
174
+
175
+ if (!isFocusableElement(target, FocusableMode.Loose)) {
176
+ event.preventDefault()
177
+ button?.focus()
178
+ }
179
+ },
180
+ })
181
+
182
+ const close = (focusableElement?: HTMLElement | MouseEvent<HTMLElement>) => {
183
+ context.closePopover()
184
+
185
+ const restoreElement = (() => {
186
+ if (!focusableElement) return button
187
+ if (focusableElement instanceof HTMLElement) return focusableElement
188
+
189
+ return button
190
+ })()
191
+
192
+ restoreElement?.focus()
150
193
  }
151
- });
152
- createOpenClosedContext({
153
- get value() {
154
- return context.popoverState === PopoverStates.Open ? State.Open : State.Closed;
194
+
195
+ const api: PopoverAPIContext = {
196
+ close,
197
+ get isPortalled() {
198
+ return isPortalled
199
+ },
155
200
  }
156
- });
201
+ setContext("PopoverAPIContext", api)
202
+
203
+ const slot = $derived({
204
+ open: popoverState === PopoverStates.Open,
205
+ close,
206
+ } satisfies PopoverRenderPropArg)
207
+
208
+ useFloatingProvider()
209
+
210
+ setContext<PopoverPanelContext | undefined>("PopoverPanelContext", undefined)
211
+
212
+ createCloseContext({
213
+ get close() {
214
+ return close
215
+ },
216
+ })
217
+
218
+ createOpenClosedContext({
219
+ get value() {
220
+ return context.popoverState === PopoverStates.Open ? State.Open : State.Closed
221
+ },
222
+ })
157
223
  </script>
158
224
 
159
225
  <MainTreeProvider node={mainTreeNode.node}>
@@ -12,11 +12,14 @@ import { type MouseEvent } from "./context.svelte.js";
12
12
  declare class __sveltets_Render<TTag extends ElementType = typeof DEFAULT_POPOVER_TAG> {
13
13
  props(): {
14
14
  as?: TTag | undefined;
15
- } & (Exclude<keyof import("../utils/types.js").PropsOf<TTag>, ("as" | "children" | "refName" | "class") | "__demoMode"> extends infer T extends keyof import("../utils/types.js").PropsOf<TTag> ? { [P in T]: import("../utils/types.js").PropsOf<TTag>[P]; } : never) & {
16
- children?: import("svelte").Snippet<[PopoverRenderPropArg, Record<string, any>]> | undefined;
15
+ } & (Exclude<keyof import("../utils/types.js").PropsOf<TTag>, ("as" | "children" | "class") | "__demoMode"> extends infer T extends keyof import("../utils/types.js").PropsOf<TTag> ? { [P in T]: import("../utils/types.js").PropsOf<TTag>[P]; } : never) & {
16
+ children?: import("svelte").Snippet<[{
17
+ slot: PopoverRenderPropArg;
18
+ props: Record<string, any>;
19
+ }]> | undefined;
17
20
  ref?: HTMLElement;
18
- } & (true extends (import("../utils/types.js").PropsOf<TTag> extends infer T_1 ? T_1 extends import("../utils/types.js").PropsOf<TTag> ? T_1 extends never ? never : "class" extends infer T_2 ? T_2 extends "class" ? T_2 extends keyof T_1 ? true : never : never : never : never : never) ? {
19
- class?: import("../utils/types.js").PropsOf<TTag>["class"] | ((bag: PopoverRenderPropArg) => string) | undefined;
21
+ } & (true extends (import("svelte/elements.js").SvelteHTMLElements[TTag] extends infer T_1 ? T_1 extends import("svelte/elements.js").SvelteHTMLElements[TTag] ? T_1 extends never ? never : "class" extends infer T_2 ? T_2 extends "class" ? T_2 extends keyof T_1 ? true : never : never : never : never : never) ? {
22
+ class?: string | ((bag: PopoverRenderPropArg) => string) | null | undefined;
20
23
  } : {}) & {
21
24
  __demoMode?: boolean;
22
25
  };