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,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;
@@ -1,16 +1,23 @@
1
- <script lang="ts" generics="T extends 'li' | 'a' | 'div' = 'li'">
1
+ <script lang="ts" generics="T extends 'li' | 'a' | 'button' | 'div' = 'li'">
2
2
  import { onMount } from 'svelte';
3
- import { COMPONENT_NAME, getListViewContext, ITEM_TAG } from './utils.js';
3
+ import {
4
+ attachTabspot,
5
+ COMPONENT_NAME,
6
+ getListViewContext,
7
+ ITEM_TAG,
8
+ LIST_VIEW_SKIP,
9
+ setListViewItemContext
10
+ } from './utils.js';
4
11
  import Checkbox from '../checkbox/checkbox.svelte';
5
12
  import { invokeHandlers, isObjectEmpty } from '../../internal/index.js';
6
13
  import type { ListViewItemProps, ListViewItemDOM } from './types.js';
7
- import { getTabspotAttributes } from 'tabspot';
14
+ import { getTabspotAttributes, type TabspotNodeOptions } from 'tabspot';
8
15
 
9
16
  const ID = $props.id();
10
17
 
11
18
  let {
12
19
  as = 'li' as T,
13
- ref = $bindable(),
20
+ ref = $bindable(null),
14
21
  shape,
15
22
  disabled,
16
23
  active: _active,
@@ -20,6 +27,8 @@
20
27
  class: classes,
21
28
  onAction,
22
29
  onfocus,
30
+ onclick,
31
+ onkeydown,
23
32
  children,
24
33
  ...attributes
25
34
  }: ListViewItemProps<T> = $props();
@@ -30,6 +39,9 @@
30
39
  */
31
40
  const _as = $derived.by(() => {
32
41
  if (!ITEM_TAG.includes(as)) throw new Error(`Invalid tag: ${as}. Must be one of ${ITEM_TAG.join(', ')}`);
42
+ if (as === 'button' && (checkmark || role === 'row')) {
43
+ throw new Error('ListViewItem buttons cannot contain checkmarks or composite row controls. Use li or div.');
44
+ }
33
45
  return as;
34
46
  });
35
47
 
@@ -41,75 +53,176 @@
41
53
  const { state: _state } = CONTEXT;
42
54
  const { handleSelection, registerItem, unregisterItem, getChildrenRole } = CONTEXT.methods;
43
55
  const role = $derived(_role || getChildrenRole(as));
56
+ const wrapped = $derived((as === 'a' || as === 'button') && CONTEXT.config.as !== 'div');
57
+ const nativeAction = $derived(as === 'a' || as === 'button');
58
+ const interactive = $derived(
59
+ nativeAction || !!_role || CONTEXT.config.selectionMode !== 'none' || !!onAction || !!onclick
60
+ );
61
+ const tabspotDisabled = $derived(CONTEXT.config.disableTabspot);
62
+ setListViewItemContext({
63
+ get as() {
64
+ return as;
65
+ }
66
+ });
44
67
 
45
68
  let active = $derived(_state.selectedItems.includes(value));
46
- let checkmarkRef = $state<HTMLDivElement>();
69
+ let checkmarkRef = $state<HTMLDivElement | null>(null);
47
70
 
