fluentui-svelte 0.1.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 (283) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +201 -0
  3. package/dist/components/auto-suggest-box/auto-suggest-box.d.ts +2 -0
  4. package/dist/components/auto-suggest-box/auto-suggest-box.js +2 -0
  5. package/dist/components/auto-suggest-box/auto-suggest-box.svelte +457 -0
  6. package/dist/components/auto-suggest-box/auto-suggest-box.svelte.d.ts +4 -0
  7. package/dist/components/auto-suggest-box/docs/llms.md +73 -0
  8. package/dist/components/auto-suggest-box/option.svelte +82 -0
  9. package/dist/components/auto-suggest-box/option.svelte.d.ts +4 -0
  10. package/dist/components/auto-suggest-box/types.d.ts +140 -0
  11. package/dist/components/auto-suggest-box/types.js +1 -0
  12. package/dist/components/avatar/avatar.svelte +269 -0
  13. package/dist/components/avatar/avatar.svelte.d.ts +20 -0
  14. package/dist/components/avatar/docs/llms.md +92 -0
  15. package/dist/components/avatar/type.d.ts +63 -0
  16. package/dist/components/avatar/type.js +1 -0
  17. package/dist/components/badge/badge-icon.svelte +103 -0
  18. package/dist/components/badge/badge-icon.svelte.d.ts +25 -0
  19. package/dist/components/badge/badge.svelte +163 -0
  20. package/dist/components/badge/badge.svelte.d.ts +20 -0
  21. package/dist/components/badge/docs/llms.md +133 -0
  22. package/dist/components/badge/types.d.ts +70 -0
  23. package/dist/components/badge/types.js +1 -0
  24. package/dist/components/badge/utils.svelte.d.ts +16 -0
  25. package/dist/components/badge/utils.svelte.js +35 -0
  26. package/dist/components/button/button.svelte +203 -0
  27. package/dist/components/button/button.svelte.d.ts +38 -0
  28. package/dist/components/button/docs/llms.md +102 -0
  29. package/dist/components/button/split-button.svelte +66 -0
  30. package/dist/components/button/split-button.svelte.d.ts +4 -0
  31. package/dist/components/button/types.d.ts +84 -0
  32. package/dist/components/button/types.js +1 -0
  33. package/dist/components/calendar-date-picker/calendar-date-picker.svelte +119 -0
  34. package/dist/components/calendar-date-picker/calendar-date-picker.svelte.d.ts +4 -0
  35. package/dist/components/calendar-date-picker/docs/llms.md +33 -0
  36. package/dist/components/calendar-date-picker/types.d.ts +36 -0
  37. package/dist/components/calendar-date-picker/types.js +1 -0
  38. package/dist/components/calendar-view/calendar-view-controls.svelte +115 -0
  39. package/dist/components/calendar-view/calendar-view-controls.svelte.d.ts +3 -0
  40. package/dist/components/calendar-view/calendar-view-days.svelte +210 -0
  41. package/dist/components/calendar-view/calendar-view-days.svelte.d.ts +3 -0
  42. package/dist/components/calendar-view/calendar-view-grid.d.ts +38 -0
  43. package/dist/components/calendar-view/calendar-view-grid.js +140 -0
  44. package/dist/components/calendar-view/calendar-view-header.svelte +66 -0
  45. package/dist/components/calendar-view/calendar-view-header.svelte.d.ts +18 -0
  46. package/dist/components/calendar-view/calendar-view-item.svelte +181 -0
  47. package/dist/components/calendar-view/calendar-view-item.svelte.d.ts +4 -0
  48. package/dist/components/calendar-view/calendar-view-months.svelte +165 -0
  49. package/dist/components/calendar-view/calendar-view-months.svelte.d.ts +3 -0
  50. package/dist/components/calendar-view/calendar-view-years.svelte +161 -0
  51. package/dist/components/calendar-view/calendar-view-years.svelte.d.ts +3 -0
  52. package/dist/components/calendar-view/calendar-view.svelte +451 -0
  53. package/dist/components/calendar-view/calendar-view.svelte.d.ts +9 -0
  54. package/dist/components/calendar-view/calendar-view.svelte.js +196 -0
  55. package/dist/components/calendar-view/docs/llms.md +121 -0
  56. package/dist/components/calendar-view/index.d.ts +1 -0
  57. package/dist/components/calendar-view/index.js +1 -0
  58. package/dist/components/calendar-view/types.d.ts +174 -0
  59. package/dist/components/calendar-view/types.js +1 -0
  60. package/dist/components/checkbox/checkbox.svelte +171 -0
  61. package/dist/components/checkbox/checkbox.svelte.d.ts +40 -0
  62. package/dist/components/checkbox/docs/llms.md +78 -0
  63. package/dist/components/checkbox/types.d.ts +50 -0
  64. package/dist/components/checkbox/types.js +1 -0
  65. package/dist/components/date-picker/date-picker.svelte +381 -0
  66. package/dist/components/date-picker/date-picker.svelte.d.ts +4 -0
  67. package/dist/components/date-picker/docs/llms.md +52 -0
  68. package/dist/components/date-picker/index.d.ts +1 -0
  69. package/dist/components/date-picker/index.js +1 -0
  70. package/dist/components/date-picker/types.d.ts +74 -0
  71. package/dist/components/date-picker/types.js +1 -0
  72. package/dist/components/dialog/dialog-actions.svelte +37 -0
  73. package/dist/components/dialog/dialog-actions.svelte.d.ts +4 -0
  74. package/dist/components/dialog/dialog-content.svelte +17 -0
  75. package/dist/components/dialog/dialog-content.svelte.d.ts +4 -0
  76. package/dist/components/dialog/dialog-surface.svelte +95 -0
  77. package/dist/components/dialog/dialog-surface.svelte.d.ts +4 -0
  78. package/dist/components/dialog/dialog-title.svelte +33 -0
  79. package/dist/components/dialog/dialog-title.svelte.d.ts +25 -0
  80. package/dist/components/dialog/dialog-trigger.svelte +23 -0
  81. package/dist/components/dialog/dialog-trigger.svelte.d.ts +4 -0
  82. package/dist/components/dialog/dialog.svelte +64 -0
  83. package/dist/components/dialog/dialog.svelte.d.ts +7 -0
  84. package/dist/components/dialog/dialog.svelte.js +2 -0
  85. package/dist/components/dialog/docs/llms.md +131 -0
  86. package/dist/components/dialog/types.d.ts +92 -0
  87. package/dist/components/dialog/types.js +1 -0
  88. package/dist/components/divider/divider.svelte +120 -0
  89. package/dist/components/divider/divider.svelte.d.ts +39 -0
  90. package/dist/components/divider/docs/llms.md +83 -0
  91. package/dist/components/divider/types.d.ts +32 -0
  92. package/dist/components/divider/types.js +1 -0
  93. package/dist/components/dropdown/docs/llms.md +70 -0
  94. package/dist/components/dropdown/dropdown-context.d.ts +2 -0
  95. package/dist/components/dropdown/dropdown-context.js +2 -0
  96. package/dist/components/dropdown/dropdown-option.svelte +31 -0
  97. package/dist/components/dropdown/dropdown-option.svelte.d.ts +4 -0
  98. package/dist/components/dropdown/dropdown.svelte +309 -0
  99. package/dist/components/dropdown/dropdown.svelte.d.ts +4 -0
  100. package/dist/components/dropdown/types.d.ts +82 -0
  101. package/dist/components/dropdown/types.js +1 -0
  102. package/dist/components/dropdown-select/docs/llms.md +72 -0
  103. package/dist/components/dropdown-select/dropdown-select-option.svelte +87 -0
  104. package/dist/components/dropdown-select/dropdown-select-option.svelte.d.ts +4 -0
  105. package/dist/components/dropdown-select/dropdown-select.svelte +165 -0
  106. package/dist/components/dropdown-select/dropdown-select.svelte.d.ts +4 -0
  107. package/dist/components/dropdown-select/dropdown-select.svelte.js +48 -0
  108. package/dist/components/dropdown-select/index.d.ts +2 -0
  109. package/dist/components/dropdown-select/index.js +2 -0
  110. package/dist/components/dropdown-select/types.d.ts +46 -0
  111. package/dist/components/dropdown-select/types.js +1 -0
  112. package/dist/components/expander/docs/llms.md +88 -0
  113. package/dist/components/expander/expander.svelte +249 -0
  114. package/dist/components/expander/expander.svelte.d.ts +21 -0
  115. package/dist/components/expander/types.d.ts +47 -0
  116. package/dist/components/expander/types.js +1 -0
  117. package/dist/components/flyout/docs/llms.md +31 -0
  118. package/dist/components/flyout/flyout.svelte +148 -0
  119. package/dist/components/flyout/flyout.svelte.d.ts +19 -0
  120. package/dist/components/flyout/types.d.ts +36 -0
  121. package/dist/components/flyout/types.js +1 -0
  122. package/dist/components/hyperlink/docs/llms.md +33 -0
  123. package/dist/components/hyperlink/hyperlink.svelte +66 -0
  124. package/dist/components/hyperlink/hyperlink.svelte.d.ts +16 -0
  125. package/dist/components/hyperlink/types.d.ts +14 -0
  126. package/dist/components/hyperlink/types.js +1 -0
  127. package/dist/components/info-bar/docs/llms.md +81 -0
  128. package/dist/components/info-bar/info-bar.svelte +140 -0
  129. package/dist/components/info-bar/info-bar.svelte.d.ts +19 -0
  130. package/dist/components/info-bar/types.d.ts +27 -0
  131. package/dist/components/info-bar/types.js +1 -0
  132. package/dist/components/label/docs/llms.md +67 -0
  133. package/dist/components/label/label.svelte +95 -0
  134. package/dist/components/label/label.svelte.d.ts +4 -0
  135. package/dist/components/label/types.d.ts +32 -0
  136. package/dist/components/label/types.js +1 -0
  137. package/dist/components/list-view/docs/llms.md +110 -0
  138. package/dist/components/list-view/list-view-item.svelte +168 -0
  139. package/dist/components/list-view/list-view-item.svelte.d.ts +25 -0
  140. package/dist/components/list-view/list-view.svelte +137 -0
  141. package/dist/components/list-view/list-view.svelte.d.ts +25 -0
  142. package/dist/components/list-view/types.d.ts +95 -0
  143. package/dist/components/list-view/types.js +1 -0
  144. package/dist/components/list-view/utils.d.ts +6 -0
  145. package/dist/components/list-view/utils.js +12 -0
  146. package/dist/components/menu/context-menu-trigger.svelte +84 -0
  147. package/dist/components/menu/context-menu-trigger.svelte.d.ts +9 -0
  148. package/dist/components/menu/docs/llms.md +169 -0
  149. package/dist/components/menu/menu-divider.svelte +9 -0
  150. package/dist/components/menu/menu-divider.svelte.d.ts +26 -0
  151. package/dist/components/menu/menu-group.svelte +31 -0
  152. package/dist/components/menu/menu-group.svelte.d.ts +4 -0
  153. package/dist/components/menu/menu-item-checkbox.svelte +143 -0
  154. package/dist/components/menu/menu-item-checkbox.svelte.d.ts +15 -0
  155. package/dist/components/menu/menu-item-radio.svelte +147 -0
  156. package/dist/components/menu/menu-item-radio.svelte.d.ts +15 -0
  157. package/dist/components/menu/menu-item-switch.svelte +194 -0
  158. package/dist/components/menu/menu-item-switch.svelte.d.ts +15 -0
  159. package/dist/components/menu/menu-item.svelte +124 -0
  160. package/dist/components/menu/menu-item.svelte.d.ts +18 -0
  161. package/dist/components/menu/menu-list.svelte +38 -0
  162. package/dist/components/menu/menu-list.svelte.d.ts +5 -0
  163. package/dist/components/menu/menu-popover.svelte +125 -0
  164. package/dist/components/menu/menu-popover.svelte.d.ts +12 -0
  165. package/dist/components/menu/menu-trigger.svelte +121 -0
  166. package/dist/components/menu/menu-trigger.svelte.d.ts +4 -0
  167. package/dist/components/menu/menu.svelte +124 -0
  168. package/dist/components/menu/menu.svelte.d.ts +4 -0
  169. package/dist/components/menu/menu.svelte.js +77 -0
  170. package/dist/components/menu/types.d.ts +127 -0
  171. package/dist/components/menu/types.js +1 -0
  172. package/dist/components/number-box/docs/llms.md +37 -0
  173. package/dist/components/number-box/number-box.svelte +115 -0
  174. package/dist/components/number-box/number-box.svelte.d.ts +15 -0
  175. package/dist/components/number-box/types.d.ts +16 -0
  176. package/dist/components/number-box/types.js +1 -0
  177. package/dist/components/persona/docs/llms.md +107 -0
  178. package/dist/components/persona/persona.svelte +79 -0
  179. package/dist/components/persona/persona.svelte.d.ts +4 -0
  180. package/dist/components/persona/types.d.ts +42 -0
  181. package/dist/components/persona/types.js +1 -0
  182. package/dist/components/progress-bar/docs/llms.md +67 -0
  183. package/dist/components/progress-bar/progress-bar.svelte +167 -0
  184. package/dist/components/progress-bar/progress-bar.svelte.d.ts +15 -0
  185. package/dist/components/progress-bar/types.d.ts +39 -0
  186. package/dist/components/progress-bar/types.js +1 -0
  187. package/dist/components/progress-ring/docs/llms.md +76 -0
  188. package/dist/components/progress-ring/progress-ring.svelte +145 -0
  189. package/dist/components/progress-ring/progress-ring.svelte.d.ts +15 -0
  190. package/dist/components/progress-ring/types.d.ts +42 -0
  191. package/dist/components/progress-ring/types.js +1 -0
  192. package/dist/components/radio-button/docs/llms.md +64 -0
  193. package/dist/components/radio-button/radio-button.svelte +129 -0
  194. package/dist/components/radio-button/radio-button.svelte.d.ts +19 -0
  195. package/dist/components/radio-button/types.d.ts +24 -0
  196. package/dist/components/radio-button/types.js +1 -0
  197. package/dist/components/skeleton/docs/llms.md +67 -0
  198. package/dist/components/skeleton/skeleton.svelte +106 -0
  199. package/dist/components/skeleton/skeleton.svelte.d.ts +39 -0
  200. package/dist/components/skeleton/types.d.ts +24 -0
  201. package/dist/components/skeleton/types.js +1 -0
  202. package/dist/components/slider/docs/llms.md +75 -0
  203. package/dist/components/slider/slider.svelte +486 -0
  204. package/dist/components/slider/slider.svelte.d.ts +19 -0
  205. package/dist/components/slider/types.d.ts +102 -0
  206. package/dist/components/slider/types.js +1 -0
  207. package/dist/components/text-box/docs/llms.md +86 -0
  208. package/dist/components/text-box/text-box-button.svelte +65 -0
  209. package/dist/components/text-box/text-box-button.svelte.d.ts +9 -0
  210. package/dist/components/text-box/text-box.svelte +299 -0
  211. package/dist/components/text-box/text-box.svelte.d.ts +16 -0
  212. package/dist/components/text-box/types.d.ts +60 -0
  213. package/dist/components/text-box/types.js +1 -0
  214. package/dist/components/textarea/docs/llms.md +55 -0
  215. package/dist/components/textarea/textarea.svelte +151 -0
  216. package/dist/components/textarea/textarea.svelte.d.ts +17 -0
  217. package/dist/components/textarea/types.d.ts +33 -0
  218. package/dist/components/textarea/types.js +1 -0
  219. package/dist/components/time-picker/docs/llms.md +47 -0
  220. package/dist/components/time-picker/index.d.ts +1 -0
  221. package/dist/components/time-picker/index.js +1 -0
  222. package/dist/components/time-picker/time-picker.svelte +336 -0
  223. package/dist/components/time-picker/time-picker.svelte.d.ts +4 -0
  224. package/dist/components/time-picker/types.d.ts +54 -0
  225. package/dist/components/time-picker/types.js +1 -0
  226. package/dist/components/toggle-switch/docs/llms.md +39 -0
  227. package/dist/components/toggle-switch/toggle-switch.svelte +151 -0
  228. package/dist/components/toggle-switch/toggle-switch.svelte.d.ts +17 -0
  229. package/dist/components/toggle-switch/types.d.ts +20 -0
  230. package/dist/components/toggle-switch/types.js +1 -0
  231. package/dist/components/tooltip/docs/llms.md +74 -0
  232. package/dist/components/tooltip/tooltip.svelte +246 -0
  233. package/dist/components/tooltip/tooltip.svelte.d.ts +18 -0
  234. package/dist/components/tooltip/types.d.ts +67 -0
  235. package/dist/components/tooltip/types.js +1 -0
  236. package/dist/components/tree-view/docs/llms.md +117 -0
  237. package/dist/components/tree-view/tree-view-item-content.svelte +158 -0
  238. package/dist/components/tree-view/tree-view-item-content.svelte.d.ts +4 -0
  239. package/dist/components/tree-view/tree-view-item.svelte +197 -0
  240. package/dist/components/tree-view/tree-view-item.svelte.d.ts +4 -0
  241. package/dist/components/tree-view/tree-view.svelte +220 -0
  242. package/dist/components/tree-view/tree-view.svelte.d.ts +4 -0
  243. package/dist/components/tree-view/tree-view.svelte.js +111 -0
  244. package/dist/components/tree-view/types.d.ts +185 -0
  245. package/dist/components/tree-view/types.js +1 -0
  246. package/dist/css/class-darkmode.css +186 -0
  247. package/dist/css/media-darkmode.css +189 -0
  248. package/dist/css/reset.css +53 -0
  249. package/dist/css/theme.css +276 -0
  250. package/dist/css/typography.css +67 -0
  251. package/dist/css/utils.d.ts +1 -0
  252. package/dist/css/utils.js +153 -0
  253. package/dist/index.d.ts +63 -0
  254. package/dist/index.js +129 -0
  255. package/dist/internal/components/dynamic-carousel/dynamic-carousel.svelte +449 -0
  256. package/dist/internal/components/dynamic-carousel/dynamic-carousel.svelte.d.ts +22 -0
  257. package/dist/internal/components/render-soc/render-soc.svelte +29 -0
  258. package/dist/internal/components/render-soc/render-soc.svelte.d.ts +30 -0
  259. package/dist/internal/constants.d.ts +26 -0
  260. package/dist/internal/constants.js +4036 -0
  261. package/dist/internal/context.d.ts +14 -0
  262. package/dist/internal/context.js +13 -0
  263. package/dist/internal/dom-utils.svelte.d.ts +6 -0
  264. package/dist/internal/dom-utils.svelte.js +46 -0
  265. package/dist/internal/floating-ui/floating.svelte.d.ts +15 -0
  266. package/dist/internal/floating-ui/floating.svelte.js +26 -0
  267. package/dist/internal/floating-ui/utils.d.ts +3 -0
  268. package/dist/internal/floating-ui/utils.js +30 -0
  269. package/dist/internal/index.d.ts +9 -0
  270. package/dist/internal/index.js +9 -0
  271. package/dist/internal/polyfills/expander.svelte.d.ts +21 -0
  272. package/dist/internal/polyfills/expander.svelte.js +96 -0
  273. package/dist/internal/transitions.d.ts +32 -0
  274. package/dist/internal/transitions.js +37 -0
  275. package/dist/internal/utils.d.ts +22 -0
  276. package/dist/internal/utils.js +63 -0
  277. package/dist/providers/fluentui-svelte/fluentui-svelte.d.ts +19 -0
  278. package/dist/providers/fluentui-svelte/fluentui-svelte.js +11 -0
  279. package/dist/providers/fluentui-svelte/fluentui-svelte.svelte +68 -0
  280. package/dist/providers/fluentui-svelte/fluentui-svelte.svelte.d.ts +9 -0
  281. package/dist/types/index.d.ts +117 -0
  282. package/dist/types/index.js +1 -0
  283. package/package.json +142 -0
