@geoffcox/sterling-svelte 2.0.1 → 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 (71) hide show
  1. package/README.md +1 -1
  2. package/dist/Button.svelte +18 -14
  3. package/dist/Button.svelte.d.ts +0 -1
  4. package/dist/Callout.svelte +162 -96
  5. package/dist/Callout.svelte.d.ts +1 -2
  6. package/dist/Checkbox.svelte +34 -15
  7. package/dist/Checkbox.svelte.d.ts +0 -1
  8. package/dist/Dialog.svelte +121 -71
  9. package/dist/Dialog.svelte.d.ts +1 -1
  10. package/dist/Dropdown.svelte +106 -56
  11. package/dist/Dropdown.svelte.d.ts +8 -3
  12. package/dist/Input.svelte +54 -29
  13. package/dist/Input.svelte.d.ts +1 -2
  14. package/dist/Label.svelte +99 -55
  15. package/dist/Label.svelte.d.ts +4 -4
  16. package/dist/Link.svelte +20 -14
  17. package/dist/Link.svelte.d.ts +0 -1
  18. package/dist/List.svelte +181 -126
  19. package/dist/List.svelte.d.ts +0 -1
  20. package/dist/ListItem.svelte +36 -21
  21. package/dist/ListItem.svelte.d.ts +0 -1
  22. package/dist/Menu.svelte +67 -45
  23. package/dist/Menu.svelte.d.ts +0 -1
  24. package/dist/MenuBar.svelte +96 -65
  25. package/dist/MenuBar.svelte.d.ts +0 -1
  26. package/dist/MenuButton.svelte +102 -62
  27. package/dist/MenuButton.svelte.d.ts +1 -1
  28. package/dist/MenuItem.svelte +332 -243
  29. package/dist/MenuItem.svelte.d.ts +3 -3
  30. package/dist/MenuSeparator.svelte +7 -7
  31. package/dist/MenuSeparator.svelte.d.ts +0 -1
  32. package/dist/Pagination.svelte +267 -0
  33. package/dist/Pagination.svelte.d.ts +4 -0
  34. package/dist/Pagination.types.d.ts +24 -0
  35. package/dist/Pagination.types.js +1 -0
  36. package/dist/Popover.svelte +114 -60
  37. package/dist/Popover.svelte.d.ts +1 -2
  38. package/dist/Portal.types.d.ts +1 -4
  39. package/dist/Progress.svelte +40 -15
  40. package/dist/Progress.svelte.d.ts +0 -1
  41. package/dist/Radio.svelte +37 -25
  42. package/dist/Radio.svelte.d.ts +0 -1
  43. package/dist/Select.svelte +191 -125
  44. package/dist/Select.svelte.d.ts +8 -2
  45. package/dist/Slider.svelte +120 -71
  46. package/dist/Slider.svelte.d.ts +0 -1
  47. package/dist/Switch.svelte +51 -20
  48. package/dist/Switch.svelte.d.ts +1 -1
  49. package/dist/Tab.svelte +39 -24
  50. package/dist/Tab.svelte.d.ts +0 -1
  51. package/dist/TabList.svelte +176 -125
  52. package/dist/TabList.svelte.d.ts +0 -1
  53. package/dist/TextArea.svelte +83 -41
  54. package/dist/TextArea.svelte.d.ts +2 -3
  55. package/dist/Tooltip.svelte +68 -36
  56. package/dist/Tree.svelte +52 -24
  57. package/dist/Tree.svelte.d.ts +0 -1
  58. package/dist/TreeChevron.svelte +24 -12
  59. package/dist/TreeChevron.svelte.d.ts +0 -1
  60. package/dist/TreeItem.svelte +292 -225
  61. package/dist/TreeItem.svelte.d.ts +1 -1
  62. package/dist/actions/extraClass.d.ts +1 -0
  63. package/dist/actions/extraClass.js +1 -0
  64. package/dist/idGenerator.d.ts +1 -0
  65. package/dist/idGenerator.js +1 -0
  66. package/dist/index.d.ts +3 -2
  67. package/dist/index.js +3 -2
  68. package/dist/mediaQueries/prefersColorSchemeDark.d.ts +0 -1
  69. package/dist/mediaQueries/prefersReducedMotion.d.ts +0 -1
  70. package/dist/mediaQueries/usingKeyboard.d.ts +0 -1
  71. package/package.json +21 -22
