@geoffcox/sterling-svelte 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/package.json +26 -28
  2. package/README.md +0 -18
  3. package/dist/@types/clickOutside.d.ts +0 -15
  4. package/dist/Button.svelte +0 -25
  5. package/dist/Button.svelte.d.ts +0 -9
  6. package/dist/Callout.svelte +0 -177
  7. package/dist/Callout.svelte.d.ts +0 -15
  8. package/dist/Callout.types.d.ts +0 -11
  9. package/dist/Callout.types.js +0 -1
  10. package/dist/Checkbox.svelte +0 -43
  11. package/dist/Checkbox.svelte.d.ts +0 -10
  12. package/dist/Dialog.svelte +0 -151
  13. package/dist/Dialog.svelte.d.ts +0 -14
  14. package/dist/Dropdown.svelte +0 -109
  15. package/dist/Dropdown.svelte.d.ts +0 -18
  16. package/dist/Input.svelte +0 -55
  17. package/dist/Input.svelte.d.ts +0 -12
  18. package/dist/Label.constants.d.ts +0 -2
  19. package/dist/Label.constants.js +0 -2
  20. package/dist/Label.svelte +0 -91
  21. package/dist/Label.svelte.d.ts +0 -17
  22. package/dist/Link.svelte +0 -25
  23. package/dist/Link.svelte.d.ts +0 -12
  24. package/dist/List.constants.d.ts +0 -1
  25. package/dist/List.constants.js +0 -1
  26. package/dist/List.svelte +0 -203
  27. package/dist/List.svelte.d.ts +0 -20
  28. package/dist/List.types.d.ts +0 -5
  29. package/dist/List.types.js +0 -1
  30. package/dist/ListItem.svelte +0 -49
  31. package/dist/ListItem.svelte.d.ts +0 -13
  32. package/dist/Menu.svelte +0 -83
  33. package/dist/Menu.svelte.d.ts +0 -13
  34. package/dist/MenuBar.constants.d.ts +0 -1
  35. package/dist/MenuBar.constants.js +0 -1
  36. package/dist/MenuBar.svelte +0 -113
  37. package/dist/MenuBar.svelte.d.ts +0 -13
  38. package/dist/MenuBar.types.d.ts +0 -4
  39. package/dist/MenuBar.types.js +0 -1
  40. package/dist/MenuButton.svelte +0 -116
  41. package/dist/MenuButton.svelte.d.ts +0 -20
  42. package/dist/MenuItem.constants.d.ts +0 -2
  43. package/dist/MenuItem.constants.js +0 -2
  44. package/dist/MenuItem.svelte +0 -342
  45. package/dist/MenuItem.svelte.d.ts +0 -22
  46. package/dist/MenuItem.types.d.ts +0 -20
  47. package/dist/MenuItem.types.js +0 -1
  48. package/dist/MenuItem.utils.d.ts +0 -7
  49. package/dist/MenuItem.utils.js +0 -36
  50. package/dist/MenuSeparator.svelte +0 -11
  51. package/dist/MenuSeparator.svelte.d.ts +0 -6
  52. package/dist/Popover.constants.d.ts +0 -1
  53. package/dist/Popover.constants.js +0 -14
  54. package/dist/Popover.svelte +0 -121
  55. package/dist/Popover.svelte.d.ts +0 -15
  56. package/dist/Popover.types.d.ts +0 -4
  57. package/dist/Popover.types.js +0 -1
  58. package/dist/Portal.constants.d.ts +0 -2
  59. package/dist/Portal.constants.js +0 -2
  60. package/dist/Portal.types.d.ts +0 -6
  61. package/dist/Portal.types.js +0 -1
  62. package/dist/Progress.constants.d.ts +0 -1
  63. package/dist/Progress.constants.js +0 -1
  64. package/dist/Progress.svelte +0 -36
  65. package/dist/Progress.svelte.d.ts +0 -12
  66. package/dist/Progress.types.d.ts +0 -4
  67. package/dist/Progress.types.js +0 -1
  68. package/dist/Radio.svelte +0 -53
  69. package/dist/Radio.svelte.d.ts +0 -13
  70. package/dist/Select.svelte +0 -196
  71. package/dist/Select.svelte.d.ts +0 -20
  72. package/dist/Slider.svelte +0 -133
  73. package/dist/Slider.svelte.d.ts +0 -19
  74. package/dist/Switch.svelte +0 -61
  75. package/dist/Switch.svelte.d.ts +0 -21
  76. package/dist/Tab.svelte +0 -51
  77. package/dist/Tab.svelte.d.ts +0 -12
  78. package/dist/TabList.constants.d.ts +0 -1
  79. package/dist/TabList.constants.js +0 -1
  80. package/dist/TabList.svelte +0 -202
  81. package/dist/TabList.svelte.d.ts +0 -18
  82. package/dist/TabList.types.d.ts +0 -5
  83. package/dist/TabList.types.js +0 -1
  84. package/dist/TextArea.constants.d.ts +0 -1
  85. package/dist/TextArea.constants.js +0 -1
  86. package/dist/TextArea.svelte +0 -74
  87. package/dist/TextArea.svelte.d.ts +0 -19
  88. package/dist/TextArea.types.d.ts +0 -4
  89. package/dist/TextArea.types.js +0 -1
  90. package/dist/Tooltip.svelte +0 -63
  91. package/dist/Tooltip.svelte.d.ts +0 -10
  92. package/dist/Tree.constants.d.ts +0 -1
  93. package/dist/Tree.constants.js +0 -1
  94. package/dist/Tree.svelte +0 -53
  95. package/dist/Tree.svelte.d.ts +0 -15
  96. package/dist/Tree.types.d.ts +0 -5
  97. package/dist/Tree.types.js +0 -1
  98. package/dist/TreeChevron.svelte +0 -27
  99. package/dist/TreeChevron.svelte.d.ts +0 -9
  100. package/dist/TreeItem.constants.d.ts +0 -1
  101. package/dist/TreeItem.constants.js +0 -1
  102. package/dist/TreeItem.svelte +0 -329
  103. package/dist/TreeItem.svelte.d.ts +0 -22
  104. package/dist/TreeItem.types.d.ts +0 -4
  105. package/dist/TreeItem.types.js +0 -1
  106. package/dist/actions/applyLightDarkMode.d.ts +0 -10
  107. package/dist/actions/applyLightDarkMode.js +0 -36
  108. package/dist/actions/clickOutside.d.ts +0 -15
  109. package/dist/actions/clickOutside.js +0 -28
  110. package/dist/actions/extraClass.d.ts +0 -8
  111. package/dist/actions/extraClass.js +0 -14
  112. package/dist/actions/forwardEvents.d.ts +0 -12
  113. package/dist/actions/forwardEvents.js +0 -32
  114. package/dist/actions/portal.d.ts +0 -8
  115. package/dist/actions/portal.js +0 -19
  116. package/dist/actions/trapKeyboardFocus.d.ts +0 -3
  117. package/dist/actions/trapKeyboardFocus.js +0 -52
  118. package/dist/idGenerator.d.ts +0 -4
  119. package/dist/idGenerator.js +0 -10
  120. package/dist/index.d.ts +0 -59
  121. package/dist/index.js +0 -54
  122. package/dist/mediaQueries/prefersColorSchemeDark.d.ts +0 -2
  123. package/dist/mediaQueries/prefersColorSchemeDark.js +0 -14
  124. package/dist/mediaQueries/prefersReducedMotion.d.ts +0 -2
  125. package/dist/mediaQueries/prefersReducedMotion.js +0 -14
  126. package/dist/mediaQueries/usingKeyboard.d.ts +0 -2
  127. package/dist/mediaQueries/usingKeyboard.js +0 -17
