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
@@ -20,7 +20,14 @@ The ListView is a component for rendering a set of vertically stacked items. The
20
20
 
21
21
  ## ListView API
22
22
 
23
- > **Best practices:** Please check the Fluent UI React List best practices for guidance.
23
+ ### Best practices
24
+
25
+ - Keep lists that only display text out of the Tab sequence. Configure `navigationMode` on the outer ListView: use `items` for native link navigation and `composite` for selectable cards with secondary controls. Let the component manage item and control tabindex unless you opt out with `disableTabspot`.
26
+ - With the default grid roles in selectable composite lists, the component wraps each row's content in one `gridcell` and manages navigation to its controls. No manual `gridcell` wrappers are needed.
27
+ - Use `onAction` for an item's primary action and native buttons or links with `onclick` for secondary actions. Enter and Space both activate non-native actionable items; links retain native keyboard behavior. In composite selection rows, the checkmark changes selection independently, and secondary buttons and links do not trigger the row's action or selection.
28
+ - Keep the default roles for flat selection widgets; they depend on both `selectionMode` and `navigationMode`. Match the HTML tags: `ul`/`ol` with `li` items, or `as="div"` on both a card list and its items.
29
+ - Use ListViewItemBranch for expandable sublists. Branches are never selectable and manage their disclosure attributes and focus. Each nested list owns its selection. Set `role="list"` on each selectable list containing branches to preserve native list semantics.
30
+ - Give selectable ListView widgets an accessible name with `aria-label` or `aria-labelledby`. Give navigation a named `nav` landmark and mark the current link with `aria-current="page"`. Use meaningful visible text or `aria-label` for controls, a descriptive branch `label`, and stable item `value` strings when using selection.
24
31
 
25
32
  ### As
26
33
 
@@ -32,10 +39,50 @@ Instead of setting this on every ListViewItem, you can set it here and every ite
32
39
 
33
40
  ### Navigation Mode
34
41
 
35
- This option allows you to set keyboard navigation based on your needs. For view-only data, you do not need to set this unless the items perform some action. If your items have multiple actionable elements within the ListViewItem, like buttons or menus, you can set this to composite.
42
+ `items` keeps each item in the Tab order; `composite` gives the entire list one tab stop and lets Right enter an item's secondary controls. Selection uses listbox/option roles in items mode, or grid/row/gridcell roles in composite mode. Set `role="list"` on each selectable navigation list with sublists to preserve native list semantics; use `aria-current` on links to announce the current page.
43
+
44
+ ### Keyboard navigation
45
+
46
+ Up/Down move between siblings and Home/End reach the ends of the current level. Right opens a collapsed branch; Right on an expanded branch enters its sublist. Left closes an expanded branch; Left on a collapsed branch or child returns to its parent. Escape closes the nearest branch and restores focus to its button. Right also enters a composite item's controls; Left/Right move between them, and Left from the first control returns to the row. Enter activates links; Enter and Space activate buttons and selectable items. `disableTabspot` opts out of arrow-key focus management. Each nested ListView owns its selection state, so selecting a child does not select its parent.
36
47
 
37
48
  ## Examples
38
49
 
