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
@@ -2,13 +2,16 @@
2
2
  </script>
3
3
 
4
4
  <script lang="ts">
5
+ import { claimLabel } from '../label/label-context.js';
5
6
  import { untrack } from 'svelte';
6
7
  import { watch } from 'runed';
7
8
  import { Tooltip } from '../../index.js';
8
- import { preventDefault } from '../../internal/index.js';
9
+ import { dispatchFieldEvents, invokeHandlers } from '../../internal/index.js';
9
10
  import type { Attachment } from 'svelte/attachments';
10
11
  import type { MouseOrTouchEvent, SliderProps } from './types.js';
11
12
 
13
+ const labelContext = claimLabel(() => id);
14
+
12
15
  let {
13
16
  value = $bindable(0),
14
17
  min = 0,
@@ -27,15 +30,17 @@
27
30
  tooltipPlacement = 'top',
28
31
  tooltipContent,
29
32
  onChange,
30
- class: classes,
31
- ref = $bindable(),
32
- inputRef = $bindable(),
33
- thumbRef = $bindable(),
34
- tooltipRef = $bindable(),
35
- railRef = $bindable(),
36
- trackRef = $bindable(),
37
- tickBarRef = $bindable(),
38
- 'aria-label': ariaLabel = 'Slider',
33
+ id = labelContext?.fieldId,
34
+ ref = $bindable(null),
35
+ wrapperRef = $bindable(null),
36
+ wrapperAttributes,
37
+ thumbRef = $bindable(null),
38
+ tooltipRef = $bindable(null),
39
+ railRef = $bindable(null),
40
+ trackRef = $bindable(null),
41
+ tickBarRef = $bindable(null),
42
+ defaultValue = min,
43
+ onkeydown,
39
44
  ...attributes
40
45
  }: SliderProps = $props();
41
46
 
@@ -63,15 +68,23 @@
63
68
  }
64
69
  );
65
70
 
71
+ /** A slider that nothing else names falls back to a generic name. */
72
+ const named = $derived(
73
+ !!(attributes['aria-label'] || attributes['aria-labelledby'] || ref?.labels?.length || labelContext)
74
+ );
75
+
76
+ /** A `fieldset` disables the slider without its `disabled` prop, and only the native input knows. */
77
+ const inert = () => disabled || !!ref?.matches(':disabled');
78
+
66
79
  let dragging = $state(false);
67
80
  let holding = $state(false);
81
+ /** Whether the pointer moved the value since it was pressed, which the release reports as a `change`. */
82
+ let moved = false;
68
83
  let thumbClientWidth = $state(20);
69
84
  let percentage = $derived(valueToPercentage(value));
70
85
  let directionAwareReverse = $derived(
71
- ref && window
72
- ? window.getComputedStyle(ref)?.direction === 'ltr' && orientation === 'horizontal'
73
- ? reverse
74
- : !reverse
86
+ wrapperRef && orientation === 'horizontal' && window.getComputedStyle(wrapperRef).direction === 'rtl'
87
+ ? !reverse
75
88
  : reverse
76
89
  );
77
90
 
@@ -80,6 +93,8 @@
80
93
  }
81
94
 
82
95
  function cancelMove() {
96
+ if (moved) dispatchFieldEvents(ref, ['change']);
97
+ moved = false;
83
98
  holding = false;
84
99
  dragging = false;
85
100
  }
@@ -92,7 +107,7 @@
92
107
  }
93
108
 
94
109
  function calculateValue(event: MouseOrTouchEvent) {
95
- if (disabled || !railRef) return;
110
+ if (inert() || !railRef) return;
96
111
  const { top, bottom, left, right, width, height } = railRef.getBoundingClientRect();
97
112
  const percentageX = (event as TouchEvent).touches
98
113
  ? (event as TouchEvent).touches[0].clientX
@@ -105,6 +120,8 @@
105
120
  const startingPos =
106
121
  orientation === 'horizontal' ? (directionAwareReverse ? right : left) : directionAwareReverse ? top : bottom;
107
122
  const length = orientation === 'horizontal' ? width : height;
123
+ // A rail without length (e.g. a vertical slider in a parent with no height) has no scale to read the pointer on.
124
+ if (!length) return;
108
125
 
109
126
  let nextStep =
110
127
  min +
@@ -117,26 +134,31 @@
117
134
  ) *