@@ -0,0 +1,381 @@
1
+ <script lang="ts">
2
+ import type { DatePickerProps } from './types.js';
3
+ import { Flyout, Button, Divider } from '../../index.js';
4
+ import { on } from 'svelte/events';
5
+ import { tick } from 'svelte';
6
+ import { PREFIX } from '../../internal/constants.js';
7
+ import { floating } from '../../internal/index.js';
8
+ import { flip, hide, offset, shift } from '@floating-ui/dom';
9
+ import DynamicCarousel from '../../internal/components/dynamic-carousel/dynamic-carousel.svelte';
10
+ import { CheckmarkRegular, DismissRegular } from 'fluentui-icons-svelte';
11
+
12
+ let {
13
+ format = 'dd/MM/yyyy',
14
+ value = $bindable(''),
15
+ open = $bindable(false),
16
+ hideYears = false,
17
+ hideMonths = false,
18
+ hideDays = false,
19
+ minYear = new Date().getFullYear() - 100,
20
+ maxYear = new Date().getFullYear() + 100,
21
+ disabledDates = [],
22
+ disabledMonths = [],
23
+ disabledYears = [],
24
+ ref = $bindable(),
25
+ wrapperRef = $bindable(),
26
+ wrapperAttributes,
27
+ inputElement = $bindable(),
28
+ inputProps,
29
+ popupLabel = 'Choose a date',
30
+ ...attributes
31
+ }: DatePickerProps = $props();
32
+
33
+ const FALLBACK_ID = $props.id();
34
+ const POPUP_ID = `${PREFIX}datepicker-${FALLBACK_ID}-popup`;
35
+
36
+ let popupRef: HTMLElement | undefined = $state();
37
+
38
+ /** Closing always hands the focus back, so the keyboard never lands nowhere. */
39
+ function closePopup() {
40
+ open = false;
41
+ ref?.focus();
42
+ }
43
+
44
+ // Escape has to work from inside the popup too, and the popup is not a descendant
45
+ // of the trigger, so the listener goes on the document.
46
+ $effect(() => {
47
+ if (!open) return;
48
+
49
+ return on(document, 'keydown', (event: KeyboardEvent) => {
50
+ if (event.key !== 'Escape') return;
51
+ event.preventDefault();
52
+ closePopup();
53
+ });
54
+ });
55
+
56
+ // Move into the dialog on open: the first wheel, so the arrows work straight away.
57
+ $effect(() => {
58
+ if (!open || !popupRef) return;
59
+
60
+ tick().then(() => {
61
+ (popupRef?.querySelector<HTMLElement>('[role="listbox"]') ?? popupRef)?.focus();
62
+ });
63
+ });
64
+
65
+ type Column = 'month' | 'day' | 'year';
66
+
67
+ const MONTH_NAMES = [
68
+ 'January',
69
+ 'February',
70
+ 'March',
71
+ 'April',
72
+ 'May',
73
+ 'June',
74
+ 'July',
75
+ 'August',
76
+ 'September',
77
+ 'October',
78
+ 'November',
79
+ 'December'
80
+ ];
81
+ const LABELS: Record<Column, string> = { month: 'Month', day: 'Day', year: 'Year' };
82
+
83
+ // Working selection (actual values). Kept in sync with `value` while the popup is closed,
84
+ // so each open starts from the committed value and Cancel/dismiss discards edits.
85
+ let month = $state(new Date().getMonth()); // 0-11
86
+ let day = $state(new Date().getDate()); // 1-31 (raw intent; clamped for display via effectiveDay)
87
+ let year = $state(new Date().getFullYear());
88
+
89
+ // Descending list of selectable years within [minYear, maxYear].
90
+ const yearsList = $derived(Array.from({ length: Math.max(0, maxYear - minYear) + 1 }, (_, i) => maxYear - i));
91
+ const yearStrings = $derived(yearsList.map(String));
92
+
93
+ // Days available in the selected month/year (leap-year aware): new Date(y, m + 1, 0) === last day.
94
+ const daysInMonth = $derived(new Date(year, month + 1, 0).getDate());
95
+ const effectiveDay = $derived(Math.min(day, daysInMonth));
96
+ const dayList = $derived(Array.from({ length: daysInMonth }, (_, i) => (i + 1).toString().padStart(2, '0')));
97
+
98
+ const yearIdx = $derived(Math.max(0, yearsList.indexOf(year)));
99
+
100
+ // The trigger shows the committed value; `true` once `value` holds a full date.
101
+ const hasValue = $derived(/^\d{4}-\d{2}-\d{2}$/.test(value ?? ''));
102
+
103
+ // Parsed committed value used for the trigger label, so the trigger reflects `value`
104
+ const committed = $derived(parseValue(value));
105
+
106
+ // Columns to render, ordered by `format` and filtered by the hide* flags.
107
+ const columns = $derived.by<Column[]>(() => {
108
+ const base: Column[] = format === 'dd/MM/yyyy' ? ['day', 'month', 'year'] : ['month', 'day', 'year'];
109
+ const hidden: Record<Column, boolean> = { month: hideMonths, day: hideDays, year: hideYears };
110
+ return base.filter((c) => !hidden[c]);
111
+ });
112
+
113
+ // --- disabled** → disabled carousel indices (arrays of real, 0-based indices) ---
114
+ const disabledYearsSet = $derived(new Set(disabledYears.map((d) => d.getFullYear())));
115
+ const disabledYearIdx = $derived(yearsList.flatMap((y, i) => (disabledYearsSet.has(y) ? [i] : [])));
116
+ const disabledMonthIdx = $derived(
117
+ MONTH_NAMES.flatMap((_, i) =>
118
+ disabledMonths.some((d) => d.getFullYear() === year && d.getMonth() === i) ? [i] : []
119
+ )
120
+ );
121
+ const disabledDayIdx = $derived(
122
+ dayList.flatMap((_, i) =>
123
+ disabledDates.some((d) => d.getFullYear() === year && d.getMonth() === month && d.getDate() === i + 1) ? [i] : []
124
+ )
125
+ );
126
+
127
+ // Whether the currently composed date lands on a disabled value (blocks Confirm as a backstop;
128
+ // the carousels already skip disabled items while scrolling).
129
+ const isSelectionDisabled = $derived(
130
+ disabledYearsSet.has(year) ||
131
+ disabledMonths.some((d) => d.getFullYear() === year && d.getMonth() === month) ||
132
+ disabledDates.some((d) => d.getFullYear() === year && d.getMonth() === month && d.getDate() === effectiveDay)
133
+ );
134
+
135
+ const pad = (n: number) => n.toString().padStart(2, '0');
136
+
137
+ function parseValue(v: string) {
138
+ const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(v ?? '');
139
+ const t = new Date();
140
+ const raw = m ? { y: +m[1], mo: +m[2] - 1, d: +m[3] } : { y: t.getFullYear(), mo: t.getMonth(), d: t.getDate() };
141
+ const y = Math.min(maxYear, Math.max(minYear, raw.y));
142
+ const mo = Math.min(11, Math.max(0, raw.mo));
143
+ const dim = new Date(y, mo + 1, 0).getDate();
144
+ const d = Math.min(dim, Math.max(1, raw.d));
145
+ return { y, mo, d };
146
+ }
147
+
148
+ // Keep the working selection synced with `value` while the popup is closed.
149
+ $effect(() => {
150
+ if (!open) {
151
+ const { y, mo, d } = parseValue(value);
152
+ year = y;
153
+ month = mo;
154
+ day = d;
155
+ }
156
+ });
157
+
158
+ function confirm() {
159
+ value = `${year}-${pad(month + 1)}-${pad(effectiveDay)}`;
160
+ closePopup();
161
+ }
162
+
163
+ function cancel() {
164
+ closePopup();
165
+ }
166
+
167
+ // Trigger label per column
168
+ function display(col: Column): string {
169
+ if (!hasValue) return LABELS[col];
170
+ if (col === 'month') return MONTH_NAMES[committed.mo];
171
+ if (col === 'day') return pad(committed.d);
172
+ return String(committed.y);
173
+ }
174
+ </script>
175
+
176
+ <div class="fs-date-picker-wrapper" bind:this={wrapperRef} {...wrapperAttributes}>
177
+ <!-- A real button: Enter and Space, the disabled state and High Contrast styling all
178
+ come for free, and the hidden input cannot live inside it. -->
179
+ <button
180
+ type="button"
181
+ class="fs-date-picker"
182
+ aria-haspopup="dialog"
183
+ aria-expanded={open}
184
+ aria-controls={open ? POPUP_ID : undefined}
185
+ onclick={() => (open = !open)}
186
+ bind:this={ref}
187
+ {...attributes}
188
+ >
189
+ {#each columns as col, idx (col)}
190
+ {#if idx > 0}
191
+ <Divider as="span" vertical aria-hidden="true" />
192
+ {/if}
193
+ <span class="picker-content">{display(col)}</span>
194
+ {/each}
195
+ </button>
196
+
197
+ <!-- Carries the value into a form. `hidden` keeps it out of the tab order and out of
198
+ the accessibility tree, and a hidden control is still submitted. -->
199
+ <input type="date" hidden {value} bind:this={inputElement} {...inputProps} />
200
+ </div>
201
+
202
+ {#if open}
203
+ <Flyout
204
+ {@attach floating(ref, {
205
+ placement: 'bottom',
206
+ middleware: [
207
+ offset(({ rects }) => {
208
+ return -rects.reference.height / 2 - rects.floating.height / 2;
209
+ }),
210
+ flip(),
211
+ shift(),
212
+ hide()
213
+ ],
214
+ strategy: 'fixed'
215
+ })}
216
+ reference={ref}
217
+ id={POPUP_ID}
218
+ role="dialog"
219
+ aria-modal="true"
220
+ aria-label={popupLabel}
221
+ tabindex={-1}
222
+ bind:ref={popupRef}
223
+ class="date-picker-flyout"
224
+ >
225
+ <div class="carousel-wrapper">
226
+ <div class="selection-bar"></div>
227
+ {#each columns as col (col)}
228
+ <div class="carousel-col col-{col}">
229
+ {#if col === 'month'}
230
+ <DynamicCarousel
231
+ values={MONTH_NAMES}
232
+ selectedIndex={month}
233
+ disabledIndices={disabledMonthIdx}
234
+ onSelected={(_, i) => (month = i)}
235
+ />
236
+ {:else if col === 'day'}
237
+ {#key `${year}-${month}`}
238
+ <DynamicCarousel
239
+ values={dayList}
240
+ selectedIndex={effectiveDay - 1}
241
+ disabledIndices={disabledDayIdx}
242
+ onSelected={(_, i) => (day = i + 1)}
243
+ />
244
+ {/key}
245
+ {:else}
246
+ <DynamicCarousel
247
+ values={yearStrings}
248
+ selectedIndex={yearIdx}
249
+ disabledIndices={disabledYearIdx}
250
+ onSelected={(_, i) => (year = yearsList[i])}
251
+ />
252
+ {/if}
253
+ </div>
254
+ {/each}
255
+ </div>
256
+ <div class="action-bar">
257
+ <Button class="justify" appearance="subtle" aria-label="Confirm" disabled={isSelectionDisabled} onclick={confirm}>
258
+ <CheckmarkRegular />
259
+ </Button>
260
+ <Button class="justify" appearance="subtle" aria-label="Cancel" onclick={cancel}>
261
+ <DismissRegular />
262
+ </Button>
263
+ </div>
264
+ </Flyout>
265
+ {/if}
266
+
267
+ <style>
268
+ /* Layout-neutral: the button keeps the box the wrapper used to have. */
269
+ .fs-date-picker-wrapper {
270
+ display: contents;
271
+ }
272
+ .fs-date-picker {
273
+ display: flex;
274
+ position: relative;
275
+ border-radius: var(--fs-control-border-radius);
276
+ font-size: var(--fs-body-font-size);
277
+ line-height: var(--fs-body-line-height);
278
+ color: var(--fs-text-primary);
279
+ background: var(--fs-control-fill-default);
280
+ border: none;
281
+ padding: 1px;
282
+ cursor: pointer;
283
+ outline: none;
284
+ user-select: none;
285
+ &:after {
286
+ content: '';
287
+ inset: 0;
288
+ position: absolute;
289
+ padding: 0.063rem;
290
+ mask:
291
+ linear-gradient(#fff 0 0) content-box,
292
+ linear-gradient(#fff 0 0);
293
+ mask-composite: exclude;
294
+ border-radius: inherit;
295
+ }
296
+ &::after {
297
+ background: var(--fs-elevation-control-border);
298
+ }
299
+ &:hover {
300
+ background: var(--fs-control-fill-secondary);
301
+ }
302
+ &:active {
303
+ color: var(--fs-text-secondary);
304
+ &::after {
305
+ background: var(--fs-control-stroke-default);
306
+ }
307
+ }
308
+ &:disabled,
309
+ &.disabled {
310
+ background: var(--fs-control-fill-disabled) !important;
311
+ color: var(--fs-text-disabled) !important;
312
+ &::after {
313
+ background: var(--fs-control-stroke-default) !important;
314
+ }
315
+ }
316
+ & .picker-content {
317
+ display: flex;
318
+ align-items: center;
319
+ padding: 0.375rem 1.626rem 0.5rem 1.626rem;
320
+ &:first-child {
321
+ padding-right: 2rem;
322
+ }
323
+ }
324
+ }
325
+ :global(.date-picker-flyout) {
326
+ flex-direction: column;
327
+ position: fixed !important;
328
+ top: 0;
329
+ left: 0;
330
+ max-width: min-content;
331
+ padding: 0 !important;
332
+ z-index: 10;
333
+ & .wrapper {
334
+ gap: 0 !important;
335
+ }
336
+ & .carousel-wrapper {
337
+ display: flex;
338
+ position: relative;
339
+ width: max-content;
340
+ min-width: max-content;
341
+ padding: 0 0.4rem !important;
342
+ }
343
+ & .carousel-col {
344
+ &.col-month {
345
+ width: 130px;
346
+ }
347
+ &.col-day {
348
+ width: 60px;
349
+ }
350
+ &.col-year {
351
+ width: 80px;
352
+ }
353
+ & :global(.fs-dynamic-carousel) {
354
+ width: 100%;
355
+ }
356
+ &:not(:last-child) :global(.fs-dynamic-carousel-track) {
357
+ border-right: 1px solid var(--fs-control-stroke-default);
358
+ }
359
+ }
360
+ & .selection-bar {
361
+ position: absolute;
362
+ top: 50%;
363
+ left: 0.4rem;
364
+ width: calc(100% - 0.8rem);
365
+ height: 40px;
366
+ background: var(--fs-accent-fill-default);
367
+ transform: translateY(-50%);
368
+ border-radius: var(--fs-control-border-radius);
369
+ z-index: -1;
370
+ }
371
+ & .action-bar {
372
+ display: flex;
373
+ width: 100%;
374
+ padding: 0.2rem;
375
+ border-top: 1px solid var(--fs-control-stroke-default);
376
+ & :global(.justify) {
377
+ flex: 1;
378
+ }
379
+ }
380
+ }
381
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { DatePickerProps } from './types.ts';
2
+ declare const DatePicker: import("svelte").Component<DatePickerProps, {}, "ref" | "value" | "wrapperRef" | "open" | "inputElement">;
3
+ type DatePicker = ReturnType<typeof DatePicker>;
4
+ export default DatePicker;
@@ -0,0 +1,52 @@
1
+ # Date Picker
2
+
3
+ An input for selecting a date from carousel-style day, month, and year columns. The value uses the `YYYY-MM-DD` format.
4
+
5
+ ## Usage
6
+
7
+ ```svelte
8
+ <script>
9
+ import { DatePicker } from 'fluentui-svelte';
10
+
11
+ let value = $state('');
12
+ </script>
13
+
14
+ <DatePicker bind:value />
15
+ ```
16
+
17
+ ## Examples
18
+
19
+ ### Date Format
20
+
21
+ Use the `format` prop to switch between `dd/MM/yyyy` and `MM/dd/yyyy`.
22
+
23
+ ```svelte
24
+ <DatePicker format="dd/MM/yyyy" />
25
+ <DatePicker format="MM/dd/yyyy" />
26
+ ```
27
+
28
+ ### Hiding Columns
29
+
30
+ Use `hideDays`, `hideMonths`, or `hideYears` to hide individual selector columns (for example, a month/year-only picker).
31
+
32
+ ```svelte
33
+ <DatePicker hideDays />
34
+ ```
35
+
36
+ ## Component Props
37
+
38
+ | Name | Type | Description |
39
+ | ---------------- | ------------------------------ | ----------------------------------------------------------------------------- |
40
+ | `value` | bindable `string` | Selected date as `YYYY-MM-DD` (matches the underlying `<input type="date">`). |
41
+ | `format` | `'MM/dd/yyyy' \| 'dd/MM/yyyy'` | The display format of the date. Default: `'dd/MM/yyyy'`. |
42
+ | `open` | bindable `boolean` | Whether the picker flyout is open. |
43
+ | `hideYears` | `boolean` | Hide the year selector column. |
44
+ | `hideMonths` | `boolean` | Hide the month selector column. |
45
+ | `hideDays` | `boolean` | Hide the day selector column. |
46
+ | `minYear` | `number` | Earliest selectable year. Default: current year − 100. |
47
+ | `maxYear` | `number` | Latest selectable year. Default: current year. |
48
+ | `disabledDates` | `Date[]` | Specific days (year-month-day) that cannot be selected. |
49
+ | `disabledMonths` | `Date[]` | Specific months (by month + year) that cannot be selected. |
50
+ | `disabledYears` | `Date[]` | Specific years (by year) that cannot be selected. |
51
+ | `element` | bindable `HTMLElement` | The DOM reference of the date picker root element. |
52
+ | `inputElement` | bindable `HTMLInputElement` | The DOM reference of the underlying input element. |
@@ -0,0 +1 @@
1
+ export { default as DatePicker } from './date-picker.svelte';
@@ -0,0 +1 @@
1
+ export { default as DatePicker } from './date-picker.svelte';
@@ -0,0 +1,74 @@
1
+ import type { HTMLAttributes, HTMLButtonAttributes, HTMLInputAttributes } from 'svelte/elements';
2
+ /** @propsmith DatePickerProps */
3
+ export type DatePickerProps = {
4
+ /** Selected date as `YYYY-MM-DD` (matches the underlying `<input type="date">`).
5
+ * @default ''
6
+ * @bindable
7
+ */
8
+ value?: string;
9
+ /** The order the three columns are laid out in.
10
+ * @default 'dd/MM/yyyy'
11
+ */
12
+ format?: 'MM/dd/yyyy' | 'dd/MM/yyyy';
13
+ /** Controls the open state of the picker.
14
+ * @default false
15
+ * @bindable
16
+ */
17
+ open?: boolean;
18
+ /** Removes the year column.
19
+ * @default false
20
+ */
21
+ hideYears?: boolean;
22
+ /** Removes the month column.
23
+ * @default false
24
+ */
25
+ hideMonths?: boolean;
26
+ /** Removes the day column.
27
+ * @default false
28
+ */
29
+ hideDays?: boolean;
30
+ /** The first year offered by the year column.
31
+ * @default Current - 100
32
+ */
33
+ minYear?: number;
34
+ /** The last year offered by the year column.
35
+ * @default Current + 100
36
+ */
37
+ maxYear?: number;
38
+ /** Specific days (year-month-day) that cannot be selected.
39
+ * @default []
40
+ */
41
+ disabledDates?: Date[];
42
+ /** Specific months (by month + year) that cannot be selected.
43
+ * @default []
44
+ */
45
+ disabledMonths?: Date[];
46
+ /** Specific years (by year) that cannot be selected.
47
+ * @default []
48
+ */
49
+ disabledYears?: Date[];
50
+ /** The DOM reference of the trigger button.
51
+ * @bindable
52
+ */
53
+ ref?: HTMLButtonElement;
54
+ /** The DOM reference of the element wrapping the trigger and its input.
55
+ * @bindable
56
+ */
57
+ wrapperRef?: HTMLDivElement;
58
+ /** The attributes to spread on the wrapper element.
59
+ * @type HTMLAttributes
60
+ */
61
+ wrapperAttributes?: HTMLAttributes<HTMLDivElement>;
62
+ /** The DOM reference of the underlying input element.
63
+ * @bindable
64
+ */
65
+ inputElement?: HTMLInputElement;
66
+ /** HTML attributes for the underlying input element. Give it a `name` to submit the
67
+ * value with a form.
68
+ */
69
+ inputProps?: HTMLInputAttributes;
70
+ /** The accessible name of the popup, which is announced as a dialog.
71
+ * @default 'Choose a date'
72
+ */
73
+ popupLabel?: string;
74
+ } & HTMLButtonAttributes;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ <script lang="ts">
2
+ import { getTabspotAttributes } from 'tabspot';
3
+ import type { DialogActionsProps } from './types.js';
4
+
5
+ let { ref = $bindable(), position = 'end', fluid, children }: DialogActionsProps = $props();
6
+
7
+ // The bar is one tab stop holding a row of buttons: without the mover the root would
8
+ // take the tab stop and then offer no way to reach the rest of the buttons.
9
+ const tabspotAttributes = getTabspotAttributes({
10
+ root: {},
11
+ mover: { axis: 'horizontal' }
12
+ });
13
+ </script>
14
+
15
+ <div class={['dialog-actions', `justify-${position}`, { fluid }]} bind:this={ref} {...tabspotAttributes}>
16
+ {@render children?.()}
17
+ </div>
18
+
19
+ <style>
20
+ .dialog-actions {
21
+ grid-row: 3 / 3;
22
+ display: flex;
23
+ padding: 1.4rem 2rem;
24
+ gap: 0.5rem;
25
+ border-top: 1px solid var(--fs-card-stroke-default);
26
+ background: var(--fs-solid-background-base);
27
+ &.justify-start {
28
+ justify-content: flex-start;
29
+ }
30
+ &.justify-center {
31
+ justify-content: center;
32
+ }
33
+ &.justify-end {
34
+ justify-content: flex-end;
35
+ }
36
+ }
37
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { DialogActionsProps } from './types.ts';
2
+ declare const DialogActions: import("svelte").Component<DialogActionsProps, {}, "ref">;
3
+ type DialogActions = ReturnType<typeof DialogActions>;
4
+ export default DialogActions;
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ import type { DialogContentProps } from './types.js';
3
+
4
+ let { children }: DialogContentProps = $props();
5
+ </script>
6
+
7
+ <div class="dialog-content">
8
+ {@render children?.()}
9
+ </div>
10
+
11
+ <style>
12
+ .dialog-content {
13
+ grid-row: 2 / 2;
14
+ padding: 1.4rem 2rem 2rem 2rem;
15
+ overflow-y: auto;
16
+ }
17
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { DialogContentProps } from './types.ts';
2
+ declare const DialogContent: import("svelte").Component<DialogContentProps, {}, "">;
3
+ type DialogContent = ReturnType<typeof DialogContent>;
4
+ export default DialogContent;
@@ -0,0 +1,95 @@
1
+ <script lang="ts">
2
+ import { onClickOutside } from 'runed';
3
+ import type { DialogSurfaceProps } from './types.js';
4
+ import { getDialogContext } from './dialog.svelte.js';
5
+ import { Button } from '../../index.js';
6
+ import DismissFilled from 'fluentui-icons-svelte/DismissFilled.svelte';
7
+
8
+ let { ref = $bindable(), children, ...attributes }: DialogSurfaceProps = $props();
9
+
10
+ const CONTEXT = getDialogContext();
11
+
12
+ if (!CONTEXT) {
13
+ throw new Error('DialogSurface must be used within a Dialog component');
14
+ }
15
+
16
+ const { config, methods, state: _state } = CONTEXT;
17
+
18
+ const { closeDialog } = methods;
19
+
20
+ const { type } = config;
21
+
22
+ if (type !== 'alert') {
23
+ onClickOutside(
24
+ () => ref,
25
+ () => closeDialog()
26
+ );
27
+ }
28
+
29
+ $effect(() => {
30
+ if (!ref) return;
31
+ _state.dialogRef = ref;
32
+ });
33
+ </script>
34
+
35
+ <dialog open={_state.open} class="fs-dialog" aria-labelledby={_state.titleId} bind:this={ref} {...attributes}>
36
+ <div class="dialog-wrapper">
37
+ {#if config.type === 'non-modal'}
38
+ <Button class="close-icon" appearance="subtle" onclick={() => methods.closeDialog()} aria-label="Close dialog">
39
+ <DismissFilled />
40
+ </Button>
41
+ {/if}
42
+ {@render children?.()}
43
+ </div>
44
+ </dialog>
45
+
46
+ <style>
47
+ .fs-dialog {
48
+ position: fixed;
49
+ top: 50%;
50
+ left: 50%;
51
+ padding: 0;
52
+ transform: translate(-50%, -50%);
53
+ border: unset;
54
+ z-index: 1100;
55
+ width: 35vw;
56
+ min-width: 20rem;
57
+ overflow: visible;
58
+ background: var(--fs-solid-background-base);
59
+ border-radius: var(--fs-control-overlay-border-radius);
60
+ &::backdrop {
61
+ background-color: var(--fs-smoke-background-default);
62
+ backdrop-filter: blur(0.25rem);
63
+ }
64
+ & .dialog-wrapper {
65
+ position: relative;
66
+ box-shadow: var(--fs-shadow-dialog);
67
+ border-radius: var(--fs-control-overlay-border-radius);
68
+ border: 1px solid var(--fs-control-surface-stroke-flyout);
69
+ display: grid;
70
+ grid-template-rows: auto 1fr;
71
+ width: 100%;
72
+ max-height: 95vh;
73
+ overflow: hidden;
74
+ background: var(--fs-layer-alt);
75
+ &::before {
76
+ content: '';
77
+ position: absolute;
78
+ background: var(--fs-acrilic-noise);
79
+ background-size: 2.5rem;
80
+ filter: grayscale(1);
81
+ opacity: 0.065;
82
+ border-radius: calc(var(--fs-control-overlay-border-radius) - 0.063rem);
83
+ pointer-events: none;
84
+ inset: 0;
85
+ z-index: 0;
86
+ }
87
+ & :global(.close-icon) {
88
+ position: absolute;
89
+ padding: 0.5rem;
90
+ top: 0.5rem;
91
+ right: 0.5rem;
92
+ }
93
+ }
94
+ }
95
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { DialogSurfaceProps } from './types.ts';
2
+ declare const DialogSurface: import("svelte").Component<DialogSurfaceProps, {}, "ref">;
3
+ type DialogSurface = ReturnType<typeof DialogSurface>;
4
+ export default DialogSurface;