fluentui-svelte 0.6.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 (243) 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 +2 -2
  63. package/dist/components/dialog/dialog-content.svelte +2 -2
  64. package/dist/components/dialog/dialog-surface.svelte +102 -9
  65. package/dist/components/dialog/dialog-title.svelte +2 -2
  66. package/dist/components/dialog/dialog-trigger.svelte +2 -2
  67. package/dist/components/dialog/dialog.svelte +22 -11
  68. package/dist/components/dialog/docs/llms.md +56 -19
  69. package/dist/components/dialog/types.d.ts +25 -7
  70. package/dist/components/divider/divider.svelte +6 -4
  71. package/dist/components/divider/types.d.ts +1 -1
  72. package/dist/components/dropdown/docs/llms.md +30 -28
  73. package/dist/components/dropdown/dropdown-option.svelte +17 -5
  74. package/dist/components/dropdown/dropdown.svelte +182 -84
  75. package/dist/components/dropdown/dropdown.svelte.d.ts +1 -1
  76. package/dist/components/dropdown/types.d.ts +26 -20
  77. package/dist/components/dropdown-select/docs/llms.md +24 -22
  78. package/dist/components/dropdown-select/dropdown-select-option.svelte +2 -2
  79. package/dist/components/dropdown-select/dropdown-select.svelte +42 -7
  80. package/dist/components/dropdown-select/types.d.ts +4 -4
  81. package/dist/components/expander/docs/llms.md +3 -3
  82. package/dist/components/expander/expander.svelte +13 -8
  83. package/dist/components/expander/types.d.ts +5 -5
  84. package/dist/components/flyout/docs/llms.md +1 -1
  85. package/dist/components/flyout/flyout.svelte +17 -6
  86. package/dist/components/flyout/types.d.ts +1 -1
  87. package/dist/components/hyperlink/docs/llms.md +6 -6
  88. package/dist/components/hyperlink/hyperlink.svelte +13 -4
  89. package/dist/components/hyperlink/types.d.ts +1 -1
  90. package/dist/components/info-bar/docs/llms.md +11 -10
  91. package/dist/components/info-bar/info-bar.svelte +3 -2
  92. package/dist/components/info-bar/types.d.ts +4 -0
  93. package/dist/components/label/docs/llms.md +75 -22
  94. package/dist/components/label/label-context.d.ts +14 -0
  95. package/dist/components/label/label-context.js +10 -0
  96. package/dist/components/label/label.svelte +153 -30
  97. package/dist/components/label/label.svelte.d.ts +1 -1
  98. package/dist/components/label/types.d.ts +48 -4
  99. package/dist/components/list-view/docs/llms.md +103 -33
  100. package/dist/components/list-view/list-view-item-branch.svelte +257 -0
  101. package/dist/components/list-view/list-view-item-branch.svelte.d.ts +4 -0
  102. package/dist/components/list-view/list-view-item.svelte +164 -46
  103. package/dist/components/list-view/list-view-item.svelte.d.ts +5 -5
  104. package/dist/components/list-view/list-view.svelte +73 -25
  105. package/dist/components/list-view/types.d.ts +71 -8
  106. package/dist/components/list-view/utils.d.ts +10 -0
  107. package/dist/components/list-view/utils.js +17 -1
  108. package/dist/components/menu/docs/llms.md +98 -15
  109. package/dist/components/menu/menu-command-bar-item.svelte +61 -0
  110. package/dist/components/menu/menu-command-bar-item.svelte.d.ts +14 -0
  111. package/dist/components/menu/menu-command-bar.svelte +218 -0
  112. package/dist/components/menu/menu-command-bar.svelte.d.ts +21 -0
  113. package/dist/components/menu/menu-group.svelte +6 -3
  114. package/dist/components/menu/menu-item-checkbox.svelte +5 -4
  115. package/dist/components/menu/menu-item-radio.svelte +5 -4
  116. package/dist/components/menu/menu-item-switch.svelte +5 -4
  117. package/dist/components/menu/menu-item.svelte +5 -5
  118. package/dist/components/menu/menu-item.svelte.d.ts +2 -3
  119. package/dist/components/menu/menu-list.svelte +10 -1
  120. package/dist/components/menu/menu-popover.svelte +9 -5
  121. package/dist/components/menu/menu-trigger.svelte +2 -1
  122. package/dist/components/menu/menu.svelte.js +1 -0
  123. package/dist/components/menu/types.d.ts +62 -7
  124. package/dist/components/number-box/docs/llms.md +6 -4
  125. package/dist/components/number-box/number-box.svelte +35 -29
  126. package/dist/components/number-box/number-box.svelte.d.ts +1 -1
  127. package/dist/components/number-box/types.d.ts +1 -1
  128. package/dist/components/persona/docs/llms.md +29 -25
  129. package/dist/components/persona/persona.svelte +8 -6
  130. package/dist/components/persona/types.d.ts +4 -3
  131. package/dist/components/progress-bar/docs/llms.md +13 -13
  132. package/dist/components/progress-bar/progress-bar.svelte +6 -6
  133. package/dist/components/progress-bar/types.d.ts +4 -4
  134. package/dist/components/progress-ring/docs/llms.md +14 -14
  135. package/dist/components/progress-ring/progress-ring.svelte +5 -5
  136. package/dist/components/progress-ring/types.d.ts +3 -3
  137. package/dist/components/radio-button/docs/llms.md +11 -8
  138. package/dist/components/radio-button/radio-button.svelte +38 -12
  139. package/dist/components/radio-button/radio-button.svelte.d.ts +1 -1
  140. package/dist/components/radio-button/types.d.ts +11 -8
  141. package/dist/components/rating/docs/llms.md +15 -14
  142. package/dist/components/rating/rating.svelte +66 -29
  143. package/dist/components/rating/rating.svelte.d.ts +1 -1
  144. package/dist/components/rating/types.d.ts +8 -8
  145. package/dist/components/side-nav/docs/llms.md +110 -0
  146. package/dist/components/side-nav/side-nav-branch.svelte +43 -0
  147. package/dist/components/side-nav/side-nav-branch.svelte.d.ts +4 -0
  148. package/dist/components/side-nav/side-nav-item.svelte +75 -0
  149. package/dist/components/side-nav/side-nav-item.svelte.d.ts +25 -0
  150. package/dist/components/side-nav/side-nav-list.svelte +32 -0
  151. package/dist/components/side-nav/side-nav-list.svelte.d.ts +7 -0
  152. package/dist/components/side-nav/side-nav.d.ts +2 -0
  153. package/dist/components/side-nav/side-nav.js +2 -0
  154. package/dist/components/side-nav/side-nav.svelte +355 -0
  155. package/dist/components/side-nav/side-nav.svelte.d.ts +24 -0
  156. package/dist/components/side-nav/types.d.ts +149 -0
  157. package/dist/components/side-nav/types.js +1 -0
  158. package/dist/components/skeleton/docs/llms.md +1 -1
  159. package/dist/components/skeleton/skeleton.svelte +2 -2
  160. package/dist/components/skeleton/types.d.ts +1 -1
  161. package/dist/components/slider/docs/llms.md +31 -28
  162. package/dist/components/slider/slider.svelte +126 -61
  163. package/dist/components/slider/slider.svelte.d.ts +1 -3
  164. package/dist/components/slider/types.d.ts +21 -14
  165. package/dist/components/tab-view/docs/llms.md +7 -7
  166. package/dist/components/tab-view/tab-view-tab.svelte +16 -14
  167. package/dist/components/tab-view/tab-view.svelte +16 -9
  168. package/dist/components/tab-view/types.d.ts +6 -6
  169. package/dist/components/table/docs/llms.md +38 -33
  170. package/dist/components/table/table-body.svelte +2 -2
  171. package/dist/components/table/table-cell-actions.svelte +8 -2
  172. package/dist/components/table/table-cell-layout.svelte +2 -2
  173. package/dist/components/table/table-cell.svelte +2 -2
  174. package/dist/components/table/table-header-cell.svelte +2 -2
  175. package/dist/components/table/table-header.svelte +2 -2
  176. package/dist/components/table/table-row.svelte +2 -2
  177. package/dist/components/table/table-selection-cell.svelte +6 -6
  178. package/dist/components/table/table.svelte +2 -2
  179. package/dist/components/table/types.d.ts +11 -11
  180. package/dist/components/tag/docs/llms.md +26 -26
  181. package/dist/components/tag/tag-group.svelte +2 -2
  182. package/dist/components/tag/tag.svelte +13 -12
  183. package/dist/components/tag/types.d.ts +6 -4
  184. package/dist/components/tag-picker/docs/llms.md +61 -55
  185. package/dist/components/tag-picker/option-group.svelte +17 -4
  186. package/dist/components/tag-picker/option-group.svelte.d.ts +1 -1
  187. package/dist/components/tag-picker/option.svelte +15 -4
  188. package/dist/components/tag-picker/tag-picker.svelte +137 -65
  189. package/dist/components/tag-picker/tag-picker.svelte.d.ts +2 -2
  190. package/dist/components/tag-picker/types.d.ts +36 -16
  191. package/dist/components/text-box/docs/llms.md +31 -4
  192. package/dist/components/text-box/text-box-button.svelte +3 -3
  193. package/dist/components/text-box/text-box-button.svelte.d.ts +1 -1
  194. package/dist/components/text-box/text-box.svelte +41 -14
  195. package/dist/components/text-box/types.d.ts +5 -5
  196. package/dist/components/textarea/docs/llms.md +4 -3
  197. package/dist/components/textarea/textarea.svelte +14 -6
  198. package/dist/components/textarea/types.d.ts +2 -4
  199. package/dist/components/time-picker/docs/llms.md +18 -15
  200. package/dist/components/time-picker/time-picker.svelte +133 -33
  201. package/dist/components/time-picker/time-picker.svelte.d.ts +1 -1
  202. package/dist/components/time-picker/types.d.ts +23 -13
  203. package/dist/components/title-bar/docs/llms.md +382 -0
  204. package/dist/components/title-bar/title-bar-left-controls.svelte +215 -0
  205. package/dist/components/title-bar/title-bar-left-controls.svelte.d.ts +22 -0
  206. package/dist/components/title-bar/title-bar-right-controls.svelte +190 -0
  207. package/dist/components/title-bar/title-bar-right-controls.svelte.d.ts +27 -0
  208. package/dist/components/title-bar/title-bar.d.ts +3 -0
  209. package/dist/components/title-bar/title-bar.js +4 -0
  210. package/dist/components/title-bar/title-bar.svelte +224 -0
  211. package/dist/components/title-bar/title-bar.svelte.d.ts +26 -0
  212. package/dist/components/title-bar/types.d.ts +165 -0
  213. package/dist/components/title-bar/types.js +1 -0
  214. package/dist/components/toggle-switch/docs/llms.md +11 -9
  215. package/dist/components/toggle-switch/toggle-switch.svelte +23 -14
  216. package/dist/components/toggle-switch/toggle-switch.svelte.d.ts +1 -1
  217. package/dist/components/toggle-switch/types.d.ts +12 -10
  218. package/dist/components/tooltip/docs/llms.md +1 -1
  219. package/dist/components/tooltip/tooltip.svelte +5 -4
  220. package/dist/components/tooltip/types.d.ts +1 -1
  221. package/dist/components/top-nav/docs/llms.md +1 -1
  222. package/dist/components/top-nav/top-nav-item.svelte +2 -2
  223. package/dist/components/top-nav/top-nav.svelte +2 -2
  224. package/dist/components/top-nav/types.d.ts +2 -2
  225. package/dist/components/tree-view/docs/llms.md +11 -11
  226. package/dist/components/tree-view/tree-view-item-content.svelte +3 -3
  227. package/dist/components/tree-view/tree-view-item.svelte +13 -5
  228. package/dist/components/tree-view/tree-view.svelte +20 -5
  229. package/dist/components/tree-view/tree-view.svelte.d.ts +1 -1
  230. package/dist/components/tree-view/tree-view.svelte.js +31 -3
  231. package/dist/components/tree-view/types.d.ts +7 -3
  232. package/dist/index.d.ts +18 -0
  233. package/dist/index.js +20 -0
  234. package/dist/internal/components/dynamic-carousel/dynamic-carousel.svelte +26 -5
  235. package/dist/internal/components/dynamic-carousel/dynamic-carousel.svelte.d.ts +2 -0
  236. package/dist/internal/index.d.ts +1 -0
  237. package/dist/internal/index.js +1 -0
  238. package/dist/internal/native-field.svelte.d.ts +44 -0
  239. package/dist/internal/native-field.svelte.js +85 -0
  240. package/dist/internal/utils.d.ts +16 -2
  241. package/dist/internal/utils.js +25 -3
  242. package/dist/types/index.d.ts +3 -3
  243. package/package.json +50 -36