118
135
  step;
119
136
 
137
+ nextStep = Math.min(Math.max(nextStep, min), max);
138
+ if (nextStep === value) return;
139
+
120
140
  value = nextStep;
141
+ moved = true;
142
+ dispatchFieldEvents(ref, ['input']);
121
143
  }
122
144
 
145
+ /** The arrows follow the direction the slider is drawn in, so they replace the stepping of the native input. */
123
146
  function handleArrowKeys(event: KeyboardEvent) {
124
147
  const { key } = event;
148
+ const increase = key === 'ArrowRight' || key === 'ArrowUp';
125
149
 
126
- if (key === 'ArrowDown' || key === 'ArrowUp') event.preventDefault();
127
- if (key === 'ArrowLeft' || (key === 'ArrowDown' && !disabled)) {
128
- if (reverse) {
129
- stepUp();
130
- } else {
131
- stepDown();
132
- }
133
- } else if (key === 'ArrowRight' || (key === 'ArrowUp' && !disabled)) {
134
- if (reverse) {
135
- stepDown();
136
- } else {
137
- stepUp();
138
- }
150
+ if (!increase && key !== 'ArrowLeft' && key !== 'ArrowDown') return;
151
+
152
+ event.preventDefault();
153
+ const previous = value;
154
+
155
+ if (increase !== reverse) {
156
+ stepUp();
157
+ } else {
158
+ stepDown();
139
159
  }
160
+
161
+ if (value !== previous) dispatchFieldEvents(ref);
140
162
  }
141
163
 
142
164
  function handleTouchStart(event: TouchEvent) {
@@ -179,7 +201,7 @@
179
201
  });
180
202
  </script>
181
203
 
182
- <!--
204
+ <!--
183
205
  @component
184
206
  A slider allows users to select a value from a continuous or discrete range by moving a thumb along a track. It supports horizontal and vertical orientations, ticks, tooltips, and reverse direction.
185
207
 
@@ -192,8 +214,6 @@
192
214
  <Slider value={50} min={0} max={100} />
193
215
  <Slider orientation="vertical" step={10} ticks={[0, 25, 50, 75, 100]} />