50
+ ### Simple navigation
51
+
52
+ Use native anchors inside a named navigation landmark. Under `ul` or `ol`, ListViewItem wraps anchors and buttons in `li` elements.
53
+
54
+ ```svelte
55
+ <nav aria-label="Main navigation">
56
+ <ListView>
57
+ <ListViewItem as="a" href="/">Home</ListViewItem>
58
+ <ListViewItem as="a" href="/docs">Documentation</ListViewItem>
59
+ <ListViewItem as="a" href="/docs/components/list-view" aria-current="page">ListView</ListViewItem>
60
+ </ListView>
61
+ </nav>
62
+ ```
63
+
64
+ ### Navigation with sublists
65
+
66
+ Nest a ListView inside ListViewItemBranch. Branches are focusable disclosure buttons and never participate in selection. The component manages expanded state, aria-expanded, aria-controls, hidden content and focus restoration. Nested lists remain mounted when collapsed, preserving their selection. Hierarchy, indentation and keyboard groups are detected automatically; every nested list owns its selection. Use native list semantics: when selection is enabled, set `role="list"` on lists containing branches. Branches cannot be placed in listbox or grid lists. See the [WAI-ARIA disclosure pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/).
67
+
68
+ ```svelte
69
+ <script lang="ts">
70
+ import { ListView, ListViewItem, ListViewItemBranch } from 'fluentui-svelte';
71
+ </script>
72
+
73
+ <nav aria-label="Component navigation">
74
+ <ListView>
75
+ <ListViewItem as="a" href="/">Home</ListViewItem>
76
+ <ListViewItemBranch label="Components" expanded>
77
+ <ListView>
78
+ <ListViewItem as="a" href="/docs/components/button">Button</ListViewItem>
79
+ <ListViewItem as="a" href="/docs/components/checkbox">Checkbox</ListViewItem>
80
+ </ListView>
81
+ </ListViewItemBranch>
82
+ </ListView>
83
+ </nav>
84
+ ```
85
+
39
86
  ### Value
40
87
 
41
88
  Sets the value of the current item. You must set this when using the ListView selection system or if items change dynamically.
@@ -49,7 +96,7 @@ This event is triggered when clicking or tapping the element. It executes the pr
49
96
  Use `selectionMode` (`single`, `multiselect`, or `extended`) to control how items are selected.
50
97
 
51
98
  ```svelte
52
- <ListView selectionMode="multiselect" selectedItems={['Apple']}>
99
+ <ListView aria-label="Fruits" selectionMode="multiselect" selectedItems={['Apple']}>
53
100
  {#each ['Apple', 'Banana', 'Cherry'] as item (item)}
54
101
  <ListViewItem value={item}>{item}</ListViewItem>
55
102
  {/each}
@@ -61,7 +108,7 @@ Use `selectionMode` (`single`, `multiselect`, or `extended`) to control how item
61
108
  In a multiselect list, a plain click selects only that item and Ctrl/Cmd + click toggles it. Set `toggleOnClick` to make a plain click toggle the item directly.
62
109
 
63
110
  ```svelte