@@ -3,12 +3,14 @@
3
3
 
4
4
  let {
5
5
  as = 'div' as Tag,
6
- ref = $bindable(),
6
+ ref = $bindable(null),
7
7
  vertical,
8
8
  alignContent = 'center',
9
9
  appearance = 'default',
10
10
  inset,
11
11
  children,
12
+ class: classes,
13
+ style,
12
14
  ...attributes
13
15
  }: DividerProps<Tag> = $props();
14
16
  </script>
@@ -30,12 +32,12 @@
30
32
  -->
31
33
  <svelte:element
32
34
  this={as}
35
+ {...attributes}
33
36
  bind:this={ref}
34
37
  role="separator"
35
- class={['fs-divider', appearance, `align-${alignContent}`, { inset, vertical }]}
36
- style=" --divider-margin: {children ? (vertical ? '0.5rem' : '0.75rem') : '0'}"
38
+ class={['fs-divider', appearance, `align-${alignContent}`, { inset, vertical }, classes]}
39
+ style="{style ?? ''}; --divider-margin: {children ? (vertical ? '0.5rem' : '0.75rem') : '0'}"
37
40
  aria-orientation={vertical ? 'vertical' : 'horizontal'}
38
- {...attributes}
39
41
  >
40
42
  {@render children?.()}