194
216
  ```
195
-
196
- @status WIP
197
217
  -->
198
218
  <svelte:window
199
219
  onmousemove={handleMove}
@@ -203,27 +223,34 @@
203
223
  ontouchcancel={cancelMove}
204
224
  />
205
225
 
226
+ <!-- The pointer works on the drawing; the keyboard, assistive technologies and forms work on the native input inside the thumb. -->
206
227
  <div
207
- class={['fs-slider', `orientation-${orientation}`, classes, { disabled }, directionAwareReverse && 'reverse']}
208
- bind:this={ref}
209
- tabindex={disabled ? -1 : 0}
210
- role="slider"
211
- aria-label={ariaLabel}
212
- aria-orientation={orientation}
213
- aria-valuemin={_range.min}
214
- aria-valuemax={_range.max}
215
- aria-valuenow={value}
216
- aria-valuetext={prefix || suffix ? `${prefix}${value}${suffix}` : undefined}
217
- aria-disabled={disabled || undefined}
218
- ontouchstart={handleTouchStart}
219
- onkeydown={handleArrowKeys}
220
- onmousedown={preventDefault(() => {
221
- holding = true;
222
- dragging = true;
223
- })}
228
+ {...wrapperAttributes}
229
+ class={[
230
+ 'fs-slider',
231
+ `orientation-${orientation}`,
232
+ { disabled },
233
+ directionAwareReverse && 'reverse',
234
+ wrapperAttributes?.class
235
+ ]}
236
+ bind:this={wrapperRef}
237
+ ontouchstart={(e) => invokeHandlers(e, [inert()], [handleTouchStart, wrapperAttributes?.ontouchstart])}
238
+ onmousedown={(e) =>
239
+ invokeHandlers(
240
+ e,
241
+ [inert()],
242
+ [
243
+ () => {
244
+ e.preventDefault();
245
+ ref?.focus();
246
+ holding = true;
247
+ dragging = true;
248
+ },
249
+ wrapperAttributes?.onmousedown
250
+ ]
251
+ )}
224
252
  style="--slider-percentage: {percentage}%; --slider-thumb-offset: {thumbClientWidth / 2 -
225
- linearScale([0, 50], [6, thumbClientWidth / 2])(percentage)}px;"
226
- {...attributes}
253
+ linearScale([0, 50], [6, thumbClientWidth / 2])(percentage)}px; {wrapperAttributes?.style ?? ''}"
227
254
  >
228
255
  {#if tooltip && !disabled}
229
256
  <Tooltip
@@ -236,8 +263,8 @@
236
263
  class="slider-tooltip"
237
264
  animationFrame
238
265
  >
239
- {#snippet children(args, ref)}
240
- {@render sliderThumb(args, ref)}
266
+ {#snippet children(args, attach)}
267
+ {@render sliderThumb(args, attach)}
241
268
  {/snippet}
242
269
  </Tooltip>
243
270
  {:else}
@@ -257,17 +284,38 @@
257
284
  {/each}
258
285
  </div>
259
286
  {/if}
260
- <input type="range" hidden {min} {max} {step} {disabled} {value} bind:this={inputRef} />
261
287
  </div>
262
288
 
263
- {#snippet sliderThumb(attrs: Record<string, unknown> | undefined = undefined, ref: Attachment | undefined = undefined)}
289
+ {#snippet sliderThumb(
290
+ attrs: Record<string, unknown> | undefined = undefined,
291
+ attach: Attachment | undefined = undefined
292
+ )}
264
293
  <div
265
- class="slider-thumb"
266
294
  {...attrs}
267
- {@attach ref ? ref : undefined}
295
+ class={['slider-thumb', attrs?.class]}
296
+ {@attach attach}
268
297
  bind:this={thumbRef}
269
298
  bind:clientWidth={thumbClientWidth}
270
- ></div>
299
+ >
300
+ <!-- The field: where the thumb is, so its focus and validation message are too. -->
301
+ <input
302
+ {...attributes}
303
+ {id}
304
+ class="native-field"
305
+ type="range"
306
+ min={_range.min}
307
+ max={_range.max}
308
+ step={_range.step}
309
+ {disabled}
310
+ aria-label={named ? attributes['aria-label'] : 'Slider'}
311
+ aria-orientation={orientation}
312
+ aria-valuetext={prefix || suffix ? `${prefix}${value}${suffix}` : attributes['aria-valuetext']}
313
+ {defaultValue}
314
+ bind:value
315
+ bind:this={ref}
316
+ onkeydown={(e) => invokeHandlers(e, [disabled], [handleArrowKeys, onkeydown])}
317
+ />
318
+ </div>
271
319
  {/snippet}
272
320
 
273
321
  <style>
@@ -280,9 +328,16 @@
280
328
  min-inline-size: 32px;
281
329
  min-block-size: 32px;
282
330
 
283
- & :focus-visible {
284
- outline: none;
285
- box-shadow: var(--fs-focus-stroke-outer);
331
+ & .slider-thumb:has(> .native-field:focus-visible) {
332
+ outline: 0.125rem var(--fs-focus-stroke-outer) solid;
333
+ outline-offset: 0.125rem;
334
+ }
335
+
336
+ &:has(.native-field:is(:user-invalid, [aria-invalid='true'])) {
337
+ & .slider-track,
338
+ & .slider-thumb::before {
339
+ background-color: var(--fs-system-critical);
340
+ }
286
341
  }
287
342
 
288
343
  &.orientation-horizontal {
@@ -387,7 +442,7 @@
387
442
  }
388
443
  }
389
444
 
390
- &.disabled {
445
+ &:is(.disabled, :has(.native-field:disabled)) {
391
446
  & .slider-rail {
392
447
  background-color: var(--fs-accent-fill-disabled);
393
448
  }
@@ -450,6 +505,16 @@
450
505
  }
451
506
  }
452
507
 
508
+ .native-field {
509
+ position: absolute;
510
+ inset: 0;
511
+ inline-size: 100%;
512
+ block-size: 100%;
513
+ margin: 0;
514
+ opacity: 0;
515
+ pointer-events: none;
516
+ }
517
+
453
518
  .slider-thumb {
454
519
  display: flex;
455
520
  justify-content: center;
@@ -11,9 +11,7 @@ import type { SliderProps } from './types.ts';
11
11
  * <Slider value={50} min={0} max={100} />
12
12
  * <Slider orientation="vertical" step={10} ticks={[0, 25, 50, 75, 100]} />
13
13
  * ```
