fluentui-svelte 0.5.0 → 0.7.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 (259) hide show
  1. package/dist/components/app-surface/app-surface.svelte +96 -0
  2. package/dist/components/app-surface/app-surface.svelte.d.ts +22 -0
  3. package/dist/components/app-surface/docs/llms.md +73 -0
  4. package/dist/components/app-surface/types.d.ts +19 -0
  5. package/dist/components/app-surface/types.js +1 -0
  6. package/dist/components/app-window/app-window.d.ts +3 -0
  7. package/dist/components/app-window/app-window.js +3 -0
  8. package/dist/components/app-window/app-window.svelte +193 -0
  9. package/dist/components/app-window/app-window.svelte.d.ts +25 -0
  10. package/dist/components/app-window/docs/llms.md +119 -0
  11. package/dist/components/app-window/types.d.ts +66 -0
  12. package/dist/components/app-window/types.js +1 -0
  13. package/dist/components/auto-suggest-box/auto-suggest-box.svelte +47 -31
  14. package/dist/components/auto-suggest-box/auto-suggest-box.svelte.d.ts +2 -2
  15. package/dist/components/auto-suggest-box/docs/llms.md +34 -33
  16. package/dist/components/auto-suggest-box/option.svelte +6 -4
  17. package/dist/components/auto-suggest-box/types.d.ts +13 -16
  18. package/dist/components/avatar/avatar.svelte +4 -3
  19. package/dist/components/avatar/docs/llms.md +1 -1
  20. package/dist/components/avatar/type.d.ts +1 -1
  21. package/dist/components/badge/badge-icon.svelte +2 -2
  22. package/dist/components/badge/badge.svelte +2 -2
  23. package/dist/components/badge/docs/llms.md +2 -2
  24. package/dist/components/badge/types.d.ts +2 -2
  25. package/dist/components/breadcrumb/breadcrumb-button.svelte +2 -2
  26. package/dist/components/breadcrumb/breadcrumb-divider.svelte +2 -2
  27. package/dist/components/breadcrumb/breadcrumb-item.svelte +2 -2
  28. package/dist/components/breadcrumb/breadcrumb.svelte +10 -5
  29. package/dist/components/breadcrumb/docs/llms.md +11 -11
  30. package/dist/components/breadcrumb/types.d.ts +7 -7
  31. package/dist/components/button/button.svelte +2 -2
  32. package/dist/components/button/docs/llms.md +17 -17
  33. package/dist/components/button/split-button.svelte +7 -7
  34. package/dist/components/button/types.d.ts +5 -5
  35. package/dist/components/calendar-date-picker/calendar-date-picker.svelte +98 -10
  36. package/dist/components/calendar-date-picker/calendar-date-picker.svelte.d.ts +1 -1
  37. package/dist/components/calendar-date-picker/docs/llms.md +19 -14
  38. package/dist/components/calendar-date-picker/types.d.ts +22 -8
  39. package/dist/components/calendar-view/calendar-view-days.svelte +3 -2
  40. package/dist/components/calendar-view/calendar-view-grid.js +8 -3
  41. package/dist/components/calendar-view/calendar-view-item.svelte +10 -4
  42. package/dist/components/calendar-view/calendar-view-months.svelte +3 -2
  43. package/dist/components/calendar-view/calendar-view-years.svelte +3 -2
  44. package/dist/components/calendar-view/calendar-view.svelte +11 -4
  45. package/dist/components/calendar-view/calendar-view.svelte.js +5 -0
  46. package/dist/components/calendar-view/docs/llms.md +20 -19
  47. package/dist/components/calendar-view/types.d.ts +6 -1
  48. package/dist/components/card/card-footer.svelte +2 -2
  49. package/dist/components/card/card-header.svelte +5 -4
  50. package/dist/components/card/card-preview.svelte +13 -2
  51. package/dist/components/card/card.svelte +5 -5
  52. package/dist/components/card/docs/llms.md +22 -21
  53. package/dist/components/card/types.d.ts +8 -4
  54. package/dist/components/checkbox/checkbox.svelte +47 -26
  55. package/dist/components/checkbox/checkbox.svelte.d.ts +3 -24
  56. package/dist/components/checkbox/docs/llms.md +24 -23
  57. package/dist/components/checkbox/types.d.ts +10 -23
  58. package/dist/components/date-picker/date-picker.svelte +98 -27
  59. package/dist/components/date-picker/date-picker.svelte.d.ts +1 -1
  60. package/dist/components/date-picker/docs/llms.md +23 -20
  61. package/dist/components/date-picker/types.d.ts +24 -15
  62. package/dist/components/dialog/dialog-actions.svelte +17 -2
  63. package/dist/components/dialog/dialog-content.svelte +2 -2
  64. package/dist/components/dialog/dialog-content.svelte.d.ts +1 -1
  65. package/dist/components/dialog/dialog-surface.svelte +130 -8
  66. package/dist/components/dialog/dialog-title.svelte +23 -6
  67. package/dist/components/dialog/dialog-trigger.svelte +8 -2
  68. package/dist/components/dialog/dialog-trigger.svelte.d.ts +1 -1
  69. package/dist/components/dialog/dialog.svelte +22 -11
  70. package/dist/components/dialog/docs/llms.md +56 -19
  71. package/dist/components/dialog/types.d.ts +31 -9
  72. package/dist/components/dialog/utils.d.ts +2 -0
  73. package/dist/components/dialog/utils.js +1 -0
  74. package/dist/components/divider/divider.svelte +6 -4
  75. package/dist/components/divider/types.d.ts +1 -1
  76. package/dist/components/dropdown/docs/llms.md +30 -28
  77. package/dist/components/dropdown/dropdown-option.svelte +17 -5
  78. package/dist/components/dropdown/dropdown.svelte +182 -84
  79. package/dist/components/dropdown/dropdown.svelte.d.ts +1 -1
  80. package/dist/components/dropdown/types.d.ts +26 -20
  81. package/dist/components/dropdown-select/docs/llms.md +24 -22
  82. package/dist/components/dropdown-select/dropdown-select-option.svelte +2 -2
  83. package/dist/components/dropdown-select/dropdown-select.svelte +42 -7
  84. package/dist/components/dropdown-select/types.d.ts +4 -4
  85. package/dist/components/expander/docs/llms.md +3 -3
  86. package/dist/components/expander/expander.svelte +13 -8
  87. package/dist/components/expander/types.d.ts +5 -5
  88. package/dist/components/flyout/docs/llms.md +1 -1
  89. package/dist/components/flyout/flyout.svelte +17 -6
  90. package/dist/components/flyout/types.d.ts +1 -1
  91. package/dist/components/hyperlink/docs/llms.md +6 -6
  92. package/dist/components/hyperlink/hyperlink.svelte +13 -4
  93. package/dist/components/hyperlink/types.d.ts +1 -1
  94. package/dist/components/info-bar/docs/llms.md +11 -10
  95. package/dist/components/info-bar/info-bar.svelte +3 -2
  96. package/dist/components/info-bar/types.d.ts +4 -0
  97. package/dist/components/label/docs/llms.md +75 -22
  98. package/dist/components/label/label-context.d.ts +14 -0
  99. package/dist/components/label/label-context.js +10 -0
  100. package/dist/components/label/label.svelte +153 -30
  101. package/dist/components/label/label.svelte.d.ts +1 -1
  102. package/dist/components/label/types.d.ts +48 -4
  103. package/dist/components/list-view/docs/llms.md +103 -33
  104. package/dist/components/list-view/list-view-item-branch.svelte +257 -0
  105. package/dist/components/list-view/list-view-item-branch.svelte.d.ts +4 -0
  106. package/dist/components/list-view/list-view-item.svelte +164 -46
  107. package/dist/components/list-view/list-view-item.svelte.d.ts +5 -5
  108. package/dist/components/list-view/list-view.svelte +73 -25
  109. package/dist/components/list-view/types.d.ts +71 -8
  110. package/dist/components/list-view/utils.d.ts +10 -0
  111. package/dist/components/list-view/utils.js +17 -1
  112. package/dist/components/menu/docs/llms.md +98 -15
  113. package/dist/components/menu/menu-command-bar-item.svelte +61 -0
  114. package/dist/components/menu/menu-command-bar-item.svelte.d.ts +14 -0
  115. package/dist/components/menu/menu-command-bar.svelte +218 -0
  116. package/dist/components/menu/menu-command-bar.svelte.d.ts +21 -0
  117. package/dist/components/menu/menu-group.svelte +6 -3
  118. package/dist/components/menu/menu-item-checkbox.svelte +5 -4
  119. package/dist/components/menu/menu-item-radio.svelte +5 -4
  120. package/dist/components/menu/menu-item-switch.svelte +5 -4
  121. package/dist/components/menu/menu-item.svelte +5 -5
  122. package/dist/components/menu/menu-item.svelte.d.ts +2 -3
  123. package/dist/components/menu/menu-list.svelte +10 -1
  124. package/dist/components/menu/menu-popover.svelte +9 -5
  125. package/dist/components/menu/menu-trigger.svelte +2 -1
  126. package/dist/components/menu/menu.svelte.js +1 -0
  127. package/dist/components/menu/types.d.ts +62 -7
  128. package/dist/components/number-box/docs/llms.md +6 -4
  129. package/dist/components/number-box/number-box.svelte +35 -29
  130. package/dist/components/number-box/number-box.svelte.d.ts +1 -1
  131. package/dist/components/number-box/types.d.ts +1 -1
  132. package/dist/components/persona/docs/llms.md +29 -25
  133. package/dist/components/persona/persona.svelte +8 -6
  134. package/dist/components/persona/types.d.ts +4 -3
  135. package/dist/components/progress-bar/docs/llms.md +13 -13
  136. package/dist/components/progress-bar/progress-bar.svelte +6 -6
  137. package/dist/components/progress-bar/types.d.ts +4 -4
  138. package/dist/components/progress-ring/docs/llms.md +14 -14
  139. package/dist/components/progress-ring/progress-ring.svelte +5 -5
  140. package/dist/components/progress-ring/types.d.ts +3 -3
  141. package/dist/components/radio-button/docs/llms.md +11 -8
  142. package/dist/components/radio-button/radio-button.svelte +38 -12
  143. package/dist/components/radio-button/radio-button.svelte.d.ts +1 -1
  144. package/dist/components/radio-button/types.d.ts +11 -8
  145. package/dist/components/rating/docs/llms.md +111 -0
  146. package/dist/components/rating/rating.svelte +247 -0
  147. package/dist/components/rating/rating.svelte.d.ts +19 -0
  148. package/dist/components/rating/types.d.ts +44 -0
  149. package/dist/components/rating/types.js +1 -0
  150. package/dist/components/side-nav/docs/llms.md +110 -0
  151. package/dist/components/side-nav/side-nav-branch.svelte +43 -0
  152. package/dist/components/side-nav/side-nav-branch.svelte.d.ts +4 -0
  153. package/dist/components/side-nav/side-nav-item.svelte +75 -0
  154. package/dist/components/side-nav/side-nav-item.svelte.d.ts +25 -0
  155. package/dist/components/side-nav/side-nav-list.svelte +32 -0
  156. package/dist/components/side-nav/side-nav-list.svelte.d.ts +7 -0
  157. package/dist/components/side-nav/side-nav.d.ts +2 -0
  158. package/dist/components/side-nav/side-nav.js +2 -0
  159. package/dist/components/side-nav/side-nav.svelte +355 -0
  160. package/dist/components/side-nav/side-nav.svelte.d.ts +24 -0
  161. package/dist/components/side-nav/types.d.ts +149 -0
  162. package/dist/components/side-nav/types.js +1 -0
  163. package/dist/components/skeleton/docs/llms.md +1 -1
  164. package/dist/components/skeleton/skeleton.svelte +2 -2
  165. package/dist/components/skeleton/types.d.ts +1 -1
  166. package/dist/components/slider/docs/llms.md +31 -28
  167. package/dist/components/slider/slider.svelte +126 -61
  168. package/dist/components/slider/slider.svelte.d.ts +1 -3
  169. package/dist/components/slider/types.d.ts +21 -14
  170. package/dist/components/tab-view/docs/llms.md +7 -7
  171. package/dist/components/tab-view/tab-view-tab.svelte +16 -14
  172. package/dist/components/tab-view/tab-view.svelte +16 -9
  173. package/dist/components/tab-view/types.d.ts +6 -6
  174. package/dist/components/table/docs/llms.md +38 -33
  175. package/dist/components/table/table-body.svelte +2 -2
  176. package/dist/components/table/table-cell-actions.svelte +8 -2
  177. package/dist/components/table/table-cell-layout.svelte +2 -2
  178. package/dist/components/table/table-cell.svelte +2 -2
  179. package/dist/components/table/table-header-cell.svelte +2 -2
  180. package/dist/components/table/table-header.svelte +2 -2
  181. package/dist/components/table/table-row.svelte +2 -2
  182. package/dist/components/table/table-selection-cell.svelte +6 -6
  183. package/dist/components/table/table.svelte +2 -2
  184. package/dist/components/table/types.d.ts +11 -11
  185. package/dist/components/tag/docs/llms.md +383 -0
  186. package/dist/components/tag/tag-group.d.ts +2 -0
  187. package/dist/components/tag/tag-group.js +2 -0
  188. package/dist/components/tag/tag-group.svelte +117 -0
  189. package/dist/components/tag/tag-group.svelte.d.ts +20 -0
  190. package/dist/components/tag/tag.svelte +459 -0
  191. package/dist/components/tag/tag.svelte.d.ts +19 -0
  192. package/dist/components/tag/types.d.ts +137 -0
  193. package/dist/components/tag/types.js +1 -0
  194. package/dist/components/tag-picker/docs/llms.md +298 -0
  195. package/dist/components/tag-picker/option-group.svelte +51 -0
  196. package/dist/components/tag-picker/option-group.svelte.d.ts +4 -0
  197. package/dist/components/tag-picker/option.svelte +82 -0
  198. package/dist/components/tag-picker/option.svelte.d.ts +4 -0
  199. package/dist/components/tag-picker/tag-picker.d.ts +2 -0
  200. package/dist/components/tag-picker/tag-picker.js +2 -0
  201. package/dist/components/tag-picker/tag-picker.svelte +716 -0
  202. package/dist/components/tag-picker/tag-picker.svelte.d.ts +21 -0
  203. package/dist/components/tag-picker/types.d.ts +142 -0
  204. package/dist/components/tag-picker/types.js +1 -0
  205. package/dist/components/text-box/docs/llms.md +31 -4
  206. package/dist/components/text-box/text-box-button.svelte +3 -3
  207. package/dist/components/text-box/text-box-button.svelte.d.ts +1 -1
  208. package/dist/components/text-box/text-box.svelte +41 -14
  209. package/dist/components/text-box/types.d.ts +5 -5
  210. package/dist/components/textarea/docs/llms.md +4 -3
  211. package/dist/components/textarea/textarea.svelte +14 -6
  212. package/dist/components/textarea/types.d.ts +2 -4
  213. package/dist/components/time-picker/docs/llms.md +18 -15
  214. package/dist/components/time-picker/time-picker.svelte +133 -33
  215. package/dist/components/time-picker/time-picker.svelte.d.ts +1 -1
  216. package/dist/components/time-picker/types.d.ts +23 -13
  217. package/dist/components/title-bar/docs/llms.md +382 -0
  218. package/dist/components/title-bar/title-bar-left-controls.svelte +215 -0
  219. package/dist/components/title-bar/title-bar-left-controls.svelte.d.ts +22 -0
  220. package/dist/components/title-bar/title-bar-right-controls.svelte +190 -0
  221. package/dist/components/title-bar/title-bar-right-controls.svelte.d.ts +27 -0
  222. package/dist/components/title-bar/title-bar.d.ts +3 -0
  223. package/dist/components/title-bar/title-bar.js +4 -0
  224. package/dist/components/title-bar/title-bar.svelte +224 -0
  225. package/dist/components/title-bar/title-bar.svelte.d.ts +26 -0
  226. package/dist/components/title-bar/types.d.ts +165 -0
  227. package/dist/components/title-bar/types.js +1 -0
  228. package/dist/components/toggle-switch/docs/llms.md +11 -9
  229. package/dist/components/toggle-switch/toggle-switch.svelte +23 -14
  230. package/dist/components/toggle-switch/toggle-switch.svelte.d.ts +1 -1
  231. package/dist/components/toggle-switch/types.d.ts +12 -10
  232. package/dist/components/tooltip/docs/llms.md +1 -1
  233. package/dist/components/tooltip/tooltip.svelte +5 -4
  234. package/dist/components/tooltip/types.d.ts +1 -1
  235. package/dist/components/top-nav/docs/llms.md +1 -1
  236. package/dist/components/top-nav/top-nav-item.svelte +2 -2
  237. package/dist/components/top-nav/top-nav.svelte +2 -2
  238. package/dist/components/top-nav/types.d.ts +2 -2
  239. package/dist/components/tree-view/docs/llms.md +11 -11
  240. package/dist/components/tree-view/tree-view-item-content.svelte +3 -3
  241. package/dist/components/tree-view/tree-view-item.svelte +13 -5
  242. package/dist/components/tree-view/tree-view.svelte +20 -5
  243. package/dist/components/tree-view/tree-view.svelte.d.ts +1 -1
  244. package/dist/components/tree-view/tree-view.svelte.js +31 -3
  245. package/dist/components/tree-view/types.d.ts +7 -3
  246. package/dist/index.d.ts +25 -0
  247. package/dist/index.js +32 -0
  248. package/dist/internal/components/dynamic-carousel/dynamic-carousel.svelte +26 -5
  249. package/dist/internal/components/dynamic-carousel/dynamic-carousel.svelte.d.ts +2 -0
  250. package/dist/internal/constants.d.ts +0 -4
  251. package/dist/internal/constants.js +0 -4002
  252. package/dist/internal/index.d.ts +1 -0
  253. package/dist/internal/index.js +1 -0
  254. package/dist/internal/native-field.svelte.d.ts +44 -0
  255. package/dist/internal/native-field.svelte.js +85 -0
  256. package/dist/internal/utils.d.ts +16 -2
  257. package/dist/internal/utils.js +25 -3
  258. package/dist/types/index.d.ts +3 -3
  259. package/package.json +51 -37