@@ -1,37 +1,52 @@
1
1
  <svelte:options runes={true} />
2
2
 
3
- <script lang="ts">import { getContext } from 'svelte';
4
- import { LIST_CONTEXT_KEY } from './List.constants';
5
- let { children, class: _class, disabled, value, ...rest } = $props();
6
- const listContext = getContext(LIST_CONTEXT_KEY);
7
- let selected = $state(listContext.selectedValue === value);
8
- // Using $derived would be preferred, but this helps avoid
9
- // updates to every list item when selectedValue changes.
10
- $effect(() => {
3
+ <script lang="ts">
4
+ import { getContext } from 'svelte';
5
+ import type { HTMLAttributes } from 'svelte/elements';
6
+ import { LIST_CONTEXT_KEY } from './List.constants';
7
+ import type { ListContext } from './List.types';
8
+
9
+ type Props = HTMLAttributes<HTMLDivElement> & {
10
+ disabled?: boolean | null;
11
+ value?: string;
12
+ };
13
+
14
+ let { children, class: _class, disabled, value, ...rest }: Props = $props();
15
+
16
+ const listContext = getContext<ListContext>(LIST_CONTEXT_KEY);
17
+
18
+ let selected = $state(listContext.selectedValue === value);
19
+
20
+ // Using $derived would be preferred, but this helps avoid
21
+ // updates to every list item when selectedValue changes.
22
+ $effect(() => {
11
23
  if (listContext.selectedValue === value && !selected) {
12
- selected = true;
24
+ selected = true;
25
+ } else if (listContext.selectedValue !== value && selected) {
26
+ selected = false;
13
27
  }
14
- else if (listContext.selectedValue !== value && selected) {
15
- selected = false;
16
- }
17
- });
18
- let itemRef;
19
- export const click = () => {
28
+ });
29
+
30
+ let itemRef: HTMLDivElement;
31
+
32
+ export const click = () => {
20
33
  itemRef?.click();
21
- };
22
- export const blur = () => {
34
+ };
35
+
36
+ export const blur = () => {
23
37
  itemRef?.blur();
24
- };
25
- export const focus = (options) => {
38
+ };
39
+
40
+ export const focus = (options?: FocusOptions) => {
26
41
  itemRef?.focus(options);
27
- };
42
+ };
28
43
  </script>
29
44
 
30
45
  <!-- svelte-ignore a11y_role_supports_aria_props -->
31
46
  <div
32
47
  aria-selected={selected}
33
48
  bind:this={itemRef}
34
- class={['sterling-list-item', _class].filter(Boolean).join(' ')}
49
+ class={['sterling-list-item', _class]}
35
50
  class:disabled={disabled || listContext.disabled}
36
51
  class:horizontal={listContext.horizontal}
37
52
  class:item-disabled={disabled}
@@ -1,4 +1,3 @@
1
- /// <reference types="svelte" />
2
1
  import type { HTMLAttributes } from 'svelte/elements';