14
- *
15
- * @status WIP
16
14
  */
17
- declare const Slider: import("svelte").Component<SliderProps, {}, "ref" | "value" | "inputRef" | "thumbRef" | "tooltipRef" | "railRef" | "trackRef" | "tickBarRef">;
15
+ declare const Slider: import("svelte").Component<SliderProps, {}, "ref" | "value" | "wrapperRef" | "thumbRef" | "tooltipRef" | "railRef" | "trackRef" | "tickBarRef">;
18
16
  type Slider = ReturnType<typeof Slider>;
19
17
  export default Slider;
@@ -1,6 +1,7 @@
1
1
  import type { Placement } from '@floating-ui/dom';
2
2
  import type { Component, Snippet } from 'svelte';
3
- import type { HTMLAttributes } from 'svelte/elements';
3
+ import type { HTMLAttributes, HTMLInputAttributes } from 'svelte/elements';
4
+ import type { HiddenInputAttributes } from '../../internal/index.js';
4
5
  /** @propsmith SliderProps */
5
6
  export type SliderProps = {
6
7
  /** The current value of the slider.
@@ -8,6 +9,10 @@ export type SliderProps = {
8
9
  * @bindable
9
10
  */
10
11
  value?: number;
12
+ /** The value a form reset returns the slider to. A range input without one would fall back to an empty value.
13
+ * @default min
14
+ */
15
+ defaultValue?: number;
11
16
  /** The minimum value of the slider.
12
17
  * @default 0
13
18
  */
@@ -52,7 +57,7 @@ export type SliderProps = {
52
57
  * @default false
53
58
  */
54
59
  disabled?: boolean;
55
- /** Snaps the thumb to the pointer as soon as it is pressed, instead of stepping towards it. */
60
+ /** Snaps the thumb to the pointer as soon as it is pressed, instead of stepping towards it. Without it, a value between two steps is drawn as it is, while the native input, and so the form, holds the nearest step. */
56
61
  sync?: boolean;
57
62
  /** Whether to show the value in a tooltip while dragging.
58
63
  * @default true
@@ -68,35 +73,37 @@ export type SliderProps = {
68
73
  tooltipContent?: string | Snippet | Component;
69
74
  /** Called whenever the value changes. */
70
75
  onChange?: (value: number) => void;
71
- /** The class to apply to the slider. */
72
- class?: string;
73
- /** The DOM reference of the slider element.
76
+ /** The DOM reference of the native range input that holds the value and takes the keyboard focus. The other attributes of the slider are applied to it.
74
77
  * @bindable
75
78
  */
76
- ref?: HTMLElement;
77
- /** The DOM reference of the underlying input element.
79
+ ref?: HTMLInputElement | null;
80
+ /** The DOM reference of the element that draws the slider.
78
81
  * @bindable
79
82
  */
80
- inputRef?: HTMLInputElement;
83
+ wrapperRef?: HTMLDivElement | null;
84
+ /** The attributes to spread on the element that draws the slider.
85
+ * @type HTMLAttributes
86
+ */
87
+ wrapperAttributes?: HTMLAttributes<HTMLDivElement>;
81
88
  /** The DOM reference of the thumb element.
82
89
  * @bindable
83
90
  */
84
- thumbRef?: HTMLElement;
91
+ thumbRef?: HTMLElement | null;
85
92
  /** The DOM reference of the tooltip element.
86
93
  * @bindable
87
94
  */
88
- tooltipRef?: HTMLElement;
95
+ tooltipRef?: HTMLElement | null;
89
96
  /** The DOM reference of the rail element.
90
97
  * @bindable
91
98
  */
92
- railRef?: HTMLElement;
99
+ railRef?: HTMLElement | null;
93
100
  /** The DOM reference of the track element.
94
101
  * @bindable
95
102
  */
96
- trackRef?: HTMLElement;
103
+ trackRef?: HTMLElement | null;
97
104
  /** The DOM reference of the tick bar element.
98
105
  * @bindable
99
106
  */
100
- tickBarRef?: HTMLElement;
101
- } & HTMLAttributes<HTMLDivElement>;
107
+ tickBarRef?: HTMLElement | null;
108
+ } & HiddenInputAttributes<HTMLInputAttributes>;
102
109
  export type MouseOrTouchEvent = MouseEvent | TouchEvent;