@@ -0,0 +1,218 @@
1
+ <script lang="ts">
2
+ import { getTabspotAttributes } from 'tabspot';
3
+ import MoreHorizontalRegular from 'fluentui-icons-svelte/MoreHorizontalRegular.svelte';
4
+ import { Button, Menu, MenuList, MenuPopover, MenuTrigger } from '../../index.js';
5
+ import { getGlobalFSContext } from '../../providers/fluentui-svelte/fluentui-svelte.js';
6
+ import { getMenuContext, setMenuCommandBarContext, COMPONENT_NAME as MENU_COMPONENT_NAME } from './menu.svelte.js';
7
+ import type { MenuCommandBarProps } from './types.js';
8
+
9
+ let {
10
+ ref = $bindable(null),
11
+ position = 'top',
12
+ label = 'Commands',
13
+ overflowLabel = 'More options',
14
+ overflow,
15
+ showLabels = false,
16
+ showDividers = false,
17
+ children: commands,
18
+ class: classes,
19
+ onkeydown: userOnkeydown,
20
+ ...attributes
21
+ }: MenuCommandBarProps = $props();
22
+
23
+ if (!getMenuContext()) throw new Error(`No MenuContext found for ${MENU_COMPONENT_NAME}.`);
24
+
25
+ const globalFSContext = getGlobalFSContext();
26
+
27
+ setMenuCommandBarContext({
28
+ get showLabels() {
29
+ return showLabels;
30
+ }
31
+ });
32
+
33
+ // Left and Right move along the bar. Up and Down are left to the bar, which hands the focus over
34
+ // to the items of the menu, and the menu hands it back when its items run out towards the bar.
35
+ const tabspotAttributes = getTabspotAttributes({
36
+ mover: { axis: 'horizontal', ignoreKeys: ['ArrowUp', 'ArrowDown'] },
37
+ grouper: {}
38
+ });
39
+
40
+ const isFocusable = (element: HTMLElement) =>
41
+ !element.matches(':disabled, [aria-disabled="true"]') && element.checkVisibility();
42
+
43
+ /** The items of the menu the bar sits in, without the bar and without any submenu. */
44
+ const getMenuItems = (list: HTMLElement) =>
45
+ Array.from(list.querySelectorAll<HTMLElement>('[role^="menuitem"]')).filter(
46
+ (item) => item.closest('.fs-menu-list') === list && !ref?.contains(item) && isFocusable(item)
47
+ );
48
+
49
+ const focusBar = () =>
50
+ Array.from(ref?.children ?? [])
51
+ .find((child): child is HTMLElement => child instanceof HTMLElement && isFocusable(child))
52
+ ?.focus();
53
+
54
+ const onkeydown = (e: KeyboardEvent) => {
55
+ // The overflow submenu owns its own keys.
56
+ if (e.target instanceof HTMLElement && e.target.parentElement !== ref) return;
57
+
58
+ const list = ref?.closest<HTMLElement>('.fs-menu-list');
59
+ const toItems = position === 'top' ? e.key === 'ArrowDown' : e.key === 'ArrowUp';
60
+ if (!list || !toItems) return;
61
+
62
+ const items = getMenuItems(list);
63
+ const item = position === 'top' ? items.at(0) : items.at(-1);
64
+ if (!item) return;
65
+
66
+ e.preventDefault();
67
+ item.focus();
68
+ };
69
+
70
+ $effect(() => {
71
+ const list = ref?.closest<HTMLElement>('.fs-menu-list');
72
+ const instance = globalFSContext?.state.tabspotInstance;
73
+ if (!list || !instance) return;
74
+
75
+ return instance.subscribe(list, (event) => {
76
+ const towardsBar = position === 'top' ? event.direction === 'up' : event.direction === 'down';
77
+ if (!event.atEdge || !towardsBar || ref?.contains(event.from)) return;
78
+
79
+ event.preventDefault();
80
+ focusBar();
81
+ });
82
+ });
83
+ </script>
84
+
85
+ <!--
86
+ @component
87
+
88
+ A row of square, icon-only `MenuCommandBarItem`s, the primary commands of a command bar flyout. It goes at the top or at the bottom of a `MenuList`, and sets itself apart from the items with a divider. An `overflow` snippet adds a "more" button that opens the secondary commands in a submenu.
89
+
90
+ - Usage:
91
+ ```tsx
92
+ <MenuList>
93
+ <MenuCommandBar>
94
+ <MenuCommandBarItem icon={CutRegular} label="Cut" />
95
+ <MenuCommandBarItem icon={CopyRegular} label="Copy" />
96
+ {#snippet overflow()}
97
+ <MenuItem>Select all</MenuItem>
98
+ {/snippet}
99
+ </MenuCommandBar>
100
+ <MenuItem>Paste</MenuItem>
101
+ </MenuList>
102
+ ```
103
+ -->
104
+ <div
105
+ {...attributes}
106
+ {...tabspotAttributes}
107
+ bind:this={ref}
108
+ role="group"
109
+ aria-label={label}
110
+ class={['fs-menu-command-bar', position, { labels: showLabels, dividers: showDividers }, classes]}
111
+ onkeydown={(e) => {
112
+ userOnkeydown?.(e);
113
+ onkeydown(e);
114
+ }}
115
+ >
116
+ {@render commands?.()}
117
+ {#if overflow}
118
+ <Menu>
119
+ <MenuTrigger>
120
+ {#snippet children({ state, menuTriggerProps })}
121
+ <Button
122
+ bind:ref={() => state.ref as HTMLButtonElement | null, (v) => (state.ref = v)}
123
+ {...menuTriggerProps}
124
+ class="{menuTriggerProps.class} fs-menu-command-bar-item"
125
+ appearance="subtle"
126
+ isMenuButton={false}
127
+ indicatorIcon={undefined}
128
+ aria-label={overflowLabel}
129
+ title={overflowLabel}
130
+ >
131
+ <MoreHorizontalRegular aria-hidden="true" />
132
+ </Button>
133
+ {/snippet}
134
+ </MenuTrigger>
135
+ <MenuPopover>
136
+ <MenuList>{@render overflow()}</MenuList>
137
+ </MenuPopover>
138
+ </Menu>
139
+ {/if}
140
+ </div>
141
+
142
+ <style>
143
+ .fs-menu-command-bar {
144
+ display: flex;
145
+ align-items: center;
146
+ /* The squares spread over the width of the menu, the overflow button closing the row. */
147
+ justify-content: space-between;
148
+ /* The commands never squeeze into the width of the menu: the menu grows to fit them all,
149
+ with this much room between any two. */
150
+ --fs-menu-command-bar-gap: 0.5rem;
151
+ gap: var(--fs-menu-command-bar-gap);
152
+ flex: none;
153
+ padding: 0.25rem;
154
+ /* The divider runs from edge to edge of the menu, like a MenuDivider. */
155
+ margin-inline: -0.25rem;
156
+ &.top {
157
+ border-bottom: 1px solid var(--fs-control-stroke-default);
158
+ margin-block: -0.25rem 0.25rem;
159
+ }
160
+ &.bottom {
161
+ border-top: 1px solid var(--fs-control-stroke-default);
162
+ margin-block: 0.25rem -0.25rem;
163
+ }
164
+ /* Every command is a square of at least 40px, the overflow button included. */
165
+ & > :global(.fs-button.fs-menu-command-bar-item) {
166
+ flex: none;
167
+ /* The overflow button is a submenu trigger, which otherwise spans the whole menu. */
168
+ inline-size: 2.5rem;
169
+ min-inline-size: 2.5rem;
170
+ min-block-size: 2.5rem;
171
+ aspect-ratio: 1;
172
+ padding: 0;
173
+ justify-content: center;
174
+ border-radius: var(--fs-control-inner-border-radius);
175
+ }
176
+ & > :global(.fs-menu-command-bar-item svg) {
177
+ flex: none;
178
+ inline-size: 1.25rem;
179
+ block-size: 1.25rem;
180
+ fill: currentColor;
181
+ }
182
+ /* With labels, or with dividers, the commands share the width of the bar evenly, so a divider
183
+ always falls halfway between two of them. The icon-only overflow button stays a square. */
184
+ &.labels,
185
+ &.dividers {
186
+ align-items: stretch;
187
+ & > :global(.fs-button.fs-menu-command-bar-item:not(.fs-submenu-trigger)) {
188
+ flex: 1 0 0;
189
+ inline-size: auto;
190
+ min-inline-size: max-content;
191
+ aspect-ratio: auto;
192
+ }
193
+ & > :global(.fs-button.fs-submenu-trigger) {
194
+ aspect-ratio: auto;
195
+ }
196
+ }
197
+ &.labels > :global(.fs-button.fs-menu-command-bar-item:not(.fs-submenu-trigger)) {
198
+ flex-direction: column;
199
+ gap: 0.25rem;
200
+ padding: 0.375rem 0.75rem;
201
+ font-size: var(--fs-caption-font-size);
202
+ line-height: var(--fs-caption-line-height);
203
+ }
204
+ &.dividers > :global(.fs-button.fs-menu-command-bar-item + .fs-button.fs-menu-command-bar-item::before) {
205
+ content: '';
206
+ position: absolute;
207
+ /* Centered in the gap between the two commands. */
208
+ inset-inline-start: calc(var(--fs-menu-command-bar-gap) / -2 - 0.5px);
209
+ inset-block: 25%;
210
+ inline-size: 1px;
211
+ background: var(--fs-divider-stroke-default);
212
+ pointer-events: none;
213
+ }
214
+ }
215
+ :global(.fs-menu-command-bar-item-label) {
216
+ white-space: nowrap;
217
+ }
218
+ </style>
@@ -0,0 +1,21 @@
1
+ import type { MenuCommandBarProps } from './types.ts';
2
+ /**
3
+ * A row of square, icon-only `MenuCommandBarItem`s, the primary commands of a command bar flyout. It goes at the top or at the bottom of a `MenuList`, and sets itself apart from the items with a divider. An `overflow` snippet adds a "more" button that opens the secondary commands in a submenu.
4
+ *
5
+ * - Usage:
6
+ * ```tsx
7
+ * <MenuList>
8
+ * <MenuCommandBar>
9
+ * <MenuCommandBarItem icon={CutRegular} label="Cut" />
10
+ * <MenuCommandBarItem icon={CopyRegular} label="Copy" />
11
+ * {#snippet overflow()}
12
+ * <MenuItem>Select all</MenuItem>
13
+ * {/snippet}
14
+ * </MenuCommandBar>
15
+ * <MenuItem>Paste</MenuItem>
16
+ * </MenuList>
17
+ * ```
18
+ */
19
+ declare const MenuCommandBar: import("svelte").Component<MenuCommandBarProps, {}, "ref">;
20
+ type MenuCommandBar = ReturnType<typeof MenuCommandBar>;
21
+ export default MenuCommandBar;
@@ -6,11 +6,14 @@
6
6
  const COMPONENT_NAME = 'menu-group';
7
7
  const ID = `${PREFIX}${COMPONENT_NAME}-${FALLBACK_ID}`;
8
8
 
9
- let { ref = $bindable(), header, children }: MenuGroupProps = $props();
9
+ let { ref = $bindable(null), header, children }: MenuGroupProps = $props();
10
10
  </script>
11
11
 
12
- <div class="fs-menu-group" role="group" aria-labelledby="group-name-{ID}" bind:this={ref}>
13
- <h3 id="group-name-{ID}" class="group-name">{header || ID}</h3>
12
+ <div class="fs-menu-group" role="group" aria-labelledby={header ? `group-name-${ID}` : undefined} bind:this={ref}>
13
+ {#if header}
14
+ <!-- A menu holds only items, groups and separators, so the label is plain text that names the group. -->
15
+ <div id="group-name-{ID}" class="group-name" role="presentation">{header}</div>
16
+ {/if}
14
17
  {@render children()}
15
18
  </div>
16
19
 
@@ -12,11 +12,11 @@
12
12
  const ID = `${PREFIX}${COMPONENT_NAME}-${FALLBACK_ID}`;
13
13
 
14
14
  let {
15
- ref = $bindable(),
15
+ ref = $bindable(null),
16
16
  icon: Icon,
17
17
  secondaryContent,
18
18
  children,
19
- checked = $bindable(),
19
+ checked = $bindable(null),
20
20
  disabled,
21
21
  class: classes,
22
22
  id = ID,
@@ -24,6 +24,7 @@
24
24
  value = id as string,
25
25
  subText,
26
26
  onclick,
27
+ onkeydown,
27
28
  ...attributes
28
29
  }: MenuItemActionableProps = $props();
29
30
 
@@ -61,6 +62,7 @@
61
62
  ```
62
63
  -->
63
64
  <Button
65
+ {...attributes}
64
66
  as="button"
65
67
  {disabled}
66
68
  appearance="subtle"
@@ -69,11 +71,10 @@
69
71
  invokeHandlers(e, [e.key !== 'Enter', !!disabled], [onkeydown, () => toggleCheckbox(e, value, name)])}
70
72
  aria-checked={checked}
71
73
  role="menuitemcheckbox"
72
- style={`--margin-left: ${hasIcons && !Icon ? 1.5 : 0}rem;}`}
74
+ style="{attributes.style ?? ''}; --margin-left: {hasIcons && !Icon ? 1.5 : 0}rem;"
73
75
  aria-label={secondaryContent ? `${secondaryContent}, Use ${secondaryContent}` : undefined}
74
76
  class="fs-menu-item-checkbox {hasIcons || hasCheckmarks ? 'align' : ''} {checked ? 'checked' : ''} {classes}"
75
77
  bind:ref
76
- {...attributes}
77
78
  >
78
79
  <span class="label" aria-label="{secondaryContent}, Use {secondaryContent}">
79
80
  <CheckmarkFilled class="indicator" />
@@ -12,17 +12,18 @@
12
12
  const ID = `${PREFIX}${COMPONENT_NAME}-${FALLBACK_ID}`;
13
13
 
14
14
  let {
15
- ref = $bindable(),
15
+ ref = $bindable(null),
16
16
  icon: Icon,
17
17
  secondaryContent,
18
18
  children,
19
- checked = $bindable(),
19
+ checked = $bindable(null),
20
20
  disabled,
21
21
  class: classes,
22
22
  id = ID,
23
23
  name = id as string,
24
24
  value = id as string,
25
25
  onclick,
26
+ onkeydown,
26
27
  subText,
27
28
  ...attributes
28
29
  }: MenuItemActionableProps = $props();
@@ -61,6 +62,7 @@
61
62
  ```
62
63
  -->
63
64
  <Button
65
+ {...attributes}
64
66
  {disabled}
65
67
  appearance="subtle"
66
68
  onclick={(e: MouseEvent) =>
@@ -73,11 +75,10 @@
73
75
  )}
74
76
  aria-checked={checked}
75
77
  role="menuitemradio"
76
- style={`--margin-left: ${hasIcons && !Icon ? 1.5 : 0}rem;}`}
78
+ style="{attributes.style ?? ''}; --margin-left: {hasIcons && !Icon ? 1.5 : 0}rem;"
77
79
  aria-label={secondaryContent ? `${secondaryContent}, Use ${secondaryContent}` : undefined}
78
80
  class="fs-menu-item-radio {hasIcons ? 'align' : ''} {checked ? 'checked' : ''} {classes}"
79
81
  bind:ref
80
- {...attributes}
81
82
  >
82
83
  <span class="label" aria-label="{secondaryContent}, Use {secondaryContent}">
83
84
  <CheckmarkFilled class="indicator" />
@@ -11,11 +11,11 @@
11
11
  const ID = `${PREFIX}${COMPONENT_NAME}-${FALLBACK_ID}`;
12
12
 
13
13
  let {
14
- ref = $bindable(),
14
+ ref = $bindable(null),
15
15
  icon: Icon,
16
16
  secondaryContent,
17
17
  children,
18
- checked = $bindable(),
18
+ checked = $bindable(null),
19
19
  disabled,
20
20
  id = ID,
21
21
  class: classes,
@@ -23,6 +23,7 @@
23
23
  value = id as string,
24
24
  subText,
25
25
  onclick,
26
+ onkeydown,
26
27
  ...attributes
27
28
  }: MenuItemActionableProps = $props();
28
29
 
@@ -62,6 +63,7 @@
62
63
  ```
63
64
  -->
64
65
  <Button
66
+ {...attributes}
65
67
  {disabled}
66
68
  {id}
67
69
  appearance="subtle"
@@ -70,11 +72,10 @@
70
72
  invokeHandlers(e, [e.key !== 'Enter', !!disabled], [onkeydown, () => toggleCheckbox(e, value, name)])}
71
73
  aria-checked={checked}
72
74
  role="menuitemcheckbox"
73
- style={`--margin-left: ${marginLeft}rem;}`}
75
+ style="{attributes.style ?? ''}; --margin-left: {marginLeft}rem;"
74
76
  aria-label={secondaryContent ? `${secondaryContent}, Use ${secondaryContent}` : undefined}
75
77
  class="fs-menu-item-switch {hasIcons || hasCheckmarks ? 'align' : ''} {checked ? 'checked' : ''} {classes}"
76
78
  bind:ref
77
- {...attributes}
78
79
  >
79
80
  <span class="label" aria-label="{secondaryContent}, Use {secondaryContent}">
80
81
  {#if Icon}
@@ -4,14 +4,14 @@
4
4
  import { PREFIX } from '../../internal/constants.js';
5
5
  import { invokeHandlers, RenderSoC } from '../../internal/index.js';
6
6
  import type { MenuItemProps } from './types.js';
7
- import type { ComponentProps } from 'svelte';
7
+ import type { ButtonProps } from '../button/types.js';
8
8
 
9
9
  const FALLBACK_ID = $props.id();
10
10
  const COMPONENT_NAME = 'menu-item';
11
11
  const ID = `${PREFIX}${COMPONENT_NAME}-${FALLBACK_ID}`;
12
12
 
13
13
  let {
14
- ref = $bindable(),
14
+ ref = $bindable(null),
15
15
  icon: Icon,
16
16
  secondaryContent,
17
17
  children,
@@ -23,7 +23,7 @@
23
23
  onkeydown,
24
24
  subText,
25
25
  ...attributes
26
- }: MenuItemProps & ComponentProps<typeof Button> = $props();
26
+ }: MenuItemProps & ButtonProps<'a' | 'button'> = $props();
27
27
 
28
28
  const context = getMenuContext();
29
29
 
@@ -54,6 +54,7 @@
54
54
  ```
