@geoffcox/sterling-svelte 1.0.11 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (306) hide show
  1. package/.DS_Store +0 -0
  2. package/Button.svelte +25 -0
  3. package/Button.svelte.d.ts +9 -0
  4. package/{dist/Callout.svelte → Callout.svelte} +61 -78
  5. package/Callout.svelte.d.ts +15 -0
  6. package/Callout.types.d.ts +11 -0
  7. package/Checkbox.svelte +43 -0
  8. package/Checkbox.svelte.d.ts +10 -0
  9. package/{dist/Dialog.svelte → Dialog.svelte} +30 -38
  10. package/Dialog.svelte.d.ts +14 -0
  11. package/Dropdown.svelte +109 -0
  12. package/Dropdown.svelte.d.ts +18 -0
  13. package/Input.svelte +55 -0
  14. package/Input.svelte.d.ts +12 -0
  15. package/Label.svelte +91 -0
  16. package/Label.svelte.d.ts +17 -0
  17. package/Link.svelte +25 -0
  18. package/Link.svelte.d.ts +12 -0
  19. package/{dist/List.svelte → List.svelte} +35 -85
  20. package/List.svelte.d.ts +20 -0
  21. package/List.types.d.ts +5 -0
  22. package/ListItem.svelte +49 -0
  23. package/ListItem.svelte.d.ts +13 -0
  24. package/{dist/Menu.svelte → Menu.svelte} +18 -45
  25. package/Menu.svelte.d.ts +13 -0
  26. package/{dist/MenuBar.svelte → MenuBar.svelte} +36 -78
  27. package/MenuBar.svelte.d.ts +13 -0
  28. package/MenuButton.svelte +116 -0
  29. package/MenuButton.svelte.d.ts +20 -0
  30. package/{dist/MenuItem.svelte → MenuItem.svelte} +107 -151
  31. package/MenuItem.svelte.d.ts +22 -0
  32. package/{dist/MenuItem.types.d.ts → MenuItem.types.d.ts} +1 -9
  33. package/MenuSeparator.svelte +11 -0
  34. package/MenuSeparator.svelte.d.ts +6 -0
  35. package/{dist/Popover.svelte → Popover.svelte} +45 -64
  36. package/Popover.svelte.d.ts +15 -0
  37. package/Progress.constants.d.ts +1 -0
  38. package/Progress.constants.js +1 -0
  39. package/Progress.svelte +36 -0
  40. package/Progress.svelte.d.ts +12 -0
  41. package/Progress.types.d.ts +4 -0
  42. package/Radio.svelte +53 -0
  43. package/Radio.svelte.d.ts +13 -0
  44. package/{dist/Select.svelte → Select.svelte} +55 -94
  45. package/Select.svelte.d.ts +20 -0
  46. package/Slider.svelte +133 -0
  47. package/Slider.svelte.d.ts +19 -0
  48. package/Switch.svelte +61 -0
  49. package/Switch.svelte.d.ts +21 -0
  50. package/Tab.svelte +51 -0
  51. package/Tab.svelte.d.ts +12 -0
  52. package/{dist/TabList.svelte → TabList.svelte} +50 -76
  53. package/TabList.svelte.d.ts +18 -0
  54. package/TabList.types.d.ts +5 -0
  55. package/{dist/TextArea.svelte → TextArea.svelte} +17 -59
  56. package/TextArea.svelte.d.ts +19 -0
  57. package/Tooltip.svelte +63 -0
  58. package/Tooltip.svelte.d.ts +10 -0
  59. package/Tree.svelte +53 -0
  60. package/Tree.svelte.d.ts +15 -0
  61. package/Tree.types.d.ts +5 -0
  62. package/TreeChevron.svelte +27 -0
  63. package/TreeChevron.svelte.d.ts +9 -0
  64. package/{dist/TreeItem.svelte → TreeItem.svelte} +105 -159
  65. package/TreeItem.svelte.d.ts +22 -0
  66. package/TreeItem.types.d.ts +4 -0
  67. package/{dist/actions → actions}/clickOutside.d.ts +1 -0
  68. package/{dist/actions → actions}/clickOutside.js +1 -0
  69. package/actions/extraClass.d.ts +8 -0
  70. package/actions/extraClass.js +14 -0
  71. package/{dist/index.d.ts → index.d.ts} +4 -12
  72. package/{dist/index.js → index.js} +3 -9
  73. package/package.json +26 -28
  74. package/README.md +0 -18
  75. package/dist/Button.constants.d.ts +0 -2
  76. package/dist/Button.constants.js +0 -2
  77. package/dist/Button.svelte +0 -63
  78. package/dist/Button.svelte.d.ts +0 -65
  79. package/dist/Button.types.d.ts +0 -6
  80. package/dist/Callout.svelte.d.ts +0 -56
  81. package/dist/Checkbox.svelte +0 -79
  82. package/dist/Checkbox.svelte.d.ts +0 -63
  83. package/dist/ColorPicker.constants.d.ts +0 -1
  84. package/dist/ColorPicker.constants.js +0 -1
  85. package/dist/ColorPicker.svelte +0 -287
  86. package/dist/ColorPicker.svelte.d.ts +0 -52
  87. package/dist/ColorPicker.types.d.ts +0 -4
  88. package/dist/ColorPicker.types.js +0 -1
  89. package/dist/Dialog.svelte.d.ts +0 -37
  90. package/dist/Dropdown.svelte +0 -138
  91. package/dist/Dropdown.svelte.d.ts +0 -77
  92. package/dist/HexColorSliders.svelte +0 -103
  93. package/dist/HexColorSliders.svelte.d.ts +0 -51
  94. package/dist/HslColorSliders.svelte +0 -128
  95. package/dist/HslColorSliders.svelte.d.ts +0 -51
  96. package/dist/Input.svelte +0 -89
  97. package/dist/Input.svelte.d.ts +0 -74
  98. package/dist/Label.svelte +0 -197
  99. package/dist/Label.svelte.d.ts +0 -82
  100. package/dist/Label.types.d.ts +0 -6
  101. package/dist/Label.types.js +0 -1
  102. package/dist/Link.svelte +0 -57
  103. package/dist/Link.svelte.d.ts +0 -65
  104. package/dist/List.svelte.d.ts +0 -75
  105. package/dist/List.types.d.ts +0 -13
  106. package/dist/ListItem.svelte +0 -78
  107. package/dist/ListItem.svelte.d.ts +0 -67
  108. package/dist/Menu.svelte.d.ts +0 -63
  109. package/dist/MenuBar.svelte.d.ts +0 -58
  110. package/dist/MenuButton.svelte +0 -145
  111. package/dist/MenuButton.svelte.d.ts +0 -71
  112. package/dist/MenuItem.svelte.d.ts +0 -83
  113. package/dist/MenuItemDisplay.svelte +0 -32
  114. package/dist/MenuItemDisplay.svelte.d.ts +0 -39
  115. package/dist/MenuSeparator.svelte +0 -9
  116. package/dist/MenuSeparator.svelte.d.ts +0 -20
  117. package/dist/Popover.svelte.d.ts +0 -59
  118. package/dist/Progress.constants.d.ts +0 -1
  119. package/dist/Progress.constants.js +0 -1
  120. package/dist/Progress.svelte +0 -83
  121. package/dist/Progress.svelte.d.ts +0 -61
  122. package/dist/Progress.types.d.ts +0 -4
  123. package/dist/Radio.svelte +0 -126
  124. package/dist/Radio.svelte.d.ts +0 -65
  125. package/dist/RgbColorSliders.svelte +0 -93
  126. package/dist/RgbColorSliders.svelte.d.ts +0 -24
  127. package/dist/Select.svelte.d.ts +0 -83
  128. package/dist/Slider.svelte +0 -190
  129. package/dist/Slider.svelte.d.ts +0 -66
  130. package/dist/Switch.svelte +0 -110
  131. package/dist/Switch.svelte.d.ts +0 -74
  132. package/dist/Tab.svelte +0 -94
  133. package/dist/Tab.svelte.d.ts +0 -71
  134. package/dist/TabList.svelte.d.ts +0 -70
  135. package/dist/TabList.types.d.ts +0 -13
  136. package/dist/TextArea.svelte.d.ts +0 -69
  137. package/dist/Tooltip.svelte +0 -106
  138. package/dist/Tooltip.svelte.d.ts +0 -70
  139. package/dist/Tree.svelte +0 -104
  140. package/dist/Tree.svelte.d.ts +0 -67
  141. package/dist/Tree.types.d.ts +0 -13
  142. package/dist/TreeChevron.svelte +0 -66
  143. package/dist/TreeChevron.svelte.d.ts +0 -53
  144. package/dist/TreeItem.svelte.d.ts +0 -101
  145. package/dist/TreeItem.types.d.ts +0 -14
  146. package/dist/TreeItemDisplay.svelte +0 -74
  147. package/dist/TreeItemDisplay.svelte.d.ts +0 -73
  148. package/dist/css/Button.base.css +0 -74
  149. package/dist/css/Button.colorful.css +0 -17
  150. package/dist/css/Button.css +0 -7
  151. package/dist/css/Button.secondary.colorful.css +0 -15
  152. package/dist/css/Button.secondary.css +0 -11
  153. package/dist/css/Button.shapes.css +0 -14
  154. package/dist/css/Button.tool.colorful.css +0 -13
  155. package/dist/css/Button.tool.css +0 -18
  156. package/dist/css/Callout.base.css +0 -43
  157. package/dist/css/Callout.colorful.css +0 -5
  158. package/dist/css/Callout.css +0 -2
  159. package/dist/css/Checkbox.base.css +0 -145
  160. package/dist/css/Checkbox.colorful.css +0 -17
  161. package/dist/css/Checkbox.css +0 -2
  162. package/dist/css/ColorPicker.base.css +0 -23
  163. package/dist/css/ColorPicker.css +0 -1
  164. package/dist/css/Dialog.base.css +0 -114
  165. package/dist/css/Dialog.css +0 -1
  166. package/dist/css/Dropdown.base.css +0 -147
  167. package/dist/css/Dropdown.colorful.css +0 -23
  168. package/dist/css/Dropdown.css +0 -2
  169. package/dist/css/HexColorSliders.base.css +0 -106
  170. package/dist/css/HexColorSliders.css +0 -1
  171. package/dist/css/HslColorSliders.base.css +0 -124
  172. package/dist/css/HslColorSliders.css +0 -1
  173. package/dist/css/Input.base.css +0 -103
  174. package/dist/css/Input.colorful.css +0 -22
  175. package/dist/css/Input.composed.css +0 -8
  176. package/dist/css/Input.css +0 -3
  177. package/dist/css/Label.base.css +0 -118
  178. package/dist/css/Label.boxed.colorful.css +0 -21
  179. package/dist/css/Label.boxed.css +0 -31
  180. package/dist/css/Label.colorful.css +0 -3
  181. package/dist/css/Label.css +0 -4
  182. package/dist/css/Link.base.css +0 -54
  183. package/dist/css/Link.colorful.css +0 -15
  184. package/dist/css/Link.css +0 -10
  185. package/dist/css/Link.ghost.colorful.css +0 -7
  186. package/dist/css/Link.ghost.css +0 -11
  187. package/dist/css/Link.text-underline.css +0 -8
  188. package/dist/css/Link.text-underline.ghost.css +0 -13
  189. package/dist/css/Link.undecorated.colorful.css +0 -8
  190. package/dist/css/Link.undecorated.css +0 -8
  191. package/dist/css/Link.undecorated.ghost.css +0 -8
  192. package/dist/css/Link.undecorated.underline.css +0 -8
  193. package/dist/css/List.base.css +0 -98
  194. package/dist/css/List.css +0 -1
  195. package/dist/css/ListItem.base.css +0 -59
  196. package/dist/css/ListItem.css +0 -1
  197. package/dist/css/Menu.base.css +0 -21
  198. package/dist/css/Menu.css +0 -1
  199. package/dist/css/MenuBar.base.css +0 -9
  200. package/dist/css/MenuBar.css +0 -1
  201. package/dist/css/MenuButton.base.css +0 -13
  202. package/dist/css/MenuButton.css +0 -1
  203. package/dist/css/MenuItem.base.css +0 -48
  204. package/dist/css/MenuItem.css +0 -1
  205. package/dist/css/MenuItemDisplay.base.css +0 -108
  206. package/dist/css/MenuItemDisplay.css +0 -1
  207. package/dist/css/MenuSeparator.base.css +0 -5
  208. package/dist/css/MenuSeparator.css +0 -1
  209. package/dist/css/Popover.css +0 -21
  210. package/dist/css/Progress.base.css +0 -99
  211. package/dist/css/Progress.css +0 -1
  212. package/dist/css/Radio.base.css +0 -135
  213. package/dist/css/Radio.colorful.css +0 -18
  214. package/dist/css/Radio.css +0 -2
  215. package/dist/css/RgbColorSliders.base.css +0 -94
  216. package/dist/css/RgbColorSliders.css +0 -1
  217. package/dist/css/Select.base.css +0 -127
  218. package/dist/css/Select.colorful.css +0 -24
  219. package/dist/css/Select.composed.css +0 -12
  220. package/dist/css/Select.css +0 -3
  221. package/dist/css/Slider.base.css +0 -192
  222. package/dist/css/Slider.colorful.css +0 -11
  223. package/dist/css/Slider.composed.css +0 -8
  224. package/dist/css/Slider.css +0 -3
  225. package/dist/css/Switch.base.css +0 -206
  226. package/dist/css/Switch.colorful.css +0 -45
  227. package/dist/css/Switch.css +0 -2
  228. package/dist/css/Tab.base.css +0 -132
  229. package/dist/css/Tab.colorful.css +0 -13
  230. package/dist/css/Tab.css +0 -2
  231. package/dist/css/TabList.base.css +0 -34
  232. package/dist/css/TabList.css +0 -1
  233. package/dist/css/TextArea.base.css +0 -85
  234. package/dist/css/TextArea.colorful.css +0 -17
  235. package/dist/css/TextArea.composed.css +0 -8
  236. package/dist/css/TextArea.css +0 -3
  237. package/dist/css/Tooltip.base.css +0 -6
  238. package/dist/css/Tooltip.css +0 -1
  239. package/dist/css/Tree.base.css +0 -74
  240. package/dist/css/Tree.composed.css +0 -8
  241. package/dist/css/Tree.css +0 -2
  242. package/dist/css/TreeChevron.base.css +0 -86
  243. package/dist/css/TreeChevron.css +0 -1
  244. package/dist/css/TreeItem.base.css +0 -3
  245. package/dist/css/TreeItem.css +0 -1
  246. package/dist/css/TreeItemDisplay.base.css +0 -74
  247. package/dist/css/TreeItemDisplay.colorful.css +0 -9
  248. package/dist/css/TreeItemDisplay.css +0 -1
  249. package/dist/css/dark-mode.css +0 -134
  250. package/dist/css/light-mode.css +0 -134
  251. package/dist/css/sterling.css +0 -37
  252. package/dist/package.json +0 -108
  253. /package/{dist/@types → @types}/clickOutside.d.ts +0 -0
  254. /package/{dist/Button.types.js → Callout.types.js} +0 -0
  255. /package/{dist/Label.constants.d.ts → Label.constants.d.ts} +0 -0
  256. /package/{dist/Label.constants.js → Label.constants.js} +0 -0
  257. /package/{dist/List.constants.d.ts → List.constants.d.ts} +0 -0
  258. /package/{dist/List.constants.js → List.constants.js} +0 -0
  259. /package/{dist/List.types.js → List.types.js} +0 -0
  260. /package/{dist/MenuBar.constants.d.ts → MenuBar.constants.d.ts} +0 -0
  261. /package/{dist/MenuBar.constants.js → MenuBar.constants.js} +0 -0
  262. /package/{dist/MenuBar.types.d.ts → MenuBar.types.d.ts} +0 -0
  263. /package/{dist/MenuBar.types.js → MenuBar.types.js} +0 -0
  264. /package/{dist/MenuItem.constants.d.ts → MenuItem.constants.d.ts} +0 -0
  265. /package/{dist/MenuItem.constants.js → MenuItem.constants.js} +0 -0
  266. /package/{dist/MenuItem.types.js → MenuItem.types.js} +0 -0
  267. /package/{dist/MenuItem.utils.d.ts → MenuItem.utils.d.ts} +0 -0
  268. /package/{dist/MenuItem.utils.js → MenuItem.utils.js} +0 -0
  269. /package/{dist/Popover.constants.d.ts → Popover.constants.d.ts} +0 -0
  270. /package/{dist/Popover.constants.js → Popover.constants.js} +0 -0
  271. /package/{dist/Popover.types.d.ts → Popover.types.d.ts} +0 -0
  272. /package/{dist/Popover.types.js → Popover.types.js} +0 -0
  273. /package/{dist/Portal.constants.d.ts → Portal.constants.d.ts} +0 -0
  274. /package/{dist/Portal.constants.js → Portal.constants.js} +0 -0
  275. /package/{dist/Portal.types.d.ts → Portal.types.d.ts} +0 -0
  276. /package/{dist/Portal.types.js → Portal.types.js} +0 -0
  277. /package/{dist/Progress.types.js → Progress.types.js} +0 -0
  278. /package/{dist/TabList.constants.d.ts → TabList.constants.d.ts} +0 -0
  279. /package/{dist/TabList.constants.js → TabList.constants.js} +0 -0
  280. /package/{dist/TabList.types.js → TabList.types.js} +0 -0
  281. /package/{dist/TextArea.constants.d.ts → TextArea.constants.d.ts} +0 -0
  282. /package/{dist/TextArea.constants.js → TextArea.constants.js} +0 -0
  283. /package/{dist/TextArea.types.d.ts → TextArea.types.d.ts} +0 -0
  284. /package/{dist/TextArea.types.js → TextArea.types.js} +0 -0
  285. /package/{dist/Tree.constants.d.ts → Tree.constants.d.ts} +0 -0
  286. /package/{dist/Tree.constants.js → Tree.constants.js} +0 -0
  287. /package/{dist/Tree.types.js → Tree.types.js} +0 -0
  288. /package/{dist/TreeItem.constants.d.ts → TreeItem.constants.d.ts} +0 -0
  289. /package/{dist/TreeItem.constants.js → TreeItem.constants.js} +0 -0
  290. /package/{dist/TreeItem.types.js → TreeItem.types.js} +0 -0
  291. /package/{dist/actions → actions}/applyLightDarkMode.d.ts +0 -0
  292. /package/{dist/actions → actions}/applyLightDarkMode.js +0 -0
  293. /package/{dist/actions → actions}/forwardEvents.d.ts +0 -0
  294. /package/{dist/actions → actions}/forwardEvents.js +0 -0
  295. /package/{dist/actions → actions}/portal.d.ts +0 -0
  296. /package/{dist/actions → actions}/portal.js +0 -0
  297. /package/{dist/actions → actions}/trapKeyboardFocus.d.ts +0 -0
  298. /package/{dist/actions → actions}/trapKeyboardFocus.js +0 -0
  299. /package/{dist/idGenerator.d.ts → idGenerator.d.ts} +0 -0
  300. /package/{dist/idGenerator.js → idGenerator.js} +0 -0
  301. /package/{dist/mediaQueries → mediaQueries}/prefersColorSchemeDark.d.ts +0 -0
  302. /package/{dist/mediaQueries → mediaQueries}/prefersColorSchemeDark.js +0 -0
  303. /package/{dist/mediaQueries → mediaQueries}/prefersReducedMotion.d.ts +0 -0
  304. /package/{dist/mediaQueries → mediaQueries}/prefersReducedMotion.js +0 -0
  305. /package/{dist/mediaQueries → mediaQueries}/usingKeyboard.d.ts +0 -0
  306. /package/{dist/mediaQueries → mediaQueries}/usingKeyboard.js +0 -0