@@ -20,7 +20,7 @@ A tab strip expects a `value` on every tab and an `activeTab` to match it agains
20
20
  let activeTab = $state('document');
21
21
  </script>
22
22
 
23
- <TabView bind:activeTab listProps={{ 'aria-label': 'Workspaces' }}>
23
+ <TabView bind:activeTab listAttributes={{ 'aria-label': 'Workspaces' }}>
24
24
  <TabViewTab id="tab-document" value="document">Document</TabViewTab>
25
25
  <TabViewTab id="tab-mail" value="mail">Mail</TabViewTab>
26
26
  </TabView>
@@ -104,16 +104,16 @@ A disabled tab cannot be selected, and the arrow keys pass over it rather than l
104
104
 
105
105
  | Name | Type | Default | Description |
106
106
  | -------------------------- | ------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
107
- | `ref` _bindable_ | `HTMLDivElement` | | The DOM reference of the element wrapping the strip. |
108
- | `listRef` _bindable_ | `HTMLDivElement` | | The DOM reference of the tab list. |
109
- | `listProps` | `HTMLAttributes` | | The props to spread on the tab list element, where the `tablist` role and its label belong. |
107
+ | `ref` _bindable_ | `HTMLDivElement` &#124; `null` | | The DOM reference of the element wrapping the strip. |
108
+ | `listRef` _bindable_ | `HTMLDivElement` &#124; `null` | | The DOM reference of the tab list. |
109
+ | `listAttributes` | `HTMLAttributes` | | The Attributes to spread on the tab list element, where the `tablist` role and its label belong. |
110
110
  | `activeTab` _bindable_ | `string` | | The value of the selected tab. |
111
111
  | `onTabChange` | `(event: Event, value: string) => void` | | Fired when the selected tab changes. |
112
112
  | `closable` | `boolean` | `false` | Every tab shows a close button, unless the tab opts out of it. |
113
113
  | `onTabClose` | `(event: Event, value: string) => void` | | Fired when a tab asks to be closed. Removing the tab is left to the consumer. |
114
114
  | `showNewTabButton` | `boolean` | `false` | Shows the button that adds a new tab at the end of the strip. |
115
115
  | `onNewTab` | `(event: MouseEvent) => void` | | Fired when the new tab button is activated. |
116
- | `newTabButtonProps` | `HTMLButtonAttributes` | | The props to spread on the new tab button. |
116
+ | `newTabButtonAttributes` | `HTMLButtonAttributes` | | The props to spread on the new tab button. |
117
117
  | `tabWidthMode` | `'equal'` &#124; `'size-to-content'` &#124; `'compact'` | `'equal'` | How the tabs take their width: sharing the strip evenly, sizing to their own content, or shrinking to the icon of every tab but the selected one. |
118
118
  | `disableTabspot` | `boolean` | `false` | Opts the strip out of the tabspot focus management, to wire your own. |
119
119
  | Element Attributes (`div`) | | | |
@@ -127,7 +127,7 @@ A disabled tab cannot be selected, and the arrow keys pass over it rather than l
127
127
 
128
128
  | Name | Type | Default | Description |
129
129
  | -------------------------- | --------------------------------------- | ------- | --------------------------------------------------------------------------------------- |
130
- | `ref` _bindable_ | `HTMLDivElement` | | The DOM reference of the tab element. |
130
+ | `ref` _bindable_ | `HTMLDivElement` &#124; `null` | | The DOM reference of the tab element. |
131
131
  | `value` | `string` | | The value that identifies the tab. Defaults to a generated id. |
132
132
  | `icon` | `Snippet` &#124; `Component` | | The icon to display before the label. |
133
133
  | `closable` | `boolean` | | Shows the close button of the tab. Falls back to the `closable` of the strip. |