55
55
  -->
56
56
  <Button
57
+ {...attributes}
57
58
  {href}
58
59
  {disabled}
59
60
  {id}
@@ -63,10 +64,9 @@
63
64
  onkeydown={(e: KeyboardEvent) =>
64
65
  invokeHandlers(e, [e.key !== 'Enter', persistOnItemActivation, disabled], [onkeydown, toggle])}
65
66
  as={href ? 'a' : 'button'}
66
- style={`--margin-left: ${marginLeft}rem;`}
67
+ style="{attributes.style ?? ''}; --margin-left: {marginLeft}rem;"
67
68
  class="fs-menu-item {hasIcons || hasCheckmarks ? 'align' : ''} {classes}"
68
69
  bind:ref
69
- {...attributes}
70
70
  >
71
71
  <span class="label" aria-label={secondaryContent ? `${secondaryContent}, Use ${secondaryContent}` : undefined}>
72
72
  {#if Icon}
@@ -1,7 +1,6 @@
1
- import { Button } from '../../index.js';
2
1
  import type { MenuItemProps } from './types.ts';
3
- import type { ComponentProps } from 'svelte';
4
- type $$ComponentProps = MenuItemProps & ComponentProps<typeof Button>;
2
+ import type { ButtonProps } from '../button/types.ts';
3
+ type $$ComponentProps = MenuItemProps & ButtonProps<'a' | 'button'>;
5
4
  /**
6
5
  * - Usage:
7
6
  * ```tsx
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
- import { getTabspotAttributes } from 'tabspot';
2
+ import { getTabspotAttributes, setTabspotAttributes, unsetTabspotSection } from 'tabspot';
3
+ import type { Attachment } from 'svelte/attachments';
3
4
  import { getMenuContext } from './menu.svelte.js';
4
5
 
5
6
  let { children } = $props();
@@ -7,6 +8,13 @@
7
8
  const context = getMenuContext();
8
9
 
9
10
  let isSubMenu = $derived(context?.config.isSubMenu);
11
+
12
+ // The list mounts when the menu opens, after Tabspot has scanned the page, so the root menu
13
+ // registers itself to get arrow key navigation. A submenu is a grouper inside it instead.
14
+ const registerRoot: Attachment<HTMLElement> = (element) => {
15
+ setTabspotAttributes({ element, config: { root: {} }, merge: true });
16
+ return () => unsetTabspotSection(element, 'root');
17
+ };
10
18
  </script>
11
19
 
12
20
  <div
@@ -21,6 +29,7 @@
21
29
  }
22
30
  : undefined
23
31
  })}
32
+ {@attach isSubMenu ? undefined : registerRoot}
24
33
  >
25
34
  {@render children()}
26
35
  </div>
@@ -10,7 +10,7 @@
10
10
 
11
11
  let {
12
12
  ref: _ref,
13
- open: _open = $bindable(),
13
+ open: _open = $bindable(null),
14
14
  keepMounted = false,
15
15
  placement,
16
16
  positionConfig: floatingPosition = {
@@ -45,11 +45,15 @@
45
45
 
46
46
  let { state: _state } = context;
47
47
 
48
- // The `placement` prop wins over the one coming from the config
48
+ // The `placement` prop wins over the one coming from the config. The strategy is always set, since the CSS position follows it.
49
49
  let config = $derived.by(() => {
50
50
  const base = positionConfig ?? floatingPosition;
51
51
 
52
- return { ...base, placement: placement ?? base.placement ?? (isSubMenu ? 'right-start' : 'bottom-start') };
52
+ return {
53
+ ...base,
54
+ strategy: base.strategy ?? 'fixed',
55
+ placement: placement ?? base.placement ?? (isSubMenu ? 'right-start' : 'bottom-start')
56
+ };
53
57
  });
54
58
 
55
59
  let boundingElement = reactiveBoundingRect();
@@ -93,7 +97,7 @@
93
97
  hidden={!opened}
94
98
  onmouseleave={openOnHover ? (e: MouseEvent) => debounced(e) : undefined}
95
99
  {@attach opened && ref ? floating(ref, config) : undefined}
96
- style={`--position=${isSubMenu ? 'absolute' : 'fixed'} --min-width=${isRefAnElement && !isSubMenu ? `${boundingElement.rect.width}px` : 'max-content'}`}
100
+ style={`--position: ${config.strategy}; --min-width: ${isRefAnElement && !isSubMenu ? `${boundingElement.rect.width}px` : 'max-content'};`}
97
101
  in:flyToOffset={opened && isRefAnElement
98
102
  ? {
99
103
  ...getIntroTransition({ config, anchor: boundingElement.rect, element, ref, isSubMenu }),
@@ -119,7 +123,7 @@
119
123
  z-index: 110;
120
124
  & :global(.fs-flyout),
121
125
  & :global(.fs-flyout::before) {
122
- border-radius: var(--fs-control-border-radius);
126
+ border-radius: var(--fs-control-overlay-border-radius);
123
127
  }
124
128
  & :global(.fs-flyout) {
125
129
  padding: 0;
@@ -55,7 +55,8 @@
55
55
  return;
56
56
  }
57
57
 
58
- if (target === _state.ref) {
58
+ // A click on the icon inside the trigger is a click on the trigger.
59
+ if (target === _state.ref || (_state.ref instanceof Node && _state.ref.contains(target))) {
59
60
  _state.locked = !_state.locked;
60
61
  toggle(e);
61
62
  // Focus the first button in the popover when opening
@@ -75,3 +75,4 @@ export const getIntroTransition = ({ config, anchor, element, ref, isSubMenu = f
75
75
  css: `top: ${top}px; left: ${left}px;`
76
76
  };
77
77
  };
78
+ export const [getMenuCommandBarContext, setMenuCommandBarContext] = createFSContext();
@@ -1,8 +1,8 @@
1
1
  import type { ComputePositionConfig, VirtualElement } from '@floating-ui/dom';
2
2
  import type { FSContext } from '../../internal/index.js';
3
- import type { Component, ComponentProps, Snippet } from 'svelte';
3
+ import type { Component, Snippet } from 'svelte';
4
4
  import type { NoUndefinedField, Shapes } from '../../types/index.js';
5
- import type { Button } from '../../index.ts';
5
+ import type { AriaAttributes, HTMLAttributes, HTMLButtonAttributes } from 'svelte/elements';
6
6
  import type { ButtonProps } from '../button/types.ts';
7
7
  export type MenuContext = FSContext<{
8
8
  id: string;
@@ -62,11 +62,11 @@ export type MenuProps = {
62
62
  /** @propsmith MenuPopoverProps */
63
63
  export type MenuPopoverProps = {
64
64
  /** The element used to position the popover. */
65
- ref?: HTMLElement;
65
+ ref?: HTMLElement | null;
66
66
  /** Controls the open state of the popover.
67
67
  * @bindable
68
68
  */
69
- open?: boolean;
69
+ open?: boolean | null;
70
70
  /** Keeps the popover mounted in the DOM while it is closed.
71
71
  * @default false
72
72
  */
@@ -106,7 +106,7 @@ export type MenuGroupProps = {
106
106
  /** The DOM reference of the group element.
107
107
  * @bindable
108
108
  */
109
- ref?: HTMLDivElement;
109
+ ref?: HTMLDivElement | null;
110
110
  /** The label of the group. */
111
111
  header?: string;
112
112
  /** The items of the group. */
@@ -136,7 +136,7 @@ export type MenuItemActionableProps = {
136
136
  /** Whether the item is checked.
137
137
  * @bindable
138
138
  */
139
- checked?: boolean;
139
+ checked?: boolean | null;
140
140
  /** The value reported to the menu when the item is checked. Falls back to the id of the item.
141
141
  * @default id
142
142
  */
@@ -145,4 +145,59 @@ export type MenuItemActionableProps = {
145
145
  * @default id
146
146
  */
147
147
  name?: string;
148
- } & Omit<MenuItemProps, 'href'> & ComponentProps<typeof Button<'div'>>;
148
+ } & Omit<MenuItemProps, 'href'> & ButtonProps<'button'>;
149
+ /** @propsmith MenuCommandBarProps */
150
+ export type MenuCommandBarProps = {
151
+ /** The DOM reference of the command bar element.
152
+ * @bindable
153
+ */
154
+ ref?: HTMLDivElement | null;
155
+ /** Where the bar sits in the menu. A bar at the top has its divider below it, and one at the bottom has it above.
156
+ * @type 'top' | 'bottom'
157
+ * @default 'top'
158
+ */
159
+ position?: 'top' | 'bottom';
160
+ /** The accessible name of the group of commands.
161
+ * @default 'Commands'
162
+ */
163
+ label?: string;
164
+ /** The accessible name of the button that opens the overflow.
165
+ * @default 'More options'
166
+ */
167
+ overflowLabel?: string;
168
+ /** The secondary commands. When present, a "more" button at the end of the bar opens them in a submenu. */
169
+ overflow?: Snippet;
170
+ /** Shows the label of every command below its icon. Without labels, every command is a square.
171
+ * @default false
172
+ */
173
+ showLabels?: boolean;
174
+ /** Draws a divider between every two commands.
175
+ * @default false
176
+ */
177
+ showDividers?: boolean;
178
+ /** The primary commands, as `MenuCommandBarItem`s. */
179
+ children?: Snippet;
180
+ } & Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'role'>;
181
+ /** @propsmith MenuCommandBarItemProps */
182
+ export type MenuCommandBarItemProps = {
183
+ /** The DOM reference of the button element.
184
+ * @bindable
185
+ */
186
+ ref?: HTMLButtonElement | null;
187
+ /** The icon of the command.
188
+ * @type Snippet | Component
189
+ */
190
+ icon: Snippet<[{
191
+ class: string;
192
+ }]> | Component<{
193
+ class: string;
194
+ }>;
195
+ /** The name of the command. Without visible labels it is the accessible name of the button and shows as its tooltip; with them it shows below the icon. */
196
+ label: string;
197
+ /** Disables the user interaction. */
198
+ disabled?: boolean;
199
+ /** Called when the command is activated. The menu closes afterwards, unless it persists on item activation.
200
+ * @type (e: MouseEvent) => void
201
+ */
202
+ onclick?: (e: MouseEvent) => void;
203
+ } & Omit<AriaAttributes, 'aria-label'> & Pick<HTMLButtonAttributes, 'id' | 'class' | 'onkeydown' | 'onfocus' | 'onblur'>;
@@ -9,7 +9,7 @@ The NumberBox component allows users to input numeric values with specified cons
9
9
  import { NumberBox } from 'fluentui-svelte';
10
10
  </script>
11
11
 
12
- <NumberBox value={0} min={0} max={100} step={1} />
12
+ <NumberBox aria-label="Quantity" value={0} min={0} max={100} step={1} />
13
13
  ```
14
14
 
15
15
  ## Component API
@@ -23,10 +23,12 @@ A brief explanation of the props that really need explaining. You can see the re
23
23
  The variant prop determines the visual style of the NumberBox.
24
24
 
25
25
  ```svelte
26
- <NumberBox hideActionButtons min={0} max={100} step={1} />
27
- <NumberBox hideActionButtons min={0} max={100} step={1} variant="compact" />
26
+ <NumberBox aria-label="Quantity" hideActionButtons min={0} max={100} step={1} />
27
+ <NumberBox aria-label="Quantity" hideActionButtons min={0} max={100} step={1} variant="compact" />
28
28
  ```
29
29
 
30
+ HTML attributes, including class, id and ARIA attributes, apply to the control element. Bind ref to access that control. Use wrapperAttributes and wrapperRef for the outer container.
31
+
30
32
  ## Component Props
31
33
 
32
34
  <!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
@@ -34,7 +36,7 @@ The variant prop determines the visual style of the NumberBox.
34
36
 
35
37
  | Name | Type | Default | Description |
36
38
  | ----------------------------- | ----------------------------- | ---------- | ----------------------------------------------------- |
37
- | `value` _bindable_ | `number` | | The current value of the input. |
39
+ | `value` _bindable_ | `number` &#124; `null` | | The current value of the input. |
38
40
  | `variant` | `'inline'` &#124; `'compact'` | `'inline'` | How the increment and decrement buttons are laid out. |
39
41
  | `TextBoxProps` without `type` | | | |
40
42