@@ -1,342 +0,0 @@
1
- <svelte:options runes={true} />
2
-
3
- <script lang="ts">import { getContext, setContext, tick } from 'svelte';
4
- import { idGenerator } from './idGenerator';
5
- import Menu from './Menu.svelte';
6
- import { MENU_BAR_CONTEXT_KEY } from './MenuBar.constants';
7
- import { MENU_ITEM_CONTEXT_KEY } from './MenuItem.constants';
8
- import { isElementEnabledMenuItem } from './MenuItem.utils';
9
- import Popover from './Popover.svelte';
10
- import { usingKeyboard } from './mediaQueries/usingKeyboard';
11
- let { checked, children, class: _class, disabled, item, menuClass, onClose, onOpen, onSelect, role = 'menuitem', text, shortcut, value, ...rest } = $props();
12
- const menuItemContext = getContext(MENU_ITEM_CONTEXT_KEY) || {};
13
- const menuBarContext = getContext(MENU_BAR_CONTEXT_KEY) || {};
14
- const instanceId = idGenerator.nextId('MenuItem');
15
- let displayId = $derived(`${value}-display-${instanceId}`);
16
- let open = $derived(menuItemContext.openValues?.includes(value));
17
- let prevOpen = $state(menuItemContext.openValues?.includes(value));
18
- let menuId = $derived(`${value}-menu-${instanceId}`);
19
- let menuItemRef = $state();
20
- let menuRef = $state();
21
- //#region methods
22
- export const blur = () => {
23
- menuItemRef?.blur();
24
- };
25
- export const click = () => {
26
- menuItemRef?.click();
27
- };
28
- export const focus = (options) => {
29
- menuItemRef?.focus(options);
30
- };
31
- //#endregion
32
- //#region events
33
- const raiseClose = (value) => {
34
- onClose?.(value);
35
- menuItemContext.onClose?.(value);
36
- };
37
- const raiseOpen = (value) => {
38
- onOpen?.(value);
39
- menuItemContext.onOpen?.(value);
40
- };
41
- const raiseSelect = (value) => {
42
- onSelect?.(value);
43
- menuItemContext.onSelect?.(value);
44
- };
45
- $effect(() => {
46
- if (open !== prevOpen) {
47
- open ? raiseOpen(value) : raiseClose(value);
48
- }
49
- prevOpen = open;
50
- });
51
- //#endregion
52
- //#region focus
53
- const focusPreviousMenuItem = () => {
54
- let candidate = menuItemRef?.previousElementSibling || menuItemRef?.parentElement?.lastElementChild;
55
- while (candidate && !isElementEnabledMenuItem(candidate)) {
56
- candidate = candidate.previousElementSibling || menuItemRef?.parentElement?.lastElementChild;
57
- if (candidate === menuItemRef) {
58
- return false;
59
- }
60
- }
61
- candidate?.focus();
62
- return !!candidate;
63
- };
64
- const focusNextMenuItem = () => {
65
- let candidate = menuItemRef?.nextElementSibling || menuItemRef?.parentElement?.firstElementChild;
66
- while (candidate && !isElementEnabledMenuItem(candidate)) {
67
- candidate = candidate.nextElementSibling || menuItemRef?.parentElement?.firstElementChild;
68
- if (candidate === menuItemRef) {
69
- return false;
70
- }
71
- }
72
- candidate?.focus();
73
- return !!candidate;
74
- };
75
- //#endregion
76
- //#region open/close
77
- // opens the menu for this menu item
78
- const openMenu = () => {
79
- if (!menuItemContext.openValues.includes(value)) {
80
- // slice to depth to close any sibling menus that are open
81
- menuItemContext.openValues = [
82
- ...menuItemContext.openValues.slice(0, menuItemContext.depth),
83
- value
84
- ];
85
- }
86
- };
87
- // closes the menu for this menu item
88
- const closeMenu = async () => {
89
- const index = menuItemContext.openValues.indexOf(value);
90
- if (index !== -1) {
91
- menuItemContext.openValues = [...menuItemContext.openValues.slice(0, index)];
92
- await tick();
93
- menuItemRef?.focus();
94
- }
95
- };
96
- const closeAllMenus = () => {
97
- menuItemContext.openValues = [];
98
- };
99
- //#endregion
100
- //#region event handlers
101
- const onKeyDown = async (event) => {
102
- if (!disabled && !event.altKey && !event.ctrlKey && !event.shiftKey) {
103
- switch (event.key) {
104
- case 'ArrowDown':
105
- // ARIA menubar/menuitem:
106
- // If the currently focused menuitem has a submenu,
107
- // opens the submenu and places focus on the first item in the submenu.
108
- if (menuItemContext.isMenuBarItem && children) {
109
- openMenu();
110
- setTimeout(async () => {
111
- await tick();
112
- menuRef?.focusFirstMenuItem();
113
- }, 10);
114
- event.preventDefault();
115
- event.stopPropagation();
116
- return false;
117
- }
118
- if (!menuItemContext.isMenuBarItem) {
119
- // ARIA menuitem:
120
- // Moves focus to the next item, optionally wrapping from the last to the first.
121
- focusNextMenuItem();
122
- event.preventDefault();
123
- event.stopPropagation();
124
- return false;
125
- }
126
- break;
127
- case 'ArrowLeft':
128
- // ARIA menubar/menuitem:
129
- // Moves focus to the previous item, optionally wrapping from the first to the last.
130
- if (menuItemContext.isMenuBarItem) {
131
- focusPreviousMenuItem();
132
- event.preventDefault();
133
- event.stopPropagation();
134
- return false;
135
- }
136
- // ARIA menuitem:
137
- // When focus is in a submenu of an item in a menu,
138
- // closes the submenu and returns focus to the parent menuitem.
139
- if (menuItemContext.depth && menuItemContext.depth > 1) {
140
- menuItemContext.closeContainingMenu?.();
141
- event.preventDefault();
142
- event.stopPropagation();
143
- return false;
144
- }
145
- // ARIA menubar/menuitem:
146
- // When focus is in a submenu of an item in a menubar,
147
- // closes the submenu,
148
- // moves focus to the previous item in the menubar,
149
- // and,
150
- // if focus is now on a menuitem with a submenu,
151
- // either opens the submenu of that menuitem without moving focus into the submenu,
152
- // or opens the submenu of that menuitem and places focus on the first item in the submenu.
153
- menuBarContext.openPreviousMenuBarItem?.();
154
- event.preventDefault();
155
- event.stopPropagation();
156
- return false;
157
- case 'ArrowRight':
158
- // ARIA menubar:
159
- // Moves focus to the next item, optionally wrapping from the last to the first.
160
- if (menuItemContext.isMenuBarItem) {
161
- focusNextMenuItem();
162
- event.preventDefault();
163
- event.stopPropagation();
164
- return false;
165
- }
166
- // ARIA menuitem:
167
- // When focus is in a menu and on a menuitem that has a submenu,
168
- // opens the submenu and places focus on its first item
169
- if (children) {
170
- openMenu();
171
- setTimeout(async () => {
172
- await tick();
173
- menuRef?.focusFirstMenuItem();
174
- }, 10);
175
- event.preventDefault();
176
- event.stopPropagation();
177
- return false;
178
- }
179
- // ARIA menubar/menuitem:
180
- // When focus is in a menu and on an item that does not have a submenu,
181
- // closes the submenu and any parent menus,
182
- // moves focus to the next item in the menubar,
183
- // and,
184
- // if focus is now on a menuitem with a submenu,
185
- // either opens the submenu of that menuitem without moving focus into the submenu,
186
- // or opens the submenu of that menuitem and places focus on the first item in the submenu.
187
- if (menuBarContext.openNextMenuBarItem) {
188
- menuBarContext.openNextMenuBarItem();
189
- event.preventDefault();
190
- event.stopPropagation();
191
- return false;
192
- }
193
- break;
194
- case 'ArrowUp':
195
- // ARIA menubar/menuitem:
196
- // If the currently focused menuitem has a submenu,
197
- // opens the submenu and places focus on the last item in the submenu.
198
- if (menuItemContext.isMenuBarItem && children) {
199
- openMenu();
200
- setTimeout(async () => {
201
- await tick();
202
- menuRef?.focusLastMenuItem();
203
- }, 10);
204
- event.preventDefault();
205
- event.stopPropagation();
206
- return false;
207
- }
208
- // ARIA menuitem:
209
- // Moves focus to the previous item, optionally wrapping from the first to the last.
210
- if (!menuItemContext.isMenuBarItem) {
211
- focusPreviousMenuItem();
212
- event.preventDefault();
213
- event.stopPropagation();
214
- return false;
215
- }
216
- break;
217
- case 'Escape':
218
- // ARIA menuitem:
219
- // Close the menu that contains focus and return focus to the element or context,
220
- // e.g., menu button or parent menuitem, from which the menu was opened.
221
- // open = false;
222
- closeAllMenus();
223
- event.preventDefault();
224
- event.stopPropagation();
225
- return false;
226
- }
227
- }
228
- rest.onkeydown?.(event);
229
- };
230
- const onMouseEnter = (event) => {
231
- menuItemRef?.focus();
232
- rest.onmouseenter?.(event);
233
- };
234
- const onClick = (event) => {
235
- if (!disabled) {
236
- if (children) {
237
- if (!menuItemContext.openValues.includes(value)) {
238
- openMenu();
239
- if ($usingKeyboard) {
240
- setTimeout(async () => {
241
- await tick();
242
- menuRef?.focusFirstMenuItem();
243
- }, 10);
244
- }
245
- }
246
- else {
247
- closeMenu();
248
- }
249
- event.preventDefault();
250
- event.stopPropagation();
251
- return false;
252
- }
253
- else {
254
- raiseSelect(value);
255
- closeAllMenus();
256
- event.preventDefault();
257
- event.stopPropagation();
258
- return false;
259
- }
260
- }
261
- rest.onclick?.(event);
262
- };
263
- //#endregion
264
- //#region set context
265
- let menuItemChildContext = {
266
- isMenuBarItem: false,
267
- get openValues() {
268
- return menuItemContext.openValues;
269
- },
270
- set openValues(value) {
271
- menuItemContext.openValues = value;
272
- },
273
- rootValue: menuItemContext.rootValue || value,
274
- depth: menuItemContext.depth ? menuItemContext.depth + 1 : 1,
275
- closeContainingMenu: closeMenu,
276
- onOpen: raiseOpen,
277
- onClose: raiseClose,
278
- onSelect: raiseSelect
279
- };
280
- setContext(MENU_ITEM_CONTEXT_KEY, menuItemChildContext);
281
- //#endregion
282
- </script>
283
-
284
- {#snippet renderDefaultItem()}
285
- <div class="sterling-menu-item-display" class:disabled>
286
- <div
287
- class="check"
288
- class:checkmark={role === 'menuitemcheckbox'}
289
- class:bullet={role === 'menuitemradio'}
290
- class:checked
291
- ></div>
292
- <div class="content">
293
- {text}
294
- </div>
295
- {#if shortcut}
296
- <div class="shortcut">
297
- {shortcut}
298
- </div>
299
- {/if}
300
- <div class="chevron" class:has-children={!menuItemContext.isMenuBarItem && !!children}></div>
301
- </div>
302
- {/snippet}
303
-
304
- <button
305
- bind:this={menuItemRef}
306
- aria-controls={menuId}
307
- aria-disabled={disabled}
308
- aria-expanded={open}
309
- aria-haspopup={!!children}
310
- aria-owns={menuId}
311
- class={['sterling-menu-item', _class].filter(Boolean).join(' ')}
312
- class:using-keyboard={usingKeyboard}
313
- data-value={value}
314
- data-root-value={menuItemContext.rootValue}
315
- {disabled}
316
- {role}
317
- tabindex={0}
318
- type="button"
319
- {...rest}
320
- onclick={onClick}
321
- onkeydown={onKeyDown}
322
- onmouseenter={onMouseEnter}
323
- >
324
- <div class="item" id={displayId}>
325
- {#if item}
326
- {@render item()}
327
- {:else}
328
- {@render renderDefaultItem()}
329
- {/if}
330
- </div>
331
- {#if menuItemRef && open && children}
332
- <Popover
333
- reference={menuItemRef}
334
- placement={menuItemContext.isMenuBarItem ? 'bottom-start' : 'right-start'}
335
- {open}
336
- >
337
- <Menu bind:this={menuRef} id={menuId} class={menuClass}>
338
- {@render children()}
339
- </Menu>
340
- </Popover>
341
- {/if}
342
- </button>
@@ -1,22 +0,0 @@
1
- import type { MenuItemRole } from './MenuItem.types';
2
- import { type Snippet } from 'svelte';
3
- import type { HTMLButtonAttributes } from 'svelte/elements';
4
- type Props = HTMLButtonAttributes & {
5
- checked?: boolean | null;
6
- item?: Snippet;
7
- menuClass?: string;
8
- onClose?: (value: string) => void;
9
- onOpen?: (value: string) => void;
10
- onSelect?: (value: string) => void;
11
- role?: MenuItemRole;
12
- shortcut?: string;
13
- text?: string;
14
- value: string;
15
- };
16
- declare const MenuItem: import("svelte").Component<Props, {
17
- blur: () => void;
18
- click: () => void;
19
- focus: (options?: FocusOptions) => void;
20
- }, "">;
21
- type MenuItem = ReturnType<typeof MenuItem>;
22
- export default MenuItem;
@@ -1,20 +0,0 @@
1
- import type { MENU_ITEM_ROLES } from './MenuItem.constants';
2
- type MenuItemRoleTuple = typeof MENU_ITEM_ROLES;
3
- export type MenuItemRole = MenuItemRoleTuple[number];
4
- export type MenuItemRegistration = {
5
- value: string;
6
- open: () => void;
7
- close: () => void;
8
- focus: () => void;
9
- };
10
- export type MenuItemContext = {
11
- isMenuBarItem?: boolean;
12
- openValues: string[];
13
- rootValue?: string;
14
- depth?: number;
15
- closeContainingMenu?: () => void;
16
- onOpen?: (value: string) => void;
17
- onClose?: (value: string) => void;
18
- onSelect?: (value: string) => void;
19
- };
20
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1,7 +0,0 @@
1
- import type { MenuItemRegistration } from './MenuItem.types';
2
- export declare const focusPreviousChild: (children: MenuItemRegistration[], currentValue: string) => void;
3
- export declare const focusNextChild: (children: MenuItemRegistration[], currentValue: string) => void;
4
- export declare const focusFirstChild: (children: MenuItemRegistration[]) => void;
5
- export declare const focusLastChild: (children: MenuItemRegistration[]) => void;
6
- export declare const isElementMenuItem: (candidate: Element | null | undefined) => boolean;
7
- export declare const isElementEnabledMenuItem: (candidate: Element | null | undefined) => boolean;
@@ -1,36 +0,0 @@
1
- export const focusPreviousChild = (children, currentValue) => {
2
- const index = children?.findIndex((menuItem) => menuItem.value === currentValue);
3
- if (index !== -1) {
4
- const focusIndex = index === 0 ? children.length - 1 : index - 1;
5
- children[focusIndex].focus();
6
- }
7
- };
8
- export const focusNextChild = (children, currentValue) => {
9
- const index = children?.findIndex((menuItem) => menuItem.value === currentValue);
10
- if (index !== -1) {
11
- const focusIndex = (index + 1) % children.length;
12
- children[focusIndex].focus();
13
- }
14
- };
15
- export const focusFirstChild = (children) => {
16
- children?.[0]?.focus();
17
- };
18
- export const focusLastChild = (children) => {
19
- children?.[children.length - 1]?.focus();
20
- };
21
- export const isElementMenuItem = (candidate) => {
22
- if (!candidate)
23
- return false;
24
- const dataValue = candidate?.getAttribute('data-value');
25
- const role = candidate?.getAttribute('role');
26
- return ((role === 'menuitem' || role === 'menuitemcheckbox' || role === 'menuitemradio') &&
27
- dataValue !== null &&
28
- dataValue !== undefined);
29
- };
30
- export const isElementEnabledMenuItem = (candidate) => {
31
- if (!isElementMenuItem(candidate)) {
32
- return false;
33
- }
34
- const disabled = candidate?.getAttribute('disabled');
35
- return disabled === null || disabled === 'false';
36
- };
@@ -1,11 +0,0 @@
1
- <svelte:options runes={true} />
2
-
3
- <script lang="ts">let { class: _class, ...rest } = $props();
4
- export {};
5
- </script>
6
-
7
- <div
8
- class={['sterling-menu-separator', _class].filter(Boolean).join(' ')}
9
- role="separator"
10
- {...rest}
11
- ></div>
@@ -1,6 +0,0 @@
1
- /// <reference types="svelte" />
2
- import type { HTMLAttributes } from 'svelte/elements';
3
- type Props = HTMLAttributes<HTMLDivElement>;
4
- declare const MenuSeparator: import("svelte").Component<Props, {}, "">;
5
- type MenuSeparator = ReturnType<typeof MenuSeparator>;
6
- export default MenuSeparator;
@@ -1 +0,0 @@
1
- export declare const POPOVER_PLACEMENTS: string[];
@@ -1,14 +0,0 @@
1
- export const POPOVER_PLACEMENTS = [
2
- 'top-start',
3
- 'top',
4
- 'top-end',
5
- 'right-start',
6
- 'right',
7
- 'right-end',
8
- 'bottom-end',
9
- 'bottom',
10
- 'bottom-start',
11
- 'left-end',
12
- 'left',
13
- 'left-start'
14
- ];
@@ -1,121 +0,0 @@
1
- <svelte:options runes={true} />
2
-
3
- <script lang="ts">import { getContext, onMount, tick } from 'svelte';
4
- import { autoUpdate, computePosition, flip, offset } from '@floating-ui/dom';
5
- import { portal } from './actions/portal';
6
- import { STERLING_PORTAL_HOST_ID, STERLING_PORTAL_CONTEXT_ID } from './Portal.constants';
7
- let { children, conditionalRender = $bindable(true), crossAxisOffset = $bindable(0), mainAxisOffset = $bindable(0), open = $bindable(false), placement = $bindable('top-start'), portalHost, reference, class: _class, ...rest } = $props();
8
- let popupRef = $state(undefined);
9
- let popupPosition = $state({ x: 0, y: 0 });
10
- let floatingUIPlacement = $derived(placement);
11
- let bodyHeight = $state(0);
12
- let resizeObserver = undefined;
13
- const { portalHost: contextPortalHost } = getContext(STERLING_PORTAL_CONTEXT_ID) || {
14
- portalHost: undefined
15
- };
16
- // ----- Portal Host ----- //
17
- const ensurePortalHost = async () => {
18
- await tick();
19
- // use the host set from context, usually set from a Dialog
20
- let host = $contextPortalHost;
21
- // use or create the sterling portal host
22
- if (!host && globalThis?.document) {
23
- host = globalThis.document.querySelector(`#${STERLING_PORTAL_HOST_ID}`);
24
- // fallback to creating the sterling portal host
25
- if (!host) {
26
- host = globalThis.document.createElement('div');
27
- host.id = STERLING_PORTAL_HOST_ID;
28
- host.style.overflow = 'visible';
29
- globalThis.document.body.append(host);
30
- }
31
- }
32
- portalHost = host;
33
- };
34
- // ----- Position ----- //
35
- let middleware = $derived([
36
- offset({ mainAxis: mainAxisOffset, crossAxis: crossAxisOffset }),
37
- flip()
38
- ]);
39
- const computePopoverPosition = async () => {
40
- if (reference && popupRef) {
41
- popupPosition = await computePosition(reference, popupRef, {
42
- placement: floatingUIPlacement,
43
- middleware
44
- });
45
- }
46
- else {
47
- popupPosition = { x: 0, y: 0 };
48
- }
49
- };
50
- // whenever a positioned element is portaled it needs resubscription to auto-update
51
- let cleanupAutoUpdate = () => { };
52
- const autoUpdatePopoverPosition = () => {
53
- cleanupAutoUpdate();
54
- if (reference && popupRef) {
55
- cleanupAutoUpdate = autoUpdate(reference, popupRef, computePopoverPosition);
56
- }
57
- };
58
- $effect(() => {
59
- autoUpdatePopoverPosition();
60
- return () => {
61
- cleanupAutoUpdate();
62
- cleanupAutoUpdate = () => { };
63
- };
64
- });
65
- $effect(() => {
66
- bodyHeight;
67
- computePopoverPosition();
68
- });
69
- // ----- EventHandlers ----- //
70
- $effect(() => {
71
- ensurePortalHost();
72
- resizeObserver = new ResizeObserver((entries) => {
73
- bodyHeight = entries[0].target.clientHeight;
74
- });
75
- // start observing a DOM node
76
- resizeObserver.observe(document.body);
77
- return () => {
78
- resizeObserver?.unobserve(document.body);
79
- resizeObserver?.disconnect();
80
- resizeObserver = undefined;
81
- };
82
- });
83
- const onKeydown = (event) => {
84
- if (event.key === 'Escape') {
85
- open = false;
86
- }
87
- rest.onkeydown?.(event);
88
- };
89
- //TODO: Is this necessary?
90
- ensurePortalHost();
91
- </script>
92
-
93
- {#if open || !conditionalRender}
94
- <div use:portal={{ target: portalHost }} class="sterling-popover-portal">
95
- <!-- svelte-ignore a11y_no_static_element_interactions -->
96
- <div
97
- bind:this={popupRef}
98
- class={['sterling-popover', _class].filter(Boolean).join(' ')}
99
- class:open
100
- class:top={popupPosition.placement === 'top'}
101
- class:top-start={popupPosition.placement === 'top-start'}
102
- class:top-end={popupPosition.placement === 'top-end'}
103
- class:right={popupPosition.placement === 'right'}
104
- class:right-start={popupPosition.placement === 'right-start'}
105
- class:right-end={popupPosition.placement === 'right-end'}
106
- class:bottom={popupPosition.placement === 'bottom'}
107
- class:bottom-start={popupPosition.placement === 'bottom-start'}
108
- class:bottom-end={popupPosition.placement === 'bottom-end'}
109
- class:left={popupPosition.placement === 'left'}
110
- class:left-start={popupPosition.placement === 'left-start'}
111
- class:left-end={popupPosition.placement === 'left-end'}
112
- {...rest}
113
- onkeydown={onKeydown}
114
- style="left:{popupPosition.x}px; top:{popupPosition.y}px"
115
- >
116
- {#if children}
117
- {@render children()}
118
- {/if}
119
- </div>
120
- </div>
121
- {/if}
@@ -1,15 +0,0 @@
1
- /// <reference types="svelte" />
2
- import type { PopoverPlacement } from './Popover.types';
3
- import type { HTMLAttributes } from 'svelte/elements';
4
- type Props = HTMLAttributes<HTMLDivElement> & {
5
- conditionalRender?: boolean;
6
- crossAxisOffset?: number;
7
- mainAxisOffset?: number;
8
- open?: boolean | null;
9
- placement?: PopoverPlacement;
10
- portalHost?: HTMLElement;
11
- reference?: HTMLElement;
12
- };
13
- declare const Popover: import("svelte").Component<Props, {}, "conditionalRender" | "crossAxisOffset" | "mainAxisOffset" | "open" | "placement">;
14
- type Popover = ReturnType<typeof Popover>;
15
- export default Popover;
@@ -1,4 +0,0 @@
1
- import type { POPOVER_PLACEMENTS } from './Popover.constants';
2
- type PopoverPlacementTuple = typeof POPOVER_PLACEMENTS;
3
- export type PopoverPlacement = PopoverPlacementTuple[number];
4
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1,2 +0,0 @@
1
- export declare const STERLING_PORTAL_HOST_ID = "SterlingPortalHost";
2
- export declare const STERLING_PORTAL_CONTEXT_ID = "SterlingPortalContext";
@@ -1,2 +0,0 @@
1
- export const STERLING_PORTAL_HOST_ID = 'SterlingPortalHost';
2
- export const STERLING_PORTAL_CONTEXT_ID = 'SterlingPortalContext';
@@ -1,6 +0,0 @@
1
- /// <reference types="svelte" />
2
- import type { Readable } from 'svelte/store';
3
- export type PortalContext = {
4
- /** The portal host for usePortal */
5
- portalHost: Readable<HTMLElement | undefined>;
6
- };
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export declare const PROGRESS_ORIENTATIONS: string[];
@@ -1 +0,0 @@
1
- export const PROGRESS_ORIENTATIONS = ['horizontal', 'vertical'];