@@ -0,0 +1,13 @@
1
+ /// <reference types="svelte" />
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ type Props = HTMLAttributes<HTMLDivElement> & {
4
+ onClose?: (value: string) => void;
5
+ onOpen?: (value: string) => void;
6
+ onSelect?: (value: string) => void;
7
+ };
8
+ declare const MenuBar: import("svelte").Component<Props, {
9
+ blur: () => void;
10
+ focus: (options?: FocusOptions) => void;
11
+ }, "">;
12
+ type MenuBar = ReturnType<typeof MenuBar>;
13
+ export default MenuBar;
@@ -0,0 +1,116 @@
1
+ <svelte:options runes={true} />
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 = () => {
19
+ buttonRef?.click();
20
+ };
21
+ export const blur = () => {
22
+ buttonRef?.blur();
23
+ };
24
+ export const focus = (options) => {
25
+ buttonRef?.focus(options);
26
+ };
27
+ // update open based on openValues
28
+ $effect(() => {
29
+ open = openValues.length > 0;
30
+ });
31
+ // update openValues based on open
32
+ $effect(() => {
33
+ if (open) {
34
+ openValues = openValues.length > 0 ? openValues : ['menu-button'];
35
+ }
36
+ else {
37
+ openValues = openValues.length === 0 ? openValues : [];
38
+ }
39
+ });
40
+ // focus when closing
41
+ $effect(() => {
42
+ if (!open && open !== prevOpen) {
43
+ focus();
44
+ }
45
+ prevOpen = open;
46
+ });
47
+ const onClick = async () => {
48
+ if (!open) {
49
+ openValues = ['menu-button'];
50
+ open = true;
51
+ await tick();
52
+ menuRef?.focusFirstMenuItem();
53
+ }
54
+ else {
55
+ open = false;
56
+ openValues = [];
57
+ }
58
+ };
59
+ const closeAllMenus = () => {
60
+ openValues = [];
61
+ open = false;
62
+ };
63
+ const onClickOutside = (event) => {
64
+ let element = event.target;
65
+ while (element) {
66
+ if (element.getAttribute('data-root-value') === value) {
67
+ return;
68
+ }
69
+ element = element.parentElement;
70
+ }
71
+ closeAllMenus?.();
72
+ };
73
+ // ----- Context ----- //
74
+ setContext(MENU_ITEM_CONTEXT_KEY, {
75
+ depth: 1,
76
+ get openValues() {
77
+ return openValues;
78
+ },
79
+ set openValues(value) {
80
+ openValues = value;
81
+ },
82
+ rootValue: value,
83
+ closeContainingMenu: () => {
84
+ open = false;
85
+ },
86
+ onOpen,
87
+ onClose,
88
+ onSelect
89
+ });
90
+ </script>
91
+
92
+ <Button
93
+ bind:this={buttonRef}
94
+ aria-controls={menuId}
95
+ aria-expanded={!!open}
96
+ aria-haspopup={!!children}
97
+ aria-owns={menuId}
98
+ class={['sterling-menu-button', _class].filter(Boolean).join(' ')}
99
+ data-value={value}
100
+ data-root-value={value}
101
+ {...rest}
102
+ onclick={onClick}
103
+ >
104
+ <div
105
+ class="reference"
106
+ bind:this={reference}
107
+ use:clickOutside={{ onclickoutside: onClickOutside }}
108
+ >
109
+ {@render children?.()}
110
+ </div>
111
+ <Popover {reference} {open} placement={popoverPlacement}>
112
+ <Menu bind:this={menuRef} id={menuId} class={menuClass}>
113
+ {@render items?.()}
114
+ </Menu>
115
+ </Popover>
116
+ </Button>
@@ -0,0 +1,20 @@
1
+ import type { HTMLButtonAttributes } from 'svelte/elements';
2
+ import { type Snippet } from 'svelte';
3
+ import type { PopoverPlacement } from './Popover.types';
4
+ type Props = HTMLButtonAttributes & {
5
+ items: Snippet;
6
+ menuClass?: string;
7
+ onOpen?: (value: string) => void;
8
+ onClose?: (value: string) => void;
9
+ onSelect?: (value: string) => void;
10
+ open?: boolean | null | undefined;
11
+ popoverPlacement?: PopoverPlacement;
12
+ value?: string;
13
+ };
14
+ declare const MenuButton: import("svelte").Component<Props, {
15
+ click: () => void;
16
+ blur: () => void;
17
+ focus: (options?: FocusOptions) => void;
18
+ }, "open">;
19
+ type MenuButton = ReturnType<typeof MenuButton>;
20
+ export default MenuButton;
@@ -1,46 +1,24 @@
1
- <script>import { getContext, afterUpdate, createEventDispatcher, onMount, setContext, tick } from 'svelte';
2
- import { writable } from 'svelte/store';
1
+ <svelte:options runes={true} />
2
+
3
+ <script lang="ts">import { getContext, setContext, tick } from 'svelte';
3
4
  import { idGenerator } from './idGenerator';