41
43
  </svelte:element>
@@ -10,7 +10,7 @@ export type DividerProps<Tag extends DividerGenerics = 'div'> = {
10
10
  * @type HTMLDivElement | HTMLHRElement | HTMLSpanElement
11
11
  * @bindable
12
12
  */
13
- ref?: DividerElement[Tag];
13
+ ref?: DividerElement[Tag] | null;
14
14
  /** Renders the divider vertically instead of horizontally. */
15
15
  vertical?: boolean;
16
16
  /** Where the content of the divider sits along the line.
@@ -9,7 +9,7 @@ The Dropdown lets users select one or more options from a list of `DropdownOptio
9
9
  import { Dropdown, DropdownOption } from 'fluentui-svelte';
10
10
  </script>
11
11
 
12
- <Dropdown>
12
+ <Dropdown aria-label="Choose an option">
13
13
  <DropdownOption value="A" text="Option A">Option A</DropdownOption>
14
14
  <DropdownOption value="B" text="Option B">Option B</DropdownOption>
15
15
  </Dropdown>
@@ -22,13 +22,13 @@ The Dropdown lets users select one or more options from a list of `DropdownOptio
22
22
  Options can render any content, such as a `Persona`. Pass a `text` prop so the closed dropdown can still display the selection.
23
23
 
24
24
  ```svelte
25
- <Dropdown>
25
+ <Dropdown aria-label="Choose an option">
26
26
  <DropdownOption value="John Doe" text="John Doe">
27
27
  <Persona
28
28
  name="John Doe"
29
29
  primaryText="Software Engineer"
30
30
  presence={{ status: 'available' }}
31
- avatar={{ color: 'colorful', idForColor: '' }}
31
+ avatarProps={{ color: 'colorful', idForColor: '' }}
32
32
  />
33
33
  </DropdownOption>
34
34
  <DropdownOption value="B" text="Option B">Option B</DropdownOption>
@@ -41,38 +41,40 @@ Options can render any content, such as a `Persona`. Pass a `text` prop so the c
41
41
  Set the `multiple` prop to allow selecting more than one option.
42
42
 
43
43
  ```svelte
44
- <Dropdown multiple>
44
+ <Dropdown multiple aria-label="Choose options">
45
45
  <DropdownOption value="A" text="Option A">Option A</DropdownOption>
46
46
  <DropdownOption value="B" text="Option B">Option B</DropdownOption>
47
47
  <DropdownOption value="C" text="Option C">Option C</DropdownOption>
48
48
  </Dropdown>
49
49
  ```
50
50
 
51
+ 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.
52
+
51
53
  ## Component Props
52
54
 
53
55
  <!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
54
56
  <!-- props:DropdownProps -->
55
57
 
56
- | Name | Type | Default | Description |
57
- | ---------------------------------------------------- | -------------------------- | -------------------- | ----------------------------------------------------------------------- |
58
- | `value` _bindable_ | `string` &#124; `string[]` | `multiple ? [] : ''` | The selected value. An array of values when `multiple` is set. |
59
- | `multiple` | `boolean` | | Allows more than one option to be selected at a time. |
60
- | `placeholder` | `string` | `'Select an option'` | The text shown by the trigger while no option is selected. |
61
- | `disabled` | `boolean` | | Disables the user interaction. |
62
- | `name` | `string` &#124; `null` | `id` | The name submitted with the form. Falls back to the id of the dropdown. |
63
- | `ref` _bindable_ | `HTMLDivElement` | | The DOM reference of the dropdown element. |
64
- | `onclick` | `(e: MouseEvent) => void` | | Called when the dropdown is clicked. |
65
- | `wrapperProps` | `HTMLAttributes` | | The attributes to spread on the wrapper element. |
66
- | `buttonRef` _bindable_ | `HTMLButtonElement` | | The DOM reference of the button that opens the dropdown. |
67
- | `buttonProps` | `HTMLButtonAttributes` | | The attributes to spread on the button that opens the dropdown. |
68
- | `inputRef` | `HTMLInputElement` | | The DOM reference of the hidden input holding the value. |
69
- | `flyoutRef` _bindable_ | `HTMLDivElement` | | The DOM reference of the flyout element. |
70
- | `flyoutProps` | `FlyoutProps` | | The props to spread on the flyout. |
71
- | `flyoutMaxHeight` | `string` | | The maximum height of the flyout, as a CSS length. |
72
- | `listboxRef` _bindable_ | `HTMLUListElement` | | The DOM reference of the listbox element. |
73
- | `listboxProps` | `ListViewProps` | | The props to spread on the listbox rendered inside the flyout. |
74
- | `DropdownMultipleValue` &#124; `DropdownSingleValue` | | | |
75
- | `HTMLInputAttributes` without `value`, `onclick` | | | |
58
+ | Name | Type | Default | Description |
59
+ | ---------------------------------------------------- | --------------------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------- |
60
+ | `value` _bindable_ | `string` &#124; `string[]` | `multiple ? [] : ''` | The selected value. An array of values when `multiple` is set. |
61
+ | `multiple` | `boolean` | | Allows more than one option to be selected at a time. |
62
+ | `placeholder` | `string` | `'Select an option'` | The text shown by the trigger while no option is selected. |
63
+ | `disabled` | `boolean` | | Disables the user interaction. |
64
+ | `name` | `string` &#124; `null` | | The name the value is submitted under. Without one, the form leaves the value out, as with any field. |
65
+ | `wrapperRef` _bindable_ | `HTMLDivElement` &#124; `null` | | The DOM reference of the wrapper element. |
66
+ | `wrapperAttributes` | `HTMLAttributes` | | The attributes to spread on the wrapper element. |
67
+ | `ref` _bindable_ | `HTMLSelectElement` &#124; `null` | | The DOM reference of the native select that holds the value. The other attributes of the dropdown are applied to it. |
68
+ | `buttonRef` _bindable_ | `HTMLButtonElement` &#124; `null` | | The DOM reference of the button that opens the dropdown. |
69
+ | `buttonAttributes` | `HTMLButtonAttributes` | | The attributes to spread on the button that opens the dropdown. |
70
+ | `flyoutRef` _bindable_ | `HTMLDivElement` &#124; `null` | | The DOM reference of the flyout element. |
71
+ | `flyoutProps` | `FlyoutProps` | | The props to spread on the flyout. |
72
+ | `flyoutMaxHeight` | `string` | | The maximum height of the flyout, as a CSS length. |
73
+ | `listboxRef` _bindable_ | `HTMLUListElement` &#124; `null` | | The DOM reference of the listbox element. |
74
+ | `listboxProps` | `ListViewProps` | | The props to spread on the listbox rendered inside the flyout. |
75
+ | `children` | `Snippet` | | The `DropdownOption` elements of the list. |
76
+ | `DropdownMultipleValue` &#124; `DropdownSingleValue` | | | |
77
+ | `NativeFieldAttributes<HTMLSelectAttributes>` | | | |
76
78
 
77
79
  <!-- /props:DropdownProps -->
78
80
 
@@ -81,9 +83,9 @@ Set the `multiple` prop to allow selecting more than one option.
81
83
  <!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
82
84
  <!-- props:DropdownOptionProps -->
83
85
 
84
- | Name | Type | Default | Description |
85
- | ------------------------- | -------- | ------- | ------------------------------------------------------------------------------------------------ |
86
- | `text` | `string` | | The text shown by the dropdown once the option is selected. Falls back to the rendered children. |
87
- | `ListViewItemProps<'li'>` | | | |
86
+ | Name | Type | Default | Description |
87
+ | ------------------------- | -------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
88
+ | `text` | `string` | | The text shown by the dropdown once the option is selected. Falls back to the rendered children, which the server cannot read: pass it when rendering on the server. |
89
+ | `ListViewItemProps<'li'>` | | | |
88
90
 
89
91
  <!-- /props:DropdownOptionProps -->
@@ -8,17 +8,29 @@
8
8
  const FALLBACK_ID = $props.id();
9
9
  const ID = `${PREFIX}-dropdown-option-${FALLBACK_ID}`;
10
10
 
11
- let { value, text, disabled, id = ID, ref = $bindable(), children }: DropdownOptionProps = $props();
11
+ let { value, text, disabled, id = ID, ref = $bindable(null), children }: DropdownOptionProps = $props();
12
12
 
13
13
  const context = getDropdownContext();
14
14
 
15
15
  if (!context) throw new Error('DropdownOption must be used within a Dropdown');
16
16
 
17
- // Report the option's label so the trigger can show text instead of the raw value.
17
+ /** The rendered content, which stands in for a missing `text` once there is a page to read it from. */
18
+ let rendered = $state<string>();
19
+
20
+ // Report the option's label and the trigger can show text instead of the raw value. It is reported
21
+ // as the option is created, ahead of the trigger, so the server writes it too.
22
+ const withdraw = context.methods?.registerOption({
23
+ get value() {
24
+ return value;
25
+ },
26
+ get text() {
27
+ return text ?? rendered;
28
+ }
29
+ });
30
+
18
31
  onMount(() => {
19
- if (value == null) return;
20
- const label = text ?? (ref?.textContent?.trim() || String(value));
21
- context.methods?.registerOption(String(value), label);
32
+ rendered = ref?.textContent?.trim() || undefined;
33
+ return withdraw;
22
34
  });
23
35
  </script>
24
36
 
@@ -1,11 +1,13 @@
1
1
  <script lang="ts">
2
+ import { claimLabel } from '../label/label-context.js';
3
+ import { dispatchFieldEvents, invokeHandlers, NativeField } from '../../internal/index.js';
2
4
  import { onClickOutside } from 'runed';
3
5
  import ChevronDownFilled from 'fluentui-icons-svelte/ChevronDownFilled.svelte';
4
6
  import { Flyout, ListView, ListViewItem } from '../../index.js';
5
- import type { DropdownContext, DropdownProps } from './types.js';
7
+ import type { DropdownContext, DropdownOptionLabel, DropdownProps } from './types.js';
6
8
  import { tick } from 'svelte';
7
- import { SvelteMap } from 'svelte/reactivity';
8
- import { setTabspotAttributes, unsetTabspotSection } from 'tabspot';
9
+ import { SvelteSet } from 'svelte/reactivity';
10
+ import { clearTabspotActive, setTabspotActive, setTabspotAttributes, unsetTabspotSection } from 'tabspot';
9
11
  import { PREFIX } from '../../internal/constants.js';
10
12
  import { getGlobalFSContext } from '../../providers/fluentui-svelte/fluentui-svelte.js';
11
13
  import { setDropdownContext } from './dropdown-context.js';
@@ -13,26 +15,39 @@
13
15
  const FALLBACK_ID = $props.id();
14
16
  const ID = `${PREFIX}-dropdown-${FALLBACK_ID}`;
15
17
 
18
+ const labelContext = claimLabel(() => id);
19
+
16
20
  let {
17
- id = ID,
18
- name = id,
21
+ id = labelContext?.fieldId ?? ID,
22
+ name,
19
23
  placeholder = 'Select an option',
20
24
  multiple,
21
25
  value = $bindable(multiple ? [] : ''),
22
26
  disabled,
23
27
  flyoutMaxHeight,
24
- ref = $bindable(),
25
- wrapperProps,
26
- buttonRef = $bindable(),
27
- buttonProps,
28
- flyoutRef = $bindable(),
28
+ wrapperRef = $bindable(null),
29
+ wrapperAttributes,
30
+ ref = $bindable(null),
31
+ buttonRef = $bindable(null),
32
+ buttonAttributes,
33
+ flyoutRef = $bindable(null),
29
34
  flyoutProps,
30
- listboxRef = $bindable(),
35
+ listboxRef = $bindable(null),
31
36
  listboxProps,
32
- onclick,
33
- children
37
+ onfocus,
38
+ onblur,
39
+ children,
40
+ ...attributes
34
41
  }: DropdownProps = $props();
35
42
 
43
+ const BUTTON_ID = $derived(buttonAttributes?.id ?? `${id}-button`);
44
+
45
+ const native = new NativeField(
46
+ () => ref,
47
+ () => buttonRef,
48
+ labelContext
49
+ );
50
+
36
51
  let open = $state(false);
37
52
 
38
53
  let roundClass = $state('bottom');
@@ -41,16 +56,19 @@
41
56
 
42
57
  const OPEN_KEYS = ['ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight', 'Enter', ' '];
43
58
 
44
- // Options only mount while the flyout is open, so we cache the last-known text
45
- // for every value here to keep rendering the selection once the list is closed.
46
- const optionLabels = new SvelteMap<string, string>();
59
+ // The list stays in the page while closed, so every option is here from the start, and is read live.
60
+ const options = new SvelteSet<DropdownOptionLabel>();
61
+ const optionLabels = $derived(new Map([...options].map((option) => [option.value, option.text])));
47
62
 
48
63
  const context: DropdownContext = $state({
49
64
  config: null,
50
65
  state: null,
51
66
  events: null,
52
67
  methods: {
53
- registerOption: (optionValue: string, text: string) => optionLabels.set(optionValue, text)
68
+ registerOption: (option) => {
69
+ options.add(option);
70
+ return () => options.delete(option);
71
+ }
54
72
  }
55
73
  });
56
74
 
@@ -65,8 +83,11 @@
65
83
  );