@@ -143,4 +143,4 @@ The strip is a single tab stop. `ArrowLeft` and `ArrowRight` walk the tabs, `Hom
143
143
 
144
144
  ## Accessibility
145
145
 
146
- The strip renders a `tablist` and every tab a `tab`, so give the list a name through the `aria-label` you pass in `listProps`. Since you render what a tab opens, give that element a `tabpanel` role and point its `aria-labelledby` at the `id` of its tab.
146
+ The strip renders a `tablist` and every tab a `tab`, so give the list a name through the `aria-label` you pass in `listAttributes`. Since you render what a tab opens, give that element a `tabpanel` role and point its `aria-labelledby` at the `id` of its tab.
@@ -7,7 +7,7 @@
7
7
  const ID = $props.id();
8
8
 
9
9
  let {
10
- ref = $bindable(),
10
+ ref = $bindable(null),
11
11
  value = `${COMPONENT_NAME}-${ID}`,
12
12
  icon,
13
13
  closable,
@@ -37,9 +37,9 @@
37
37
  * That costs the keys a button would have answered on its own, so they are handled here:
38
38
  * `Enter`/`Space` select, and `Delete` closes — the tabs pattern reserves it for exactly that.
39
39
  *
40
- * The close button is held out of the tab order, so the strip stays a single tab stop. `Delete`
41
- * is what reaches it from the keyboard, which is why the key is bound even while the pointer
42
- * has a button of its own.
40
+ * The close button is for the pointer only: a tab cannot hold a control of its own, so it is
41
+ * hidden from assistive technology and out of the tab order. `Delete` is what closes the tab
42
+ * from the keyboard, and `aria-keyshortcuts` announces it.
43
43
  */
44
44
  const onTabKeydown = (event: KeyboardEvent) => {
45
45
  // A key spent inside the close button is already answered; only the tab itself speaks here.
@@ -73,16 +73,17 @@
73
73
  ```
74
74
  -->
75
75
  <div
76
+ {...attributes}
76
77
  bind:this={ref}
77
78
  role="tab"
78
79
  tabindex={disabled ? -1 : 0}
79
80
  data-value={value}
80
81
  aria-selected={active}
81
82
  aria-disabled={disabled || undefined}
83
+ aria-keyshortcuts={_closable ? 'Delete' : undefined}
82
84
  class={['fs-tab-view-tab', context.config.tabWidthMode, { active, disabled }, classes]}
83
85
  onclick={(e: MouseEvent) => invokeHandlers(e, disabled, [() => context.methods.setActiveTab(e, value), onclick])}
84
86
  onkeydown={(e: KeyboardEvent) => invokeHandlers(e, disabled, [onTabKeydown, onkeydown])}
85
- {...attributes}
86
87
  >