4
5
  import Menu from './Menu.svelte';
5
6
  import { MENU_BAR_CONTEXT_KEY } from './MenuBar.constants';
6
7
  import { MENU_ITEM_CONTEXT_KEY } from './MenuItem.constants';
7
8
  import { isElementEnabledMenuItem } from './MenuItem.utils';
8
- import MenuItemDisplay from './MenuItemDisplay.svelte';
9
9
  import Popover from './Popover.svelte';
10
10
  import { usingKeyboard } from './mediaQueries/usingKeyboard';
11
- // ----- Props ----- //
12
- /**
13
- * When true, the menu item is checked.
14
- * Use with role='menuitemcheckbox' or role='menuitemradio'.
15
- */
16
- export let checked = false;
17
- /** When true, the menu item is disabled. */
18
- export let disabled = false;
19
- /** The role of the menu item. */
20
- export let role = 'menuitem';
21
- /** The text of the menu item. Not used when the item slot is filled.*/
22
- export let text = undefined;
23
- /** The value uniquely identifying this menu item within the menu hierarchy. */
24
- export let value;
25
- /** Additional class names to apply. */
26
- export let variant = '';
27
- /** Additional class names to apply to the sub Menu*/
28
- export let menuVariant = '';
29
- // ----- Get Context ----- //
30
- const { isMenuBarItem, openValues = writable([]), rootValue = value, depth = 0, closeContainingMenu = undefined, onOpen = undefined, onClose = undefined, onSelect = undefined } = getContext(MENU_ITEM_CONTEXT_KEY) || {};
31
- const { openPreviousMenuBarItem = undefined, openNextMenuBarItem = undefined } = getContext(MENU_BAR_CONTEXT_KEY) || {};
32
- // ----- State ----- //
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) || {};
33
14
  const instanceId = idGenerator.nextId('MenuItem');