66
84
 
67
85
  onClickOutside(
68
- () => ref,
69
- () => closeDropdown()
86
+ () => wrapperRef,
87
+ (event) => {
88
+ if (event.target instanceof Element && event.target.closest('label')?.control === ref) return;
89
+ closeDropdown();
90
+ }
70
91
  );
71
92
 
72
93
  function handlePlacementChange(newPlacement: string) {
@@ -77,7 +98,12 @@
77
98
  if (disabled || open) return;
78
99
  open = true;
79
100
  await tick();
80
- globalFSContext?.state?.tabspotInstance?.rebuild(ref);
101
+ globalFSContext?.state?.tabspotInstance?.rebuild(wrapperRef ?? undefined);
102
+ // The cursor starts on the selection, or on the first option, so it is announced on open.
103
+ const start =
104
+ listboxRef?.querySelector<HTMLElement>('.fs-list-view-item[aria-selected="true"]:not([aria-disabled="true"])') ??
105
+ listboxRef?.querySelector<HTMLElement>('.fs-list-view-item:not([aria-disabled="true"])');
106
+ if (start) setTabspotActive(start, { nearest: true });
81
107
  }
82
108
 
83
109
  function closeDropdown() {
@@ -91,6 +117,13 @@
91
117
 
92
118
  const { key } = e;
93
119
 
120
+ if (open && (key === 'Enter' || key === ' ')) {
121
+ e.preventDefault();
122
+ // Focus stays on the button, so the option under the cursor never sees the key: click it here.
123
+ listboxRef?.querySelector<HTMLElement>('[data-active="true"]')?.click();
124
+ return;
125
+ }
126
+
94
127
  if (OPEN_KEYS.includes(key)) {
95
128
  e.preventDefault();
96
129
  await openDropdown();
@@ -103,18 +136,32 @@
103
136
  }
104
137
  }