48
- // A row is a level of its own: right enters the cells, left comes back out. Anything
49
- // that is not a row navigates as a plain item of the list above it.
50
- const tabspotAttrs = $derived(
51
- (role === 'row'
52
- ? getTabspotAttributes({
71
+ // A grid cell contains the secondary controls, independently of the primary row action.
72
+ const cellConfig: TabspotNodeOptions | undefined = $derived(
73
+ role === 'row' && !tabspotDisabled
74
+ ? {
75
+ // Checkbox inputs are transparent while their visible wrappers supply the focus ring.
53
76
  mover: {
54
- axis: 'vertical'
77
+ axis: 'horizontal',
78
+ skip: LIST_VIEW_SKIP
55
79
  },
56
80
  grouper: { enterDirection: 'right', exitDirection: 'left' }
57
- })
58
- : null) ?? {}
81
+ }
82
+ : undefined
83
+ );
84
+ const cellAttrs = $derived(cellConfig && getTabspotAttributes(cellConfig));
85
+ // A roving list has one tab stop. Tabspot sets it on mount, so the server HTML gives it to the first item, as Tabspot will.
86
+ // svelte-ignore state_referenced_locally
87
+ const holdsTabStop = !CONTEXT.config.roving || (interactive && !disabled && CONTEXT.methods.claimTabStop(ID));
88
+ const focusAttributes = $derived(
89
+ nativeAction
90
+ ? disabled && as === 'a'
91
+ ? { tabindex: -1 }
92
+ : {}
93
+ : interactive && !tabspotDisabled
94
+ ? { tabindex: disabled || !holdsTabStop ? -1 : 0 }
95
+ : {}
59
96
  );
60
97
 
98
+ function handleClick(e: MouseEvent) {
99
+ const target = e.target instanceof Element ? e.target : null;
100
+ if (target?.closest('.fs-list-view-item') !== e.currentTarget) return;
101
+ const fromCheckmark = !!checkmarkRef?.contains(target);
102
+ const control = target?.closest(
103
+ 'a, button, input, select, textarea, [contenteditable="true"], [role="button"], [role="link"]'
104
+ );
105
+ if (!fromCheckmark && control && control !== e.currentTarget) return;
106
+ invokeHandlers(e, disabled, [
107
+ onclick,
108
+ (role !== 'row' || fromCheckmark) && ((event: MouseEvent) => handleSelection(event, value, fromCheckmark)),
109
+ !fromCheckmark && ((event: MouseEvent) => onAction?.(event, value))
110
+ ]);
111
+ }
112
+
113
+ function handleKeydown(e: KeyboardEvent) {
114
+ invokeHandlers(e, disabled, [onkeydown]);
115
+ if (e.defaultPrevented || nativeAction || e.target !== e.currentTarget || !['Enter', ' '].includes(e.key)) return;
116
+ e.preventDefault();
117
+ (e.currentTarget as HTMLElement).dispatchEvent(
118
+ new MouseEvent('click', {
119
+ bubbles: true,
120
+ ctrlKey: e.ctrlKey,
121
+ metaKey: e.metaKey,
122
+ shiftKey: e.shiftKey
123
+ })
124
+ );
125
+ }
126
+
61
127
  onMount(() => {
62
- registerItem?.(ID, value as string, !!disabled);
63
- return () => unregisterItem?.(ID);
128
+ registerItem(ID, () => ({ value, disabled: !!disabled, element: ref ?? null }));
129
+ return () => unregisterItem(ID);
64
130
  });
65
131
  </script>
66
132
 
67
- <svelte:element
68
- this={_as}
69
- {role}
70
- data-value={value}
71
- aria-selected={role === 'option' ? active : undefined}
72
- aria-disabled={disabled || undefined}
73
- bind:this={ref as ListViewItemDOM[T]}
74
- class={[
75
- 'fs-list-view-item',
76
- shape ? shape : _shape,
77
- classes,
78
- { disabled },
79
- role !== 'row' && 'indicator',
80
- (_active || active) && 'active',
81
- !disabled && role && 'interactive'
82
- ]}
83
- {...tabspotAttrs}
84
- onfocus={(e: FocusEvent) => invokeHandlers(e, disabled, [(event: FocusEvent) => onfocus?.(event, value || '')])}
85
- onclick={(e: MouseEvent) => {
86
- // The checkmark only toggles the selection, even on a row. The primary action is for the rest of the item.
87
- const fromCheckmark = !!checkmarkRef?.contains(e.target as Node);
88
- invokeHandlers(e, disabled, [
89
- (role !== 'row' || fromCheckmark) && ((event: MouseEvent) => handleSelection?.(event, value, fromCheckmark)),
90
- !fromCheckmark && ((event: MouseEvent) => onAction?.(event, value || ''))
91
- ]);
92
- }}
93
- {...attributes}
94
- >
133
+ {#snippet content()}
95
134
  {#if checkmark}
96
135
  <Checkbox
136
+ {...!isObjectEmpty(checkmark) ? checkmark : {}}
97
137
  {disabled}
98
138
  bind:checked={active}
99
139
  bind:wrapperRef={checkmarkRef}
100
- tabindex={-1}
101
- {...!isObjectEmpty(checkmark) ? checkmark : {}}
140
+ tabindex={tabspotDisabled ? undefined : -1}
141
+ aria-label={checkmark?.['aria-label'] ?? attributes['aria-label'] ?? value}
142
+ inert={role === 'option' || undefined}
102
143
  />
103
144
  {/if}
104
145
  {@render children?.()}
105
- </svelte:element>
146
+ {/snippet}
147
+
148
+ {#snippet item()}
149
+ <svelte:element
150
+ this={_as}
151
+ {...attributes}
152
+ role={wrapped || (nativeAction && role === 'listitem') ? undefined : role}
153
+ type={as === 'button' ? 'button' : undefined}
154
+ disabled={as === 'button' ? disabled : undefined}
155
+ data-value={value}
156
+ data-list-view-interactive={interactive || undefined}
157
+ aria-selected={!wrapped && (role === 'option' || role === 'row') ? active : undefined}
158
+ aria-current={role !== 'option' && role !== 'row' && (_active || active) ? 'true' : undefined}
159
+ aria-disabled={disabled || undefined}
160
+ bind:this={ref as ListViewItemDOM[T]}
161
+ class={[
162
+ 'fs-list-view-item',
163
+ shape ? shape : _shape,
164
+ classes,
165
+ { disabled },
166
+ role !== 'row' && 'indicator',
167
+ (_active || active) && 'active',
168
+ !disabled && interactive && 'interactive'
169
+ ]}
170
+ {...focusAttributes}
171
+ onfocus={(e: FocusEvent) => {
172
+ if (e.target === e.currentTarget) invokeHandlers(e, disabled, [() => onfocus?.(e, value)]);
173
+ }}
174
+ onclick={handleClick}
175
+ onkeydown={handleKeydown}
176
+ >
177
+ {#if role === 'row'}
178
+ <div role="gridcell" {...cellAttrs} {@attach attachTabspot(cellConfig)}>
179
+ {@render content()}
180
+ </div>
181
+ {:else}
182
+ {@render content()}
183
+ {/if}
184
+ </svelte:element>
185
+ {/snippet}
186
+
187
+ {#if wrapped}
188
+ <li {role} aria-selected={role === 'option' || role === 'row' ? active : undefined} class="fs-list-view-item-wrapper">
189
+ {@render item()}
190
+ </li>
191
+ {:else}
192
+ {@render item()}
193
+ {/if}
106
194
 
107
195
  <style>
196
+ .fs-list-view-item-wrapper {
197
+ list-style: none;
198
+ & > :global(.fs-list-view-item) {
199
+ width: 100%;
200
+ box-sizing: border-box;
201
+ text-align: start;
202
+ }
203
+ }
204
+ .fs-list-view-item-wrapper,
205
+ .fs-list-view-item:has(> :global(.fs-list-view)) {
206
+ & > :global(.fs-list-view) {
207
+ padding-inline-start: 1rem;
208
+ box-sizing: border-box;
209
+ }
210
+ }
211
+ .fs-list-view-item:has(> :global(.fs-list-view)) {
212
+ display: block;
213
+ }
214
+ [role='gridcell'] {
215
+ display: flex;
216
+ gap: 0.5rem;
217
+ align-items: center;
218
+ width: 100%;
219
+ }
108
220
  .fs-list-view-item {
109
221
  border-radius: var(--fs-control-border-radius);
110
222
  font-size: var(--fs-body2-font-size);
111
223
  color: var(--fs-text-primary);
112
224
  border: none;
225
+ background: var(--fs-subtle-fill-transparent);
113
226
  padding: 0.275rem 0.713rem 0.4rem 0.713rem;
114
227
  position: relative;
115
228
  outline: none;
@@ -174,4 +287,9 @@
174
287
  border-radius: 50vw;
175
288
  }
176
289
  }
290
+ @media (prefers-reduced-motion: reduce) {
291
+ .fs-list-view-item.interactive.indicator.active::after {
292
+ transition: none;
293
+ }
294
+ }
177
295
  </style>
@@ -1,12 +1,12 @@
1
1
  import type { ListViewItemProps } from './types.ts';
2
- declare function $$render<T extends 'li' | 'a' | 'div' = 'li'>(): {
2
+ declare function $$render<T extends 'li' | 'a' | 'button' | 'div' = 'li'>(): {
3
3
  props: ListViewItemProps<T>;
4
4
  exports: {};
5
5
  bindings: "ref";
6
6
  slots: {};
7
7
  events: {};
8
8
  };
9
- declare class __sveltets_Render<T extends 'li' | 'a' | 'div' = 'li'> {
9
+ declare class __sveltets_Render<T extends 'li' | 'a' | 'button' | 'div' = 'li'> {
10
10
  props(): ReturnType<typeof $$render<T>>['props'];
11
11
  events(): ReturnType<typeof $$render<T>>['events'];
12
12
  slots(): ReturnType<typeof $$render<T>>['slots'];
@@ -14,12 +14,12 @@ declare class __sveltets_Render<T extends 'li' | 'a' | 'div' = 'li'> {
14
14
  exports(): {};
15
15
  }
16
16
  interface $$IsomorphicComponent {
17
- new <T extends 'li' | 'a' | 'div' = 'li'>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
17
+ new <T extends 'li' | 'a' | 'button' | 'div' = 'li'>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
18
18
  $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
19
19
  } & ReturnType<__sveltets_Render<T>['exports']>;
20
- <T extends 'li' | 'a' | 'div' = 'li'>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
20
+ <T extends 'li' | 'a' | 'button' | 'div' = 'li'>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
21
21
  z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
22
22
  }
23
23
  declare const ListViewItem: $$IsomorphicComponent;
24
- type ListViewItem<T extends 'li' | 'a' | 'div' = 'li'> = InstanceType<typeof ListViewItem<T>>;
24
+ type ListViewItem<T extends 'li' | 'a' | 'button' | 'div' = 'li'> = InstanceType<typeof ListViewItem<T>>;
25
25
  export default ListViewItem;