87
88
  {#if icon}
88
89
  <span class="fs-tab-view-tab-icon">
@@ -91,20 +92,17 @@
91
92
  {/if}
92
93
  <span class="fs-tab-view-tab-label">{@render children?.()}</span>
93
94
  {#if _closable}
94
- <button
95
- type="button"
95
+ <span
96
96
  class="fs-tab-view-tab-close"
97
- aria-label="Close tab"
98
- tabindex={-1}
99
- {disabled}
97
+ aria-hidden="true"
100
98
  onclick={(e: MouseEvent) => {
101
99
  // The tab underneath would otherwise read the click as "select me".
102
100
  e.stopPropagation();
103
- close(e);
101
+ if (!disabled) close(e);
104
102
  }}
105
103
  >
106
104
  <DismissRegular />
107
- </button>
105
+ </span>
108
106
  {/if}
109
107
  {#if active}
110
108
  <!--
@@ -223,6 +221,10 @@
223
221
  }
224
222
  & .fs-tab-view-tab-close {
225
223
  cursor: not-allowed;
224
+ &:hover,
225
+ &:active {
226
+ background: transparent;
227
+ }
226
228
  }
227
229
  }
228
230
  /* Share the strip evenly, within the width a document tab is worth. */
@@ -274,10 +276,10 @@
274
276
  background: transparent;
275
277
  color: inherit;
276
278
  cursor: pointer;
277
- &:hover:not(:disabled) {
279
+ &:hover {
278
280
  background: var(--fs-subtle-fill-secondary);
279
281
  }
280
- &:active:not(:disabled) {
282
+ &:active {
281
283
  background: var(--fs-subtle-fill-tertiary);
282
284
  }
283
285
  & :global(svg) {
@@ -1,20 +1,21 @@
1
1
  <script lang="ts">
2
+ import { invokeHandlers } from '../../internal/index.js';
2
3
  import { getTabspotAttributes } from 'tabspot';
3
4
  import AddRegular from 'fluentui-icons-svelte/AddRegular.svelte';
4
5
  import { setTabViewContext, TABSPOT_ITEMS, TABSPOT_SKIP } from './tab-view.svelte.js';
5
6
  import type { TabViewContext, TabViewProps } from './types.js';
6
7
 
7
8
  let {
8
- ref = $bindable(),
9
- listRef = $bindable(),
10
- listProps,
9
+ ref = $bindable(null),
10
+ listRef = $bindable(null),
11
+ listAttributes,
11
12
  activeTab = $bindable(''),
12
13
  onTabChange,
13
14
  closable = false,
14
15
  onTabClose,
15
16
  showNewTabButton = false,
16
17
  onNewTab,
17
- newTabButtonProps,
18
+ newTabButtonAttributes,
18
19
  tabWidthMode = 'equal',
19
20
  disableTabspot = false,
20
21
  class: classes,
@@ -90,17 +91,23 @@
90
91
  {/if}
91
92
  ```
92
93
  -->
93
- <div bind:this={ref} class={['fs-tab-view', classes]} {...attributes}>
94
- <div bind:this={listRef} role="tablist" class="fs-tab-view-list" {...tabspotAttributes} {...listProps}>
94
+ <div {...attributes} bind:this={ref} class={['fs-tab-view', classes]}>
95
+ <div
96
+ {...listAttributes}
97
+ bind:this={listRef}
98
+ role="tablist"
99
+ class={['fs-tab-view-list', listAttributes?.class]}
100
+ {...tabspotAttributes}
101
+ >
95
102
  {@render children?.()}
96
103
  </div>
97
104
  {#if showNewTabButton}
98
105
  <button
106
+ {...newTabButtonAttributes}
99
107
  type="button"
100
- class="fs-tab-view-new-tab"
108
+ class={['fs-tab-view-new-tab', newTabButtonAttributes?.class]}
101
109
  aria-label="Add new tab"
102
- onclick={(e: MouseEvent) => onNewTab?.(e)}
103
- {...newTabButtonProps}
110
+ onclick={(e: MouseEvent) => invokeHandlers(e, [], [onNewTab, newTabButtonAttributes?.onclick])}
104
111
  >
105
112
  <AddRegular />
106
113
  </button>
@@ -17,15 +17,15 @@ export type TabViewProps = {
17
17
  /** The DOM reference of the element wrapping the strip.
18
18
  * @bindable
19
19
  */
20
- ref?: HTMLDivElement;
20
+ ref?: HTMLDivElement | null;
21
21
  /** The DOM reference of the tab list.
22
22
  * @bindable
23
23
  */
24
- listRef?: HTMLDivElement;
25
- /** The props to spread on the tab list element, where the `tablist` role and its label belong.
24
+ listRef?: HTMLDivElement | null;
25
+ /** The Attributes to spread on the tab list element, where the `tablist` role and its label belong.
26
26
  * @type HTMLAttributes
27
27
  */
28
- listProps?: HTMLAttributes<HTMLDivElement>;
28
+ listAttributes?: HTMLAttributes<HTMLDivElement>;
29
29
  /** The value of the selected tab.
30
30
  * @bindable
31
31
  */
@@ -47,7 +47,7 @@ export type TabViewProps = {
47
47
  /** The props to spread on the new tab button.
48
48
  * @type HTMLButtonAttributes
49
49
  */
50
- newTabButtonProps?: HTMLButtonAttributes;
50
+ newTabButtonAttributes?: HTMLButtonAttributes;
51
51
  /** How the tabs take their width: sharing the strip evenly, sizing to their own content, or
52
52
  * shrinking to the icon of every tab but the selected one.
53
53
  * @default 'equal'
@@ -63,7 +63,7 @@ export type TabViewTabProps = {
63
63
  /** The DOM reference of the tab element.
64
64
  * @bindable
65
65
  */
66
- ref?: HTMLDivElement;
66
+ ref?: HTMLDivElement | null;
67
67
  /** The value that identifies the tab. Defaults to a generated id. */
68
68
  value?: string;
69
69
  /** The icon to display before the label.