105
138
 
106
- async function handleClick(e: MouseEvent) {
139
+ async function handleClick() {
107
140
  if (open) closeDropdown();
108
141
  else await openDropdown();
109
- onclick?.(e);
142
+ }
143
+
144
+ /** A label activates the native select it belongs to, which hands the activation to the button. */
145
+ async function openFromField() {
146
+ buttonRef?.focus();
147
+ await openDropdown();
148
+ }
149
+
150
+ function select(items: string[] | undefined) {
151
+ const next = multiple ? (items ?? []) : (items?.[0] ?? '');
152
+ if (!multiple && next === value) return;
153
+ value = next;
154
+ dispatchFieldEvents(ref);
110
155
  }
111
156
 
112
157
  // Force rebuild of tabspot handling tree
113
158
  $effect(() => {
114
- if (!ref) return;
159
+ if (!wrapperRef) return;
115
160
 
116
161
  if (!open) {
117
- unsetTabspotSection(ref, 'root');
162
+ // The list stays in the page once closed: nothing in it reads as chosen.
163
+ clearTabspotActive(wrapperRef);
164
+ unsetTabspotSection(wrapperRef, 'root');
118
165
  return;
119
166
  }
120
167
 
@@ -127,7 +174,7 @@
127
174
  axis: 'vertical',
128
175
  items: '.fs-list-view-item',
129
176
  activation: {
130
- controller: `#${id}-button`,
177
+ controller: `#${CSS.escape(BUTTON_ID)}`,
131
178
  mode: 'activedescendant',
132
179
  mark: {
133
180
  attribute: 'data-active'
@@ -135,83 +182,119 @@
135
182
  }
136
183
  }
137
184
  },
138
- element: ref,
185
+ element: wrapperRef,
139
186
  merge: true
140
187
  });