3
2
  type Props = HTMLAttributes<HTMLDivElement> & {
4
3
  disabled?: boolean | null;
package/dist/Menu.svelte CHANGED
@@ -1,72 +1,94 @@
1
1
  <svelte:options runes={true} />
2
2
 
3
- <script lang="ts">import { getContext } from 'svelte';
4
- import { slide } from 'svelte/transition';
5
- import { MENU_ITEM_CONTEXT_KEY } from './MenuItem.constants';
6
- import { isElementEnabledMenuItem, isElementMenuItem } from './MenuItem.utils';
7
- import { prefersReducedMotion } from './mediaQueries/prefersReducedMotion';
8
- let { children, class: _class, ...rest } = $props();
9
- let menuRef;
10
- let menuItemsRef;
11
- const noSlide = (node, params) => {
3
+ <script lang="ts">
4
+ import { getContext } from 'svelte';
5
+ import type { HTMLAttributes } from 'svelte/elements';
6
+ import { slide, type SlideParams, type TransitionConfig } from 'svelte/transition';
7
+ import { MENU_ITEM_CONTEXT_KEY } from './MenuItem.constants';
8
+ import type { MenuItemContext } from './MenuItem.types';
9
+ import { isElementEnabledMenuItem, isElementMenuItem } from './MenuItem.utils';
10
+ import { prefersReducedMotion } from './mediaQueries/prefersReducedMotion';
11
+
12
+ type Props = HTMLAttributes<HTMLDivElement>;
13
+
14
+ let { children, class: _class, ...rest }: Props = $props();
15
+
16
+ let menuRef: HTMLDivElement;
17
+ let menuItemsRef: HTMLDivElement;
18
+
19
+ const noSlide = (node: Element, params?: SlideParams): TransitionConfig => {
12
20
  return { delay: 0, duration: 0 };
13
- };
14
- let slideMotion = $derived(!$prefersReducedMotion ? slide : noSlide);
15
- const { rootValue = undefined } = getContext(MENU_ITEM_CONTEXT_KEY);
16
- const isElementInThisMenu = (candidate) => {
21
+ };
22
+
23
+ let slideMotion = $derived(!$prefersReducedMotion ? slide : noSlide);
24
+
25
+ const { rootValue = undefined } = getContext<MenuItemContext>(MENU_ITEM_CONTEXT_KEY);
26
+
27
+ const isElementInThisMenu = (candidate: Element) => {
17
28
  return candidate && candidate.closest('[role="menu"]') === menuRef;
18
- };
19
- //#region focus
20
- export const focus = (options) => {
29
+ };
30
+
31
+ //#region focus
32
+ export const focus = (options?: FocusOptions) => {
21
33
  menuRef?.focus(options);
22
- };
23
- export const blur = () => {
34
+ };
35
+
36
+ export const blur = () => {
24
37
  menuRef?.blur();
25
- };
26
- export const focusFirstMenuItem = () => {
27
- let candidate = menuItemsRef?.firstElementChild;
38
+ };
39
+
40
+ export const focusFirstMenuItem = () => {
41
+ let candidate: Element | undefined | null = menuItemsRef?.firstElementChild;
28
42
  while (candidate && !isElementEnabledMenuItem(candidate)) {
29
- candidate = candidate.nextElementSibling;
43
+ candidate = candidate.nextElementSibling;
30
44
  }
31
- candidate?.focus({ preventScroll: true });
45
+
46
+ (candidate as HTMLElement)?.focus({ preventScroll: true });
32
47
  return !!candidate;
33
- };
34
- export const focusPreviousMenuItem = () => {
48
+ };
49
+
50
+ export const focusPreviousMenuItem = () => {
35
51
  let candidate = document.activeElement;
52
+
36
53
  if (candidate && isElementMenuItem(candidate) && isElementInThisMenu(candidate)) {
37
- candidate = menuItemsRef?.previousElementSibling;
38
- while (candidate && !isElementEnabledMenuItem(candidate)) {
39
- candidate = candidate.previousElementSibling;
40
- }
41
- candidate?.focus();
54
+ candidate = menuItemsRef?.previousElementSibling;
55
+ while (candidate && !isElementEnabledMenuItem(candidate)) {
56
+ candidate = candidate.previousElementSibling;
57
+ }
58
+ (candidate as HTMLElement)?.focus();
42
59
  }
43
60
  return !!candidate;
44
- };
45
- export const focusNextMenuItem = () => {
61
+ };
62
+
63
+ export const focusNextMenuItem = () => {
46
64
  let candidate = document.activeElement;
65
+
47
66
  if (candidate && isElementMenuItem(candidate) && isElementInThisMenu(candidate)) {
48
- candidate = menuItemsRef?.nextElementSibling;
49
- while (candidate && !isElementEnabledMenuItem(candidate)) {
50
- candidate = candidate.nextElementSibling;
51
- }
52
- candidate?.focus();
67
+ candidate = menuItemsRef?.nextElementSibling;
68
+ while (candidate && !isElementEnabledMenuItem(candidate)) {
69
+ candidate = candidate.nextElementSibling;
70
+ }
71
+ (candidate as HTMLElement)?.focus();
53
72
  }
54
73
  return !!candidate;
55
- };
56
- export const focusLastMenuItem = () => {
57
- let candidate = menuItemsRef?.lastElementChild;
74
+ };
75
+
76
+ export const focusLastMenuItem = () => {
77
+ let candidate: Element | undefined | null = menuItemsRef?.lastElementChild;
58
78
  while (candidate && !isElementEnabledMenuItem(candidate)) {
59
- candidate = candidate.previousElementSibling;
79
+ candidate = candidate.previousElementSibling;
60
80
  }
61
- candidate?.focus({ preventScroll: true });
81
+
82
+ (candidate as HTMLElement)?.focus({ preventScroll: true });
62
83
  return !!candidate;
63
- };
64
- //#endregion
84
+ };
85
+
86
+ //#endregion
65
87
  </script>
66
88
 
67
89
  <div
68
90
  bind:this={menuRef}
69
- class={['sterling-menu', _class].filter(Boolean).join(' ')}
91
+ class={['sterling-menu', _class]}
70
92
  role="menu"
71
93
  class:open
72
94
  data-root-value={rootValue}
@@ -1,4 +1,3 @@
1
- /// <reference types="svelte" />
2
1
  import type { HTMLAttributes } from 'svelte/elements';
3
2
  type Props = HTMLAttributes<HTMLDivElement>;
4
3
  declare const Menu: import("svelte").Component<Props, {
@@ -1,113 +1,144 @@
1
1
  <svelte:options runes={true} />
2
2
 
3
- <script lang="ts">import { setContext } from 'svelte';
4
- import { clickOutside } from './actions/clickOutside';
5
- import { idGenerator } from './idGenerator';
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
- let { class: _class, children, onClose, onOpen, onSelect, ...rest } = $props();
10
- const rootValue = idGenerator.nextId('MenuBar');
11
- let openValues = $state([]);
12
- let prevOpenValue = $state();
13
- let menuBarRef;
14
- $effect(() => {
3
+ <script lang="ts">
4
+ import { setContext } from 'svelte';
5
+ import type { HTMLAttributes } from 'svelte/elements';
6
+ import { clickOutside } from './actions/clickOutside';
7
+ import { MENU_BAR_CONTEXT_KEY } from './MenuBar.constants';
8
+ import type { MenuBarContext } from './MenuBar.types';
9
+ import { MENU_ITEM_CONTEXT_KEY } from './MenuItem.constants';
10
+ import type { MenuItemContext } from './MenuItem.types';
11
+ import { isElementEnabledMenuItem } from './MenuItem.utils';
12
+
13
+ const uuid = $props.id();
14
+
15
+ type Props = HTMLAttributes<HTMLDivElement> & {
16
+ onClose?: (value: string) => void;
17
+ onOpen?: (value: string) => void;
18
+ onSelect?: (value: string) => void;
19
+ };
20
+
21
+ let { class: _class, children, onClose, onOpen, onSelect, ...rest }: Props = $props();
22
+
23
+ const rootValue = `MenuBar-${uuid}`;
24
+ let openValues: string[] = $state([]);
25
+ let prevOpenValue: string | undefined = $state();
26
+
27
+ let menuBarRef: HTMLDivElement;
28
+
29
+ $effect(() => {
15
30
  prevOpenValue = openValues[0];
16
- });
17
- // Restore focus to the last open menu bar item when it closes
18
- $effect(() => {
31
+ });
32
+
33
+ // Restore focus to the last open menu bar item when it closes
34
+ $effect(() => {
19
35
  if (openValues.length === 0 && prevOpenValue !== undefined) {
20
- const candidate = menuBarRef.querySelector(`[data-value="${prevOpenValue}"]`);
21
- candidate?.focus();
22
- prevOpenValue = undefined;
36
+ const candidate = menuBarRef.querySelector(`[data-value="${prevOpenValue}"]`);
37
+ (candidate as HTMLElement)?.focus();
38
+ prevOpenValue = undefined;
23
39
  }
24
- });
25
- export const blur = () => {
40
+ });
41
+
42
+ export const blur = () => {
26
43
  menuBarRef?.blur();
27
- };
28
- export const focus = (options) => {
44
+ };
45
+
46
+ export const focus = (options?: FocusOptions) => {
29
47
  menuBarRef?.focus(options);
30
- };
31
- const getOpenMenuBarItem = () => {
48
+ };
49
+
50
+ const getOpenMenuBarItem = () => {
32
51
  const value = openValues[0];
33
52
  if (value) {
34
- return menuBarRef.querySelector(`[data-value="${value}"]`);
53
+ return menuBarRef.querySelector(`[data-value="${value}"]`);
35
54
  }
36
55
  return null;
37
- };
38
- const openPreviousMenuBarItem = () => {
56
+ };
57
+
58
+ const openPreviousMenuBarItem = () => {
39
59
  const openItem = getOpenMenuBarItem() || menuBarRef.firstElementChild;
40
60
  let candidate = openItem?.previousElementSibling || menuBarRef.lastElementChild;
61
+
41
62
  while (candidate && !isElementEnabledMenuItem(candidate)) {
42
- candidate = candidate.previousElementSibling || menuBarRef.lastElementChild;
43
- if (candidate === openItem) {
44
- return false;
45
- }
63
+ candidate = candidate.previousElementSibling || menuBarRef.lastElementChild;
64
+ if (candidate === openItem) {
65
+ return false;
66
+ }
46
67
  }
68
+
47
69
  if (!candidate) {
48
- candidate = menuBarRef.lastElementChild;
49
- candidate = candidate && isElementEnabledMenuItem(candidate) ? candidate : null;
70
+ candidate = menuBarRef.lastElementChild;
71
+ candidate = candidate && isElementEnabledMenuItem(candidate) ? candidate : null;
50
72
  }
51
- candidate?.click();
73
+
74
+ (candidate as HTMLElement)?.click();
52
75
  return !!candidate;
53
- };
54
- const openNextMenuBarItem = () => {
76
+ };
77
+
78
+ const openNextMenuBarItem = () => {
55
79
  const openItem = getOpenMenuBarItem() || menuBarRef.lastElementChild;
56
80
  let candidate = openItem?.nextElementSibling || menuBarRef.firstElementChild;
81
+
57
82
  while (candidate && !isElementEnabledMenuItem(candidate)) {
58
- candidate = candidate.nextElementSibling || menuBarRef.firstElementChild;
59
- if (candidate === openItem) {
60
- return false;
61
- }
83
+ candidate = candidate.nextElementSibling || menuBarRef.firstElementChild;
84
+ if (candidate === openItem) {
85
+ return false;
86
+ }
62
87
  }
88
+
63
89
  if (!candidate) {
64
- candidate = menuBarRef.firstElementChild;
65
- candidate = candidate && isElementEnabledMenuItem(candidate) ? candidate : null;
90
+ candidate = menuBarRef.firstElementChild;
91
+ candidate = candidate && isElementEnabledMenuItem(candidate) ? candidate : null;
66
92
  }
67
- candidate?.click();
93
+
94
+ (candidate as HTMLElement)?.click();
68
95
  return !!candidate;
69
- };
70
- const closeAllMenus = () => {
96
+ };
97
+
98
+ const closeAllMenus = () => {
71
99
  openValues = [];
72
- };
73
- const onClickOutside = (event) => {
74
- let element = event.target;
100
+ };
101
+
102
+ const onClickOutside = (event: MouseEvent) => {
103
+ let element: HTMLElement | null = event.target as HTMLElement;
75
104
  while (element) {
76
- if (element.getAttribute('data-root-value') === rootValue) {
77
- return;
78
- }
79
- element = element.parentElement;
105
+ if (element.getAttribute('data-root-value') === rootValue) {
106
+ return;
107
+ }
108
+ element = element.parentElement;
80
109
  }
81
110
  closeAllMenus?.();
82
- };
83
- let menuBarContext = { openPreviousMenuBarItem, openNextMenuBarItem };
84
- setContext(MENU_BAR_CONTEXT_KEY, menuBarContext);
85
- let menuItemContext = {
111
+ };
112
+
113
+ let menuBarContext: MenuBarContext = { openPreviousMenuBarItem, openNextMenuBarItem };
114
+
115
+ setContext<MenuBarContext>(MENU_BAR_CONTEXT_KEY, menuBarContext);
116
+
117
+ let menuItemContext: MenuItemContext = {
86
118
  isMenuBarItem: true,
87
119
  depth: 0,
88
120
  get openValues() {
89
- return openValues;
121
+ return openValues;
90
122
  },
91
- set openValues(value) {
92
- openValues = value;
123
+ set openValues(value: string[]) {
124
+ openValues = value;
93
125
  },
94
126
  rootValue,
95
127
  onClose,
96
128
  onOpen,
97
129
  onSelect
98
- };
99
- setContext(MENU_ITEM_CONTEXT_KEY, menuItemContext);
130
+ };
131
+
132
+ setContext<MenuItemContext>(MENU_ITEM_CONTEXT_KEY, menuItemContext);
100
133
  </script>
101
134
 
102
135
  <div
103
136
  bind:this={menuBarRef}
104
- class={['sterling-menu-bar', _class].filter(Boolean).join(' ')}
137
+ class={['sterling-menu-bar', _class]}
105
138
  role="menubar"
106
139
  tabindex="-1"
107
140
  {...rest}
108
141
  use:clickOutside={{ onclickoutside: onClickOutside }}
109
142
  >
110
- {#if children}
111
- {@render children()}
112
- {/if}
143
+ {@render children?.()}
113
144
  </div>
@@ -1,4 +1,3 @@
1
- /// <reference types="svelte" />
2
1
  import type { HTMLAttributes } from 'svelte/elements';
3
2
  type Props = HTMLAttributes<HTMLDivElement> & {
4
3
  onClose?: (value: string) => void;
@@ -1,92 +1,132 @@
1
1
  <svelte:options runes={true} />
2
2
 
3
- <script lang="ts">import { setContext, tick } from 'svelte';
4
- import Button from './Button.svelte';
5
- import Menu from './Menu.svelte';
6
- import { MENU_ITEM_CONTEXT_KEY } from './MenuItem.constants';
7
- import { idGenerator } from './idGenerator';
8
- import Popover from './Popover.svelte';
9
- import { clickOutside } from './actions/clickOutside';
10
- let { children, class: _class, items, menuClass, open = $bindable(false), onClose, onOpen, onSelect, popoverPlacement = 'bottom-start', value, ...rest } = $props();
11
- const instanceId = idGenerator.nextId('MenuButton');
12
- let buttonRef;
13
- let openValues = $state([]);
14
- let menuRef;
15
- let menuId = $derived(`${value}-menu-${instanceId}`);
16
- let prevOpen = $state(open);
17
- let reference = $state();
18
- export const click = () => {
3
+ <script lang="ts">
4
+ import { setContext, tick, type Snippet } from 'svelte';
5
+ import type { HTMLButtonAttributes } from 'svelte/elements';
6
+ import { clickOutside } from './actions/clickOutside';
7
+ import Button from './Button.svelte';
8
+ import Menu from './Menu.svelte';
9
+ import { MENU_ITEM_CONTEXT_KEY } from './MenuItem.constants';
10
+ import type { MenuItemContext } from './MenuItem.types';
11
+ import Popover from './Popover.svelte';
12
+ import type { PopoverPlacement } from './Popover.types';
13
+
14
+ const uuid = $props.id();
15
+
16
+ type Props = HTMLButtonAttributes & {
17
+ items: Snippet;
18
+ menuClass?: string;
19
+ onOpen?: (value: string) => void;
20
+ onClose?: (value: string) => void;
21
+ onSelect?: (value: string) => void;
22
+ open?: boolean | null | undefined;
23
+ popoverPlacement?: PopoverPlacement;
24
+ value?: string;
25
+ };
26
+
27
+ let {
28
+ children,
29
+ class: _class,
30
+ items,
31
+ menuClass,
32
+ open = $bindable(false),
33
+ onClose,
34
+ onOpen,
35
+ onSelect,
36
+ popoverPlacement = 'bottom-start',
37
+ value,
38
+ ...rest
39
+ }: Props = $props();
40
+
41
+ const instanceId = `MenuButton-${uuid}`;
42
+
43
+ let buttonRef: Button;
44
+ let openValues: string[] = $state([]);
45
+ let menuRef: Menu;
46
+ let menuId = $derived(`${value}-menu-${instanceId}`);
47
+ let prevOpen = $state(open);
48
+ let reference: HTMLDivElement | undefined = $state();
49
+
50
+ export const click = () => {
19
51
  buttonRef?.click();
20
- };
21
- export const blur = () => {
52
+ };
53
+
54
+ export const blur = () => {
22
55
  buttonRef?.blur();
23
- };
24
- export const focus = (options) => {
56
+ };
57
+
58
+ export const focus = (options?: FocusOptions) => {
25
59
  buttonRef?.focus(options);
26
- };
27
- // update open based on openValues
28
- $effect(() => {
60
+ };
61
+
62
+ // update open based on openValues
63
+ $effect(() => {
29
64
  open = openValues.length > 0;
30
- });
31
- // update openValues based on open
32
- $effect(() => {
65
+ });
66
+
67
+ // update openValues based on open
68
+ $effect(() => {
33
69
  if (open) {
34
- openValues = openValues.length > 0 ? openValues : ['menu-button'];
35
- }
36
- else {
37
- openValues = openValues.length === 0 ? openValues : [];
70
+ openValues = openValues.length > 0 ? openValues : ['menu-button'];
71
+ } else {
72
+ openValues = openValues.length === 0 ? openValues : [];
38
73
  }
39
- });
40
- // focus when closing
41
- $effect(() => {
74
+ });
75
+
76
+ // focus when closing
77
+ $effect(() => {
42
78
  if (!open && open !== prevOpen) {
43
- focus();
79
+ focus();
44
80
  }
45
81
  prevOpen = open;
46
- });
47
- const onClick = async () => {
82
+ });
83
+
84
+ const onClick = async () => {
48
85
  if (!open) {
49
- openValues = ['menu-button'];
50
- open = true;
51
- await tick();
52
- menuRef?.focusFirstMenuItem();
86
+ openValues = ['menu-button'];
87
+ open = true;
88
+ await tick();
89
+ menuRef?.focusFirstMenuItem();
90
+ } else {
91
+ open = false;
92
+ openValues = [];
53
93
  }
54
- else {
55
- open = false;
56
- openValues = [];
57
- }
58
- };
59
- const closeAllMenus = () => {
94
+ };
95
+
96
+ const closeAllMenus = () => {
60
97
  openValues = [];
61
98
  open = false;
62
- };
63
- const onClickOutside = (event) => {
64
- let element = event.target;
99
+ };
100
+
101
+ const onClickOutside = (event: MouseEvent) => {
102
+ let element: HTMLElement | null = event.target as HTMLElement;
65
103
  while (element) {
66
- if (element.getAttribute('data-root-value') === value) {
67
- return;
68
- }
69
- element = element.parentElement;
104
+ if (element.getAttribute('data-root-value') === value) {
105
+ return;
106
+ }
107
+ element = element.parentElement;
70
108
  }
71
109
  closeAllMenus?.();
72
- };
73
- // ----- Context ----- //
74
- setContext(MENU_ITEM_CONTEXT_KEY, {
110
+ };
111
+
112
+ // ----- Context ----- //
113
+
114
+ setContext<MenuItemContext>(MENU_ITEM_CONTEXT_KEY, {
75
115
  depth: 1,
76
116
  get openValues() {
77
- return openValues;
117
+ return openValues;
78
118
  },
79
- set openValues(value) {
80
- openValues = value;
119
+ set openValues(value: string[]) {
120
+ openValues = value;
81
121
  },
82
122
  rootValue: value,
83
123
  closeContainingMenu: () => {
84
- open = false;
124
+ open = false;
85
125
  },
86
126
  onOpen,
87
127
  onClose,
88
128
  onSelect
89
- });
129
+ });
90
130
  </script>
91
131
 
92
132
  <Button
@@ -95,7 +135,7 @@ setContext(MENU_ITEM_CONTEXT_KEY, {
95
135
  aria-expanded={!!open}
96
136
  aria-haspopup={!!children}
97
137
  aria-owns={menuId}
98
- class={['sterling-menu-button', _class].filter(Boolean).join(' ')}
138
+ class={['sterling-menu-button', _class]}
99
139
  data-value={value}
100
140
  data-root-value={value}
101
141
  {...rest}
@@ -1,5 +1,5 @@
1
- import type { HTMLButtonAttributes } from 'svelte/elements';
2
1
  import { type Snippet } from 'svelte';
2
+ import type { HTMLButtonAttributes } from 'svelte/elements';
3
3
  import type { PopoverPlacement } from './Popover.types';
4
4
  type Props = HTMLButtonAttributes & {
5
5
  items: Snippet;