64
- <ListView selectionMode="multiselect" toggleOnClick>
111
+ <ListView aria-label="Fruits" selectionMode="multiselect" toggleOnClick>
65
112
  {#each ['Apple', 'Banana', 'Cherry'] as item (item)}
66
113
  <ListViewItem value={item}>{item}</ListViewItem>
67
114
  {/each}
@@ -77,7 +124,7 @@ Add and configure a checkbox for the element. You can use the same props for the
77
124
  let selected = $state(['Apple']);
78
125
  </script>
79
126
 
80
- <ListView selectionMode="multiselect" bind:selectedItems={selected}>
127
+ <ListView aria-label="Fruits" selectionMode="multiselect" bind:selectedItems={selected}>
81
128
  {#each ['Apple', 'Banana', 'Cherry'] as item (item)}
82
129
  <ListViewItem value={item} checkmark={{}}>{item}</ListViewItem>
83
130
  {/each}
@@ -88,7 +135,7 @@ Add and configure a checkbox for the element. You can use the same props for the
88
135
 
89
136
  ### Cards with a primary action
90
137
 
91
- With `navigationMode` set to `composite` and a `selectionMode`, every item is a row. Clicking it runs its primary action (`onAction`), the checkbox toggles its selection, and any button inside is a secondary action. Use `checkmark.wrapperAttributes.style` to place the checkbox where you need it, like the top-right corner of the card. Note: every actionable element inside this ListViewItem, except the checkbox, must call e.stopPropagation().
138
+ With `navigationMode` set to `composite` and a `selectionMode`, every item is a row. Use `as="div"` for both the list and its card items to keep the HTML structure valid. Clicking a row runs its primary action (`onAction`), the checkbox toggles its selection, and any button inside is a secondary action. Use `checkmark.wrapperAttributes.style` to place the checkbox where you need it, like the top-right corner of the card. Secondary controls run their own actions without selecting the item or triggering its primary action.
92
139
 
93
140
  ```svelte
94
141
  <script>
@@ -106,13 +153,13 @@ With `navigationMode` set to `composite` and a `selectionMode`, every item is a
106
153
  {#each cards as card (card.name)}
107
154
  {#snippet install()}
108
155
  <Button
109
- onclick={(e) => {
110
- e.stopPropagation();
156
+ onclick={() => {
111
157
  lastAction = 'Installed ' + card.name;
112
158
  }}>Install</Button
113
159
  >
114
160
  {/snippet}
115
161
  <ListViewItem
162
+ as="div"
116
163
  value={card.name}
117
164
  checkmark={{ wrapperAttributes: { style: 'position: absolute; top: 0.5rem; right: 0.5rem; z-index: 2;' } }}
118
165
  style="padding: 0"
@@ -135,18 +182,20 @@ With `navigationMode` set to `composite` and a `selectionMode`, every item is a
135
182
  <!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
136
183
  <!-- props:ListViewProps -->
137
184
 
138
- | Name | Type | Default | Description |
139
- | -------------------------- | --------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------- |
140
- | `selectionMode` | `'none'` &#124; `'single'` &#124; `'multiselect'` &#124; `'extended'` | `'none'` | How many items can be selected, and how the selection is made. |
141
- | `navigationMode` | `'items'` &#124; `'composite'` | `'items'` | Whether the list itself takes a single tab stop, or every item takes its own. |
142
- | `selectedItems` _bindable_ | `string[]` | | The values of the selected items. |
143
- | `shape` | `'circular'` &#124; `'rounded'` &#124; `'square'` | `'rounded'` | The items can have a circular, rounded or square shape. |
144
- | `onSelectionChange` | `(e: Event, selectedItems: string[]` &#124; `[]) => void` | | Called whenever the selection changes. |
145
- | `toggleOnClick` | `boolean` | `false` | When `selectionMode` is `'multiselect'`, clicking an item toggles its selection directly instead of requiring Ctrl/Cmd + click. |
146
- | `as` | `'ul'` &#124; `'ol'` &#124; `'div'` | `'ul'` | The HTML element to render the list as. |
147
- | `ref` _bindable_ | `HTMLUListElement` &#124; `HTMLOListElement` &#124; `HTMLDivElement` | | The DOM reference of the list element. |
148
- | `disableTabspot` | `boolean` | `false` | Opts the list out of the tabspot focus management, to wire your own. |
149
- | HTML Attributes | | | |
185
+ | Name | Type | Default | Description |
186
+ | -------------------------- | --------------------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
187
+ | `role` | `AriaRole` | | Overrides the list role and the roles derived for its items. Use `list` for nested navigation with independent selection. |
188
+ | `selectionMode` | `'none'` &#124; `'single'` &#124; `'multiselect'` &#124; `'extended'` | `'none'` | How many items can be selected, and how the selection is made. |
189
+ | `navigationMode` | `'items'` &#124; `'composite'` | `'items'` | Whether the list itself takes a single tab stop, or every item takes its own. |
190
+ | `manageSpecialKeys` | `boolean` &#124; `TabspotRootOptions['manageSpecialKeys']` | `true` | Which special keys Tabspot handles at the root. Set individual keys to leave Escape to an enclosing panel. Omitted keys in an object are off. |
191
+ | `selectedItems` _bindable_ | `string[]` | | The values of the selected items. |
192
+ | `shape` | `'circular'` &#124; `'rounded'` &#124; `'square'` | `'rounded'` | The items can have a circular, rounded or square shape. |
193
+ | `onSelectionChange` | `(e: Event, selectedItems: string[]` &#124; `[]) => void` | | Called whenever the selection changes. |
194
+ | `toggleOnClick` | `boolean` | `false` | When `selectionMode` is `'multiselect'`, clicking an item toggles its selection directly instead of requiring Ctrl/Cmd + click. |
195
+ | `as` | `'ul'` &#124; `'ol'` &#124; `'div'` | `'ul'` | The HTML element to render the list as. |
196
+ | `ref` _bindable_ | `HTMLUListElement` &#124; `HTMLOListElement` &#124; `HTMLDivElement` | | The DOM reference of the list element. |
197
+ | `disableTabspot` | `boolean` | `false` | Opts the list out of the tabspot focus management, to wire your own. |
198
+ | HTML Attributes | | | |
150
199
 
151
200
  <!-- /props:ListViewProps -->
152
201
 
@@ -155,18 +204,39 @@ With `navigationMode` set to `composite` and a `selectionMode`, every item is a
155
204
  <!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
156
205
  <!-- props:ListViewItemProps -->
157
206
 
158
- | Name | Type | Default | Description |
159
- | ---------------- | ------------------------------------------------------------------ | ------- | ------------------------------------------------------------------------------------------ |
160
- | `as` | `'li'` &#124; `'a'` &#124; `'div'` | `'li'` | The HTML element to render the item as. |
161
- | `shape` | `'circular'` &#124; `'rounded'` &#124; `'square'` | | The item can have a circular, rounded or square shape. Inherited from the list by default. |
162
- | `value` | `string` | | The value reported to the list when the item is selected. Falls back to a generated id. |
163
- | `active` | `boolean` | | Renders the item in its pressed state. |
164
- | `role` | `'treeitem'` &#124; `'menuitem'` &#124; `'option'` &#124; `'row'` | | The ARIA role of the item. Derived from the list by default. |
165
- | `disabled` | `boolean` | | Disables the user interaction. |
166
- | `checkmark` | `CheckboxProps` | | The props to spread on the checkbox rendered when the list is multiselect. |
167
- | `onAction` | `(e: MouseEvent, value: string) => void` | | Called when the item is clicked or activated with the keyboard. |
168
- | `onfocus` | `(e: Event, value: string) => void` | | Called when the item receives the focus. |
169
- | `ref` _bindable_ | `HTMLLIElement` &#124; `HTMLAnchorElement` &#124; `HTMLDivElement` | | The DOM reference of the item element. |
170
- | HTML Attributes | | | |
207
+ | Name | Type | Default | Description |
208
+ | ---------------- | --------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
209
+ | `as` | `'li'` &#124; `'a'` &#124; `'button'` &#124; `'div'` | `'li'` | The HTML element to render the item as. Links and buttons are wrapped in a list item when the list renders as `ul` or `ol`. Use `li` or `div` for nested lists and content with multiple controls. Buttons cannot contain checkmarks or composite rows. |
210
+ | `shape` | `'circular'` &#124; `'rounded'` &#124; `'square'` | | The item can have a circular, rounded or square shape. Inherited from the list by default. |
211
+ | `value` | `string` | | The value reported to the list when the item is selected. Falls back to a generated id. |
212
+ | `active` | `boolean` | | Renders the item in its pressed state. |
213
+ | `role` | `'treeitem'` &#124; `'menuitem'` &#124; `'option'` &#124; `'row'` | | The ARIA role of the item. Derived from the list by default. |
214
+ | `disabled` | `boolean` | | Disables the user interaction. |
215
+ | `checkmark` | `CheckboxProps` | | The props to spread on the checkbox rendered when the list is multiselect. |
216
+ | `onAction` | `(e: MouseEvent, value: string) => void` | | Called when the item is clicked or activated with the keyboard. |
217
+ | `onfocus` | `(e: Event, value: string) => void` | | Called when the item receives the focus. |
218
+ | `ref` _bindable_ | `HTMLLIElement` &#124; `HTMLAnchorElement` &#124; `HTMLButtonElement` &#124; `HTMLDivElement` | | The DOM reference of the item element. |
219
+ | HTML Attributes | | | |
171
220
 
172
221
  <!-- /props:ListViewItemProps -->
222
+
223
+ ## ListViewItemBranch Props
224
+
225
+ <!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
226
+ <!-- props:ListViewItemBranchProps -->
227
+
228
+ | Name | Type | Default | Description |
229
+ | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------- |
230
+ | `label` | `string` | | The accessible label rendered in the disclosure button. |
231
+ | `icon` | `Snippet` &#124; `Component` | | The decorative icon before the label. |
232
+ | `expanded` _bindable_ | `boolean` | `false` | Whether the nested content is visible. Branches only expand or collapse and are never selectable. |
233
+ | `onExpandedChange` | `(e: Event, expanded: boolean) => void` | | Called when a user expands or collapses the branch. |
234
+ | `disabled` | `boolean` | `false` | Disables the disclosure button. |
235
+ | `shape` | `'circular'` &#124; `'rounded'` &#124; `'square'` | | The disclosure button shape. Inherited from the list by default. |
236
+ | `ref` _bindable_ | `HTMLButtonElement` | | The DOM reference of the native disclosure button. Additional HTML attributes, including class and id, also target this button. |
237
+ | `wrapperRef` _bindable_ | `HTMLLIElement` &#124; `HTMLDivElement` | | The DOM reference of the wrapper element, the list item that holds the button and the nested list. |
238
+ | `wrapperAttributes` | `HTMLAttributes` | | The attributes to spread on the wrapper element. |
239
+ | `children` | `Snippet` | | Nested ListView content, kept mounted while collapsed to preserve its selection. |
240
+ | `HTMLButtonAttributes` without `children`, `role`, `type`, `value`, `aria-expanded`, `aria-controls` | | | |
241
+
242
+ <!-- /props:ListViewItemBranchProps -->
@@ -0,0 +1,257 @@
1
+ <script lang="ts">
2
+ import ChevronDownRegular from 'fluentui-icons-svelte/ChevronDownRegular.svelte';
3
+ import { getTabspotAttributes, type TabspotNavigationEvent, type TabspotNodeOptions } from 'tabspot';
4
+ import { getGlobalFSContext } from '../../providers/fluentui-svelte/fluentui-svelte.js';
5
+ import { invokeHandlers, RenderSoC } from '../../internal/index.js';
6
+ import {
7
+ attachTabspot,
8
+ getListViewContext,
9
+ getListViewItemContext,
10
+ LIST_VIEW_SKIP,
11
+ setListViewItemContext
12
+ } from './utils.js';
13
+ import type { ListViewItemBranchProps } from './types.js';
14
+
15
+ const ID = $props.id();
16
+ const REGION_ID = `fs-list-view-branch-${ID}`;
17
+ const parent = getListViewContext();
18
+ const owner = getListViewItemContext();
19
+ const globalContext = getGlobalFSContext();
20
+ if (!parent) throw new Error('ListViewItemBranch must be used within a ListView');
21
+ const context = parent;
22
+
23
+ let {
24
+ label,
25
+ icon,
26
+ expanded = $bindable(false),
27
+ disabled = false,
28
+ shape,
29
+ ref = $bindable(null),
30
+ wrapperRef = $bindable(null),
31
+ wrapperAttributes,
32
+ class: classes,
33
+ onclick,
34
+ onkeydown,
35
+ onExpandedChange,
36
+ children,
37
+ ...attributes
38
+ }: ListViewItemBranchProps = $props();
39
+
40
+ let regionRef: HTMLDivElement | undefined;
41
+
42
+ const wrapperTag = $derived.by(() => {
43
+ if (context.config.role !== undefined && context.config.role !== 'list') {
44
+ throw new Error('ListViewItemBranch requires a native list. Set role="list" on selectable navigation lists.');
45
+ }
46
+ if (owner?.as === 'a' || owner?.as === 'button') {
47
+ throw new Error('ListViewItemBranch must be outside links and buttons.');
48
+ }
49
+ return context.config.as === 'div' ? 'div' : 'li';
50
+ });
51
+
52
+ setListViewItemContext({
53
+ get as() {
54
+ return wrapperTag;
55
+ }
56
+ });
57
+
58
+ const tabspotConfig: TabspotNodeOptions | undefined = $derived(
59
+ context.config.disableTabspot
60
+ ? undefined
61
+ : {
62
+ mover: {
63
+ axis: 'vertical',
64
+ items: '.fs-list-view-item-branch-trigger',
65
+ visibilityAware: 'Visible',
66
+ skip: LIST_VIEW_SKIP,
67
+ ignoreKeys: expanded ? ['ArrowLeft'] : ['ArrowRight']
68
+ }
69
+ }
70
+ );
71
+
72
+ const tabspotAttributes = $derived(tabspotConfig && getTabspotAttributes(tabspotConfig));
73
+
74
+ const canceledKeys = new WeakSet<KeyboardEvent>();
75
+
76
+ function changeExpanded(event: Event, next: boolean) {
77
+ if (disabled || expanded === next) return;
78
+ expanded = next;
79
+ onExpandedChange?.(event, expanded);
80
+ }
81
+
82
+ // Tabspot prevents Escape before native handlers. Record further cancellation by the consumer.
83
+ function trackCancellation(event: KeyboardEvent, run: () => void) {
84
+ const preventDefault = event.preventDefault;
85
+ event.preventDefault = () => {
86
+ canceledKeys.add(event);
87
+ preventDefault.call(event);
88
+ };
89
+ try {
90
+ run();
91
+ } finally {
92
+ event.preventDefault = preventDefault;
93
+ }
94
+ }
95
+
96
+ function handleKeydown(event: KeyboardEvent) {
97
+ if (onkeydown) trackCancellation(event, () => invokeHandlers(event, disabled, [onkeydown]));
98
+ if (event.defaultPrevented || disabled) return;
99
+ if (context.config.disableTabspot && (event.key === 'ArrowRight' || event.key === 'ArrowLeft')) return;
100
+ if ((event.key === 'ArrowRight' && !expanded) || (event.key === 'ArrowLeft' && expanded)) {
101
+ event.preventDefault();
102
+ changeExpanded(event, !expanded);
103
+ }
104
+ }
105
+
106
+ function handleEscape(event: KeyboardEvent) {
107
+ if (
108
+ !expanded ||
109
+ event.key !== 'Escape' ||
110
+ (event.target as Element).closest('.fs-list-view-item-branch') !== wrapperRef
111
+ )
112
+ return;
113
+ if (disabled || canceledKeys.has(event) || (context.config.disableTabspot && event.defaultPrevented)) return;
114
+ event.preventDefault();
115
+ event.stopPropagation();
116
+ changeExpanded(event, false);
117
+ ref?.focus();
118
+ }
119
+
120
+ function guardEscape(event: TabspotNavigationEvent) {
121
+ if (expanded && event.direction === 'escape' && event.from?.closest('.fs-list-view-item-branch') === wrapperRef)
122
+ event.preventDefault();
123
+ }
124
+
125
+ function restoreFocusBeforeCollapse() {
126
+ if (!expanded && regionRef?.contains(document.activeElement)) ref?.focus();
127
+ }
128
+
129
+ $effect.pre(restoreFocusBeforeCollapse);
130
+ </script>
131
+
132
+ <svelte:element
133
+ this={wrapperTag}
134
+ role={wrapperTag === 'div' ? 'listitem' : undefined}
135
+ {...wrapperAttributes}
136
+ bind:this={wrapperRef}
137
+ class={['fs-list-view-item-branch', wrapperAttributes?.class]}
138
+ onkeydown={(event: KeyboardEvent) => {
139
+ const wrapperKeydown = wrapperAttributes?.onkeydown;
140
+ if (typeof wrapperKeydown === 'function')
141
+ trackCancellation(event, () => invokeHandlers(event, [], [wrapperKeydown]));
142
+ handleEscape(event);
143
+ }}
144
+ {...tabspotAttributes}
145
+ {@attach attachTabspot(tabspotConfig, guardEscape, globalContext?.state.tabspotInstance)}
146
+ >
147
+ <button
148
+ {...attributes}
149
+ class={['fs-list-view-item-branch-trigger', shape ?? context.config.shape, classes]}
150
+ {@attach (element) => {
151
+ ref = element;
152
+ return () => {
153
+ ref = null;
154
+ };
155
+ }}
156
+ type="button"
157
+ role={undefined}
158
+ {disabled}
159
+ aria-expanded={expanded}
160
+ aria-controls={REGION_ID}
161
+ onclick={(event) => {
162
+ invokeHandlers(event, disabled, [onclick]);
163
+ if (!event.defaultPrevented) changeExpanded(event, !expanded);
164
+ }}
165
+ onkeydown={handleKeydown}
166
+ >
167
+ {#if icon}
168
+ <span class="branch-icon" aria-hidden="true">
169
+ <RenderSoC SoC={icon} />
170
+ </span>
171
+ {/if}
172
+ <span class="fs-list-view-branch-label">{label}</span>
173
+ <ChevronDownRegular aria-hidden="true" width="1rem" height="1rem" class={['branch-chevron', { expanded }]} />
174
+ </button>
175
+ <div
176
+ id={REGION_ID}
177
+ {@attach (element) => {
178
+ regionRef = element;
179
+ return () => {
180
+ regionRef = undefined;
181
+ };
182
+ }}
183
+ class="fs-list-view-item-branch-region"
184
+ hidden={!expanded}
185
+ >
186
+ {@render children?.()}
187
+ </div>
188
+ </svelte:element>
189
+
190
+ <style>
191
+ .fs-list-view-item-branch {
192
+ list-style: none;
193
+ }
194
+ .fs-list-view-item-branch-trigger {
195
+ display: flex;
196
+ align-items: center;
197
+ justify-content: space-between;
198
+ gap: 0.5rem;
199
+ margin: 0.3rem 0;
200
+ width: 100%;
201
+ box-sizing: border-box;
202
+ padding: 0.275rem 0.713rem 0.4rem;
203
+ border: none;
204
+ border-radius: var(--fs-control-border-radius);
205
+ font: inherit;
206
+ font-size: var(--fs-body2-font-size);
207
+ text-align: start;
208
+ color: var(--fs-text-primary);
209
+ background: var(--fs-subtle-fill-transparent);
210
+ cursor: pointer;
211
+ &:hover:not(:disabled) {
212
+ background: var(--fs-subtle-fill-secondary);
213
+ }
214
+ &:active:not(:disabled) {
215
+ background: var(--fs-subtle-fill-tertiary);
216
+ }
217
+ &:focus-visible {
218
+ outline: 0.125rem solid var(--fs-focus-stroke-outer);
219
+ outline-offset: 0.063rem;
220
+ }
221
+ &:disabled {
222
+ color: var(--fs-text-disabled);
223
+ cursor: not-allowed;
224
+ }
225
+ &.square {
226
+ border-radius: 0;
227
+ }
228
+ &.circular {
229
+ border-radius: 50vw;
230
+ }
231
+ & :global(.branch-chevron) {
232
+ flex-shrink: 0;
233
+ margin-inline-start: auto;
234
+ font-size: 1rem;
235
+ }
236
+ & :global(.branch-chevron.expanded) {
237
+ rotate: 180deg;
238
+ }
239
+ }
240
+ .branch-icon {
241
+ display: inline-flex;
242
+ align-items: center;
243
+ justify-content: center;
244
+ flex: none;
245
+ inline-size: 1rem;
246
+ /* As tall as a line of text, so a branch is as tall with its label as without it. */
247
+ block-size: 1lh;
248
+ & :global(svg) {
249
+ inline-size: 1rem;
250
+ block-size: 1rem;
251
+ }
252
+ }
253
+ .fs-list-view-item-branch-region > :global(.fs-list-view) {
254
+ padding-inline-start: 1rem;
255
+ box-sizing: border-box;
256
+ }
257
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { ListViewItemBranchProps } from './types.ts';
2
+ declare const ListViewItemBranch: import("svelte").Component<ListViewItemBranchProps, {}, "ref" | "wrapperRef" | "expanded">;
3
+ type ListViewItemBranch = ReturnType<typeof ListViewItemBranch>;
4
+ export default ListViewItemBranch;