34
- $: displayId = `${value}-display-${instanceId}`;
35
- $: open = $openValues.includes(value);
36
- $: menuId = `${value}-menu-${instanceId}`;
37
- let menuItemRef;
38
- let menuRef;
39
- const children = writable([]);
40
- let mounted = false;
41
- let prevOpen = open;
42
- $: hasChildren = $$slots.default;
43
- // ----- Methods ----- //
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
44
22
  export const blur = () => {
45
23
  menuItemRef?.blur();
46
24
  };
@@ -50,30 +28,28 @@ export const click = () => {
50
28
  export const focus = (options) => {
51
29
  menuItemRef?.focus(options);
52
30
  };
53
- // ----- Events ----- //
54
- const dispatch = createEventDispatcher();
31
+ //#endregion
32
+ //#region events
55
33
  const raiseClose = (value) => {
56
- dispatch('close', { value });
57
34
  onClose?.(value);
35
+ menuItemContext.onClose?.(value);
58
36
  };
59
37
  const raiseOpen = (value) => {
60
- dispatch('open', { value });
61
38
  onOpen?.(value);
39
+ menuItemContext.onOpen?.(value);
62
40
  };
63
- $: {
64
- if (hasChildren && open !== prevOpen) {
65
- open ? raiseOpen(value) : raiseClose(value);
66
- }
67
- prevOpen = open;
68
- }
69
- // dispatches the event and bubbles it up the context
70
- // so that container components can subscribe to select
71
- // events for children.
72
41
  const raiseSelect = (value) => {
73
- dispatch('select', { value });
74
42
  onSelect?.(value);
43
+ menuItemContext.onSelect?.(value);
75
44
  };
76
- // ----- Focus ----- //
45
+ $effect(() => {
46
+ if (open !== prevOpen) {
47
+ open ? raiseOpen(value) : raiseClose(value);
48
+ }
49
+ prevOpen = open;
50
+ });
51
+ //#endregion
52
+ //#region focus
77
53
  const focusPreviousMenuItem = () => {
78
54
  let candidate = menuItemRef?.previousElementSibling || menuItemRef?.parentElement?.lastElementChild;
79
55
  while (candidate && !isElementEnabledMenuItem(candidate)) {
@@ -96,33 +72,32 @@ const focusNextMenuItem = () => {
96
72
  candidate?.focus();
97
73
  return !!candidate;
98
74
  };
99
- // ----- Open/Close ----- //
75
+ //#endregion
76
+ //#region open/close
100
77
  // opens the menu for this menu item
101
78
  const openMenu = () => {
102
- if (!$openValues.includes(value)) {
79
+ if (!menuItemContext.openValues.includes(value)) {
103
80
  // slice to depth to close any sibling menus that are open
104
- openValues.set([...$openValues.slice(0, depth), value]);
81
+ menuItemContext.openValues = [
82
+ ...menuItemContext.openValues.slice(0, menuItemContext.depth),
83
+ value
84
+ ];
105
85
  }
106
86
  };
107
87
  // closes the menu for this menu item
108
88
  const closeMenu = async () => {
109
- const index = $openValues.indexOf(value);
89
+ const index = menuItemContext.openValues.indexOf(value);
110
90
  if (index !== -1) {
111
- openValues.set([...$openValues.slice(0, index)]);
91
+ menuItemContext.openValues = [...menuItemContext.openValues.slice(0, index)];
112
92
  await tick();
113
93
  menuItemRef?.focus();
114
94
  }
115
95
  };
116
96
  const closeAllMenus = () => {
117
- openValues.set([]);
97
+ menuItemContext.openValues = [];
118
98
  };
119
- // ----- Event Handlers ----- //
120
- onMount(() => {
121
- mounted = true;
122
- });
123
- afterUpdate(() => {
124
- prevOpen = open;
125
- });
99
+ //#endregion
100
+ //#region event handlers
126
101
  const onKeyDown = async (event) => {
127
102
  if (!disabled && !event.altKey && !event.ctrlKey && !event.shiftKey) {
128
103
  switch (event.key) {
@@ -130,7 +105,7 @@ const onKeyDown = async (event) => {
130
105
  // ARIA menubar/menuitem:
131
106
  // If the currently focused menuitem has a submenu,
132
107
  // opens the submenu and places focus on the first item in the submenu.
133
- if (isMenuBarItem && hasChildren) {
108
+ if (menuItemContext.isMenuBarItem && children) {
134
109
  openMenu();
135
110
  setTimeout(async () => {
136
111
  await tick();
@@ -140,7 +115,7 @@ const onKeyDown = async (event) => {
140
115
  event.stopPropagation();
141
116
  return false;
142
117
  }
143
- if (!isMenuBarItem) {
118
+ if (!menuItemContext.isMenuBarItem) {
144
119
  // ARIA menuitem:
145
120
  // Moves focus to the next item, optionally wrapping from the last to the first.
146
121
  focusNextMenuItem();
@@ -152,7 +127,7 @@ const onKeyDown = async (event) => {
152
127
  case 'ArrowLeft':
153
128
  // ARIA menubar/menuitem:
154
129
  // Moves focus to the previous item, optionally wrapping from the first to the last.
155
- if (isMenuBarItem) {
130
+ if (menuItemContext.isMenuBarItem) {
156
131
  focusPreviousMenuItem();
157
132
  event.preventDefault();
158
133
  event.stopPropagation();
@@ -161,8 +136,8 @@ const onKeyDown = async (event) => {
161
136
  // ARIA menuitem:
162
137
  // When focus is in a submenu of an item in a menu,
163
138
  // closes the submenu and returns focus to the parent menuitem.
164
- if (depth > 1) {
165
- closeContainingMenu?.();
139
+ if (menuItemContext.depth && menuItemContext.depth > 1) {
140
+ menuItemContext.closeContainingMenu?.();
166
141
  event.preventDefault();
167
142
  event.stopPropagation();
168
143
  return false;
@@ -175,14 +150,14 @@ const onKeyDown = async (event) => {
175
150
  // if focus is now on a menuitem with a submenu,
176
151
  // either opens the submenu of that menuitem without moving focus into the submenu,
177
152
  // or opens the submenu of that menuitem and places focus on the first item in the submenu.
178
- openPreviousMenuBarItem?.();
153
+ menuBarContext.openPreviousMenuBarItem?.();
179
154
  event.preventDefault();
180
155
  event.stopPropagation();
181
156
  return false;
182
157
  case 'ArrowRight':
183
158
  // ARIA menubar:
184
159
  // Moves focus to the next item, optionally wrapping from the last to the first.
185
- if (isMenuBarItem) {
160
+ if (menuItemContext.isMenuBarItem) {
186
161
  focusNextMenuItem();
187
162
  event.preventDefault();
188
163
  event.stopPropagation();
@@ -191,7 +166,7 @@ const onKeyDown = async (event) => {
191
166
  // ARIA menuitem:
192
167
  // When focus is in a menu and on a menuitem that has a submenu,
193
168
  // opens the submenu and places focus on its first item
194
- if (hasChildren) {
169
+ if (children) {
195
170
  openMenu();
196
171
  setTimeout(async () => {
197
172
  await tick();
@@ -209,8 +184,8 @@ const onKeyDown = async (event) => {
209
184
  // if focus is now on a menuitem with a submenu,
210
185
  // either opens the submenu of that menuitem without moving focus into the submenu,
211
186
  // or opens the submenu of that menuitem and places focus on the first item in the submenu.
212
- if (openNextMenuBarItem) {
213
- openNextMenuBarItem();
187
+ if (menuBarContext.openNextMenuBarItem) {
188
+ menuBarContext.openNextMenuBarItem();
214
189
  event.preventDefault();
215
190
  event.stopPropagation();
216
191
  return false;
@@ -220,7 +195,7 @@ const onKeyDown = async (event) => {
220
195
  // ARIA menubar/menuitem:
221
196
  // If the currently focused menuitem has a submenu,
222
197
  // opens the submenu and places focus on the last item in the submenu.
223
- if (isMenuBarItem && hasChildren) {
198
+ if (menuItemContext.isMenuBarItem && children) {
224
199
  openMenu();
225
200
  setTimeout(async () => {
226
201
  await tick();
@@ -232,7 +207,7 @@ const onKeyDown = async (event) => {
232
207
  }
233
208
  // ARIA menuitem:
234
209
  // Moves focus to the previous item, optionally wrapping from the first to the last.
235
- if (!isMenuBarItem) {
210
+ if (!menuItemContext.isMenuBarItem) {
236
211
  focusPreviousMenuItem();
237
212
  event.preventDefault();
238
213
  event.stopPropagation();
@@ -243,21 +218,23 @@ const onKeyDown = async (event) => {
243
218
  // ARIA menuitem:
244
219
  // Close the menu that contains focus and return focus to the element or context,
245
220
  // e.g., menu button or parent menuitem, from which the menu was opened.
246
- open = false;
221
+ // open = false;
247
222
  closeAllMenus();
248
223
  event.preventDefault();
249
224
  event.stopPropagation();
250
225
  return false;
251
226
  }
252
227
  }
228
+ rest.onkeydown?.(event);
253
229
  };
254
230
  const onMouseEnter = (event) => {
255
231
  menuItemRef?.focus();
232
+ rest.onmouseenter?.(event);
256
233
  };
257
234
  const onClick = (event) => {
258
235
  if (!disabled) {
259
- if (hasChildren) {
260
- if (!$openValues.includes(value)) {
236
+ if (children) {
237
+ if (!menuItemContext.openValues.includes(value)) {
261
238
  openMenu();
262
239
  if ($usingKeyboard) {
263
240
  setTimeout(async () => {
@@ -281,105 +258,84 @@ const onClick = (event) => {
281
258
  return false;
282
259
  }
283
260
  }
261
+ rest.onclick?.(event);
284
262
  };
285
- // ----- Set Context ----- //
286
- setContext(MENU_ITEM_CONTEXT_KEY, {
263
+ //#endregion
264
+ //#region set context
265
+ let menuItemChildContext = {
287
266
  isMenuBarItem: false,
288
- openValues,
289
- rootValue,
290
- depth: depth + 1,
291
- closeContainingMenu: () => {
292
- closeMenu();
267
+ get openValues() {
268
+ return menuItemContext.openValues;
293
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,
294
276
  onOpen: raiseOpen,
295
277
  onClose: raiseClose,
296
278
  onSelect: raiseSelect
297
- });
279
+ };
280
+ setContext(MENU_ITEM_CONTEXT_KEY, menuItemChildContext);
281
+ //#endregion
298
282
  </script>
299
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
+
300
304
  <button
305
+ bind:this={menuItemRef}
301
306
  aria-controls={menuId}
302
307
  aria-disabled={disabled}
303
308
  aria-expanded={open}
304
- aria-haspopup={hasChildren}
309
+ aria-haspopup={!!children}
305
310
  aria-owns={menuId}
306
- bind:this={menuItemRef}
307
- class={`sterling-menu-item ${variant}`}
311
+ class={['sterling-menu-item', _class].filter(Boolean).join(' ')}
308
312
  class:using-keyboard={usingKeyboard}
309
313
  data-value={value}
310
- data-root-value={rootValue}
314
+ data-root-value={menuItemContext.rootValue}
311
315
  {disabled}
312
316
  {role}
313
317
  tabindex={0}
314
318
  type="button"
315
- on:blur
316
- on:click
317
- on:dblclick
318
- on:dragend
319
- on:dragenter
320
- on:dragleave
321
- on:dragover
322
- on:dragstart
323
- on:drop
324
- on:focus
325
- on:focusin
326
- on:focusout
327
- on:keydown
328
- on:keypress
329
- on:keyup
330
- on:mousedown
331
- on:mouseenter
332
- on:mouseleave
333
- on:mousemove
334
- on:mouseover
335
- on:mouseout
336
- on:mouseup
337
- on:pointercancel
338
- on:pointerdown
339
- on:pointerenter
340
- on:pointerleave
341
- on:pointermove
342
- on:pointerover
343
- on:pointerout
344
- on:pointerup
345
- on:wheel|passive
346
- on:click={onClick}
347
- on:keydown={onKeyDown}
348
- on:mouseenter={onMouseEnter}
349
- {...$$restProps}
319
+ {...rest}
320
+ onclick={onClick}
321
+ onkeydown={onKeyDown}
322
+ onmouseenter={onMouseEnter}
350
323
  >
351
324
  <div class="item" id={displayId}>
352
- <slot
353
- name="item"
354
- {checked}
355
- {depth}
356
- {disabled}
357
- {hasChildren}
358
- {isMenuBarItem}
359
- {open}
360
- {role}
361
- {text}
362
- {value}
363
- {variant}
364
- >
365
- <MenuItemDisplay
366
- {checked}
367
- {disabled}
368
- {hasChildren}
369
- {isMenuBarItem}
370
- menuItemRole={role}
371
- {variant}>{text}</MenuItemDisplay
372
- >
373
- </slot>
325
+ {#if item}
326
+ {@render item()}
327
+ {:else}
328
+ {@render renderDefaultItem()}
329
+ {/if}
374
330
  </div>
375
- {#if menuItemRef && open && hasChildren}
331
+ {#if menuItemRef && open && children}
376
332
  <Popover
377
333
  reference={menuItemRef}
378
- placement={isMenuBarItem ? 'bottom-start' : 'right-start'}
334
+ placement={menuItemContext.isMenuBarItem ? 'bottom-start' : 'right-start'}
379
335
  {open}
380
336
  >
381
- <Menu bind:this={menuRef} id={menuId} variant={menuVariant}>
382
- <slot {depth} {disabled} />
337
+ <Menu bind:this={menuRef} id={menuId} class={menuClass}>
338
+ {@render children()}
383
339
  </Menu>
384
340
  </Popover>
385
341
  {/if}
@@ -0,0 +1,22 @@
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,5 +1,3 @@
1
- /// <reference types="svelte" />
2
- import type { Writable } from 'svelte/store';
3
1
  import type { MENU_ITEM_ROLES } from './MenuItem.constants';
4
2
  type MenuItemRoleTuple = typeof MENU_ITEM_ROLES;
5
3
  export type MenuItemRole = MenuItemRoleTuple[number];
@@ -10,14 +8,8 @@ export type MenuItemRegistration = {
10
8
  focus: () => void;
11
9
  };
12
10
  export type MenuItemContext = {
13
- /**
14
- * If the menu item is a top-level item in a menu bar
15
- */
16
11
  isMenuBarItem?: boolean;
17
- /**
18
- * The menu item values for the chain of open menus.
19
- */
20
- openValues: Writable<string[]>;
12
+ openValues: string[];
21
13
  rootValue?: string;
22
14
  depth?: number;
23
15
  closeContainingMenu?: () => void;
@@ -0,0 +1,11 @@
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>
@@ -0,0 +1,6 @@
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;