141
188
  });
142
189
  </script>
143
190
 
144
- <div class={['fs-dropdown', `square-${roundClass}`, { open, disabled }]} bind:this={ref} {...wrapperProps}>
191
+ <div
192
+ {...wrapperAttributes}
193
+ class={['fs-dropdown', `square-${roundClass}`, { open, disabled }, wrapperAttributes?.class]}
194
+ bind:this={wrapperRef}
195
+ >
196
+ <!-- Hidden while closed, not gone: each option reports its text as it is created, and comes before
197
+ the button, so the button shows it before the list ever opens, on the server too. -->
198
+ <Flyout
199
+ {...flyoutProps}
200
+ hidden={!open}
201
+ style="{flyoutProps?.style ?? ''}; {flyoutMaxHeight ? `max-height: ${flyoutMaxHeight};` : ''}"
202
+ offset={0}
203
+ bind:ref={flyoutRef}
204
+ floating
205
+ roundCorners="bottom"
206
+ reference={wrapperRef ?? undefined}
207
+ id={`${id}-flyout`}
208
+ onPlacementChange={(place) => {
209
+ handlePlacementChange(place);
210
+ flyoutProps?.onPlacementChange?.(place);
211
+ }}
212
+ placementConfig={{ allowedPlacements: ['bottom', 'top'] }}
213
+ class="fs-dropdown-flyout {roundClass === 'top' ? 'top-shadow' : ''} {flyoutProps?.class}"
214
+ >
215
+ <ListView
216
+ {...listboxProps}
217
+ bind:ref={listboxRef}
218
+ bind:selectedItems={() => selectedValues, select}
219
+ selectionMode={multiple ? 'multiselect' : 'single'}
220
+ toggleOnClick={multiple}
221
+ onSelectionChange={(event, selected) => {
222
+ if (!multiple) closeDropdown();
223
+ listboxProps?.onSelectionChange?.(event, selected);
224
+ }}
225
+ navigationMode="items"
226
+ id={`${id}-listbox`}
227
+ role="listbox"
228
+ disableTabspot
229
+ >
230
+ {#if !children}
231
+ <ListViewItem disabled>No options available</ListViewItem>
232
+ {:else}
233
+ {@render children?.()}
234
+ {/if}
235
+ </ListView>
236
+ </Flyout>
145
237
  <ChevronDownFilled class="indicator" />
146
238
  <button
239
+ {...buttonAttributes}
240
+ {...native.aria(attributes, buttonAttributes)}
147
241
  bind:this={buttonRef}
148
242
  type="button"
149
243
  role="combobox"
150
- onclick={handleClick}
151
- onkeydown={handleKeyDown}
152
- aria-controls={`${id}-listbox`}
244
+ onclick={(e) => invokeHandlers(e, [disabled], [handleClick, buttonAttributes?.onclick])}
245
+ onkeydown={(e) => invokeHandlers(e, [disabled], [handleKeyDown, buttonAttributes?.onkeydown])}
246
+ onfocus={(e) => invokeHandlers(e, [], [onfocus, buttonAttributes?.onfocus])}
247
+ onblur={(e) => invokeHandlers(e, [], [onblur, buttonAttributes?.onblur])}
248
+ aria-controls={open ? `${id}-listbox` : undefined}
153
249
  aria-expanded={open}
154
- id={`${id}-button`}
250
+ id={BUTTON_ID}
155
251
  aria-haspopup="listbox"
156
252
  {disabled}
157
- class={['fs-dropdown-button', hasValue && 'has-value']}
158
- {...buttonProps}
253
+ class={['fs-dropdown-button', hasValue && 'has-value', buttonAttributes?.class]}
159
254
  >
160
255
  {displayLabel}
161
256
  </button>
162
257
 
163
- <input type="hidden" {name} {value} {id} {disabled} />
164
-
165
- {#if open}
166
- <Flyout
167
- style={flyoutMaxHeight ? `max-height: ${flyoutMaxHeight};` : ''}
168
- offset={0}
169
- bind:ref={flyoutRef}
170
- {...flyoutProps}
171
- floating
172
- roundCorners="bottom"
173
- reference={ref}
174
- id={`${id}-flyout`}
175
- onPlacementChange={(place) => handlePlacementChange(place)}
176
- placementConfig={{ allowedPlacements: ['bottom', 'top'] }}
177
- class="fs-dropdown-flyout {roundClass === 'top' ? 'top-shadow' : ''} {flyoutProps?.class}"
258
+ <!-- The field of the form: submitted, validated and reset like any select input,
259
+ behind the button and out of the tab order, since the button is what people use. See `NativeField`. -->
260
+ <!-- Svelte needs a static `multiple` to bind a select, hence the two branches. This is the native form -->
261
+ {#if multiple}
262
+ <select
263
+ {...attributes}
264
+ class="native-field"
265
+ aria-hidden={native.reported ? undefined : 'true'}
266
+ multiple
267
+ tabindex={-1}
268
+ {id}
269
+ {name}
270
+ {disabled}
271
+ bind:value
272
+ bind:this={ref}
273
+ onclick={openFromField}
178
274
  >
179
- <ListView
180
- bind:ref={listboxRef}
181
- bind:selectedItems={
182
- () => {
183
- if (multiple) {
184
- return (value as string[]) || [];
185
- } else {
186
- return value ? [value as string] : [];
187
- }
188
- },
189
- (v) => {
190
- if (multiple) {
191
- value = v || [];
192
- } else {
193
- value = v?.[0] || '';
194
- }
195
- }
196
- }
197
- selectionMode={multiple ? 'multiselect' : 'single'}
198
- toggleOnClick={multiple}
199
- onSelectionChange={() => {
200
- if (!multiple) closeDropdown();
201
- }}
202
- navigationMode="items"
203
- id={`${id}-listbox`}
204
- role="listbox"
205
- disableTabspot
206
- {...listboxProps}
207
- >
208
- {#if !children}
209
- <ListViewItem disabled>No options available</ListViewItem>
210
- {:else}
211
- {@render children?.()}
212
- {/if}
213
- </ListView>
214
- </Flyout>
275
+ {#each selectedValues as option (option)}
276
+ <option value={option}>{optionLabels.get(option) ?? option}</option>
277
+ {/each}
278
+ </select>
279
+ {:else}
280
+ <select
281
+ {...attributes}
282
+ class="native-field"
283
+ aria-hidden={native.reported ? undefined : 'true'}
284
+ tabindex={-1}
285
+ {id}
286
+ {name}
287
+ {disabled}
288
+ bind:value
289
+ bind:this={ref}
290
+ onclick={openFromField}
291
+ >
292
+ <!-- The placeholder makes an empty value missing for `required`, and is what a reset returns to. -->
293
+ <option value="" hidden></option>
294
+ {#if hasValue}
295
+ <option {value}>{displayLabel}</option>
296
+ {/if}
297
+ </select>
215
298
  {/if}
216
299
  </div>
217
300
 
@@ -300,6 +383,21 @@
300
383
  left: -0.063rem;
301
384
  background-color: var(--fs-accent-fill-default);
302
385
  }
386
+ &:has(> .native-field:is(:user-invalid, [aria-invalid='true'])):after {
387
+ width: calc(100% + 0.125rem);
388
+ left: -0.063rem;
389
+ background-color: var(--fs-system-critical);
390
+ }
391
+ & .native-field {
392
+ position: absolute;
393
+ inset: 0;
394
+ inline-size: 100%;
395
+ block-size: 100%;
396
+ margin: 0;
397
+ box-sizing: border-box;
398
+ opacity: 0;
399
+ pointer-events: none;
400
+ }
303
401
  & :global(> .indicator) {
304
402
  width: 1rem;
305
403
  position: absolute;
@@ -1,4 +1,4 @@
1
1
  import type { DropdownProps } from './types.ts';
2
- declare const Dropdown: import("svelte").Component<DropdownProps, {}, "ref" | "value" | "flyoutRef" | "buttonRef" | "listboxRef">;
2
+ declare const Dropdown: import("svelte").Component<DropdownProps, {}, "ref" | "value" | "wrapperRef" | "flyoutRef" | "buttonRef" | "listboxRef">;
3
3
  type Dropdown = ReturnType<typeof Dropdown>;
4
4
  export default Dropdown;