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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jorge Acosta
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,201 @@
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://github.com/JLAcostaEC/fluentui-svelte/blob/main/static/images/banner-white.png?raw=true" height="130" style="margin-bottom: 20px">
4
+ <img alt="Fluent UI Svelte Logo" src="https://github.com/JLAcostaEC/fluentui-svelte/blob/main/static/images/banner.png?raw=true" height="130" style="margin-bottom: 20px"/>
5
+ </picture>
6
+ </p>
7
+
8
+ <p align="center">
9
+ Modern components based on <a href="https://learn.microsoft.com/en-us/windows/apps/design/">Microsoft's Fluent UI</a> for Svelte 5.
10
+ </p>
11
+
12
+ ## Features
13
+
14
+ - Svelte 5 components!
15
+ - All components are accessible following [WAI-ARIA](https://www.w3.org/WAI/standards-guidelines/aria/) standards.
16
+ - Semantic HTML & Native Behavior Elements (Like: `<dialog></dialog>` or `<details></details>`)
17
+ - Reduced motion supported.
18
+ - Keyboard navigation handled by [Tabspot](https://github.com/JLAcostaEC/tabspot), so arrow keys, roving
19
+ tabindex and `aria-activedescendant` behave the same across every component.
20
+
21
+ ## Browser support
22
+
23
+ > [!IMPORTANT]
24
+ > The library relies on CSS features that, for now, ship only in Chromium-based browsers
25
+ > (Chrome, Edge, Opera, Brave). In Firefox and Safari some styles will not render correctly.
26
+
27
+ What the source actually depends on, and what it costs elsewhere:
28
+
29
+ | Feature | Used for | Outside Chromium |
30
+ | ----------------------- | ------------------------------------- | --------------------------------------------- |
31
+ | `field-sizing: content` | the TextArea growing with its content | no auto-grow; the textarea keeps its set size |
32
+ | `mask` (unprefixed) | acrylic noise, the tooltip arrow | needs `-webkit-mask`; overlays can look flat |
33
+ | `backdrop-filter` | the acrylic blur behind flyouts | falls back to a solid surface |
34
+ | `color-mix()`, `:has()` | tokens and state-driven styling | supported in current Firefox and Safari |
35
+
36
+ Behaviour and accessibility are unaffected — what degrades is visual. If you need those
37
+ browsers today, treat the current release as a known limitation rather than a target.
38
+
39
+ ## Installation
40
+
41
+ ```sh
42
+ pnpm add fluentui-svelte
43
+ # npm install fluentui-svelte
44
+ # yarn add fluentui-svelte
45
+ ```
46
+
47
+ Svelte 5 is the only peer dependency:
48
+
49
+ ```sh
50
+ pnpm add -D svelte@^5
51
+ ```
52
+
53
+ ## Setup
54
+
55
+ ### 1. Import the stylesheets
56
+
57
+ The library ships its theme as plain CSS. Import it once, at the root of your app
58
+ (`src/routes/+layout.svelte` in SvelteKit):
59
+
60
+ ```svelte
61
+ <script>
62
+ import 'fluentui-svelte/theme.css';
63
+ import 'fluentui-svelte/reset.css';
64
+ import 'fluentui-svelte/typography.css';
65
+ import 'fluentui-svelte/class-darkmode.css';
66
+ </script>
67
+ ```
68
+
69
+ | Stylesheet | What it does |
70
+ | -------------------- | ------------------------------------------------------------------------------ |
71
+ | `theme.css` | Design tokens. **Required** — every component reads its colours from here. |
72
+ | `reset.css` | Opinionated reset. Optional, but the components are designed against it. |
73
+ | `typography.css` | Fluent type ramp and the `.fs-*` text helpers. Optional. |
74
+ | `class-darkmode.css` | Dark theme, switched by a class on `<html>`. Pick this **or** the next. |
75
+ | `media-darkmode.css` | Dark theme, switched by `prefers-color-scheme`. Pick this **or** the previous. |
76
+
77
+ ### 2. Wrap your app in the provider
78
+
79
+ `FluentUISvelte` starts the keyboard-navigation engine and carries the reduced-motion and theme
80
+ preferences. Without it the components still render, but arrow-key navigation does not work:
81
+
82
+ ```svelte
83
+ <script>
84
+ import { FluentUISvelte } from 'fluentui-svelte';
85
+
86
+ let { children } = $props();
87
+ </script>
88
+
89
+ <FluentUISvelte>
90
+ {@render children()}
91
+ </FluentUISvelte>
92
+ ```
93
+
94
+ ## Usage
95
+
96
+ ```svelte
97
+ <script>
98
+ import { TextBox, Dialog, DialogTrigger, DialogSurface, DialogTitle, DialogContent } from 'fluentui-svelte';
99
+
100
+ let name = $state('');
101
+ </script>
102
+
103
+ <TextBox bind:value={name} placeholder="Your name" />
104
+
105
+ <Dialog>
106
+ <DialogTrigger>Say hello</DialogTrigger>
107
+ <DialogSurface>
108
+ <DialogTitle>Greeting</DialogTitle>
109
+ <DialogContent>Hello, {name || 'stranger'}.</DialogContent>
110
+ </DialogSurface>
111
+ </Dialog>
112
+ ```
113
+
114
+ Every component is exported from the package root. Prop tables live next to each component in
115
+ `src/lib/components/<name>/docs/`, and the same content is published for AI agents as
116
+ `llms.md` inside the package.
117
+
118
+ ### Running the documentation site
119
+
120
+ The docs site is the reference while the hosted version is being built:
121
+
122
+ ```sh
123
+ pnpm install
124
+ pnpm dev
125
+ ```
126
+
127
+ ## Component status
128
+
129
+ The documentation site is currently in the building phase, and not every component exported by
130
+ the library is documented yet.
131
+
132
+ Any undocumented component is **considered to be in the 0.x phase of development**. It may
133
+ receive breaking API changes, or be heavily reworked, before it is finalised. Components carry
134
+ a status badge in the navigation — `Beta`, `Experimental`, `Prototype` and `WIP` all mean the
135
+ API is still moving.
136
+
137
+ ## Bugs and feature requests
138
+
139
+ - **Found a bug?** Open a [bug report](https://github.com/JLAcostaEC/fluentui-svelte/issues/new?template=bug_report.yml).
140
+ Please include the browser, the Svelte version, and a minimal reproduction — a
141
+ [SvelteLab](https://sveltelab.dev) or StackBlitz link is ideal.
142
+ - **Want a component or a prop?** Open a
143
+ [feature request](https://github.com/JLAcostaEC/fluentui-svelte/issues/new?template=feature_request.yml)
144
+ and say which Fluent UI / WinUI control it maps to.
145
+ - **Security issue?** Please do not open a public issue — contact the maintainer directly.
146
+
147
+ Before filing, a quick search of the
148
+ [open issues](https://github.com/JLAcostaEC/fluentui-svelte/issues) saves everyone a round trip.
149
+
150
+ ## Contributing
151
+
152
+ This is a large project maintained by one person in their free time, so help is genuinely
153
+ welcome — code, documentation, translations and bug reports alike. Start with
154
+ [CONTRIBUTING.md](./CONTRIBUTING.md).
155
+
156
+ ```sh
157
+ pnpm install # install dependencies
158
+ pnpm dev # docs site with hot reload
159
+ pnpm check # svelte-check
160
+ pnpm lint # prettier + eslint
161
+ pnpm test:unit # vitest (browser mode, Chromium)
162
+ ```
163
+
164
+ UI strings live in `messages/en.json` (source of truth) and `messages/es.json`. Prop tables are
165
+ generated by [propsmith](https://www.npmjs.com/package/@jlacostaec/propsmith) — run `pnpm exec propsmith`
166
+ after changing a prop's JSDoc rather than editing the generated tables by hand.
167
+
168
+ ## Resources
169
+
170
+ > ### [Windows UI Kit - Figma File](https://www.figma.com/community/file/1440832812269040007)
171
+ >
172
+ > The Windows UI Toolkit is a resource for creating experiences on Windows. It contains a control library and examples of how those controls are used within the WinUI platform.
173
+
174
+ > ### Official Gallery Docs: [WinUI 3 Gallery](https://apps.microsoft.com/detail/9p3jfpwwdzrc)
175
+ >
176
+ > This app demonstrates all of the Windows UI 3 library controls and styles available to make a WinUI 3 app with the Windows App SDK.
177
+
178
+ > ### Documentation: [Microsoft's Fluent UI](https://learn.microsoft.com/en-us/windows/apps/design/)
179
+ >
180
+ > Design guidelines and UI code examples for creating Windows app experiences.
181
+
182
+ > ### [Fluent UI 2 Web - Figma File](https://www.figma.com/community/file/836828295772957889)
183
+ >
184
+ > Explore the next evolution of Microsoft's design system with the Fluent 2 Web UI Kit.
185
+
186
+ ## Credits
187
+
188
+ > - [Microsoft's Fluent UI](https://learn.microsoft.com/en-us/windows/apps/design/): Thank Microsoft for creating this masterpiece.
189
+ > - [Fluent Svelte](https://github.com/Tropix126/fluent-svelte): Which was unfortunately abandoned.
190
+
191
+ ## Notes
192
+
193
+ > [!IMPORTANT]
194
+ > This repository is not affiliated with or connected to Microsoft in any way. It is merely an open-source package that provides their components to use with Svelte 5 applications.
195
+
196
+ > [!NOTE]
197
+ > This package is not an exact port/reimplementation of Fluent UI (React, Web or any other official/unofficial variation), many features/APIs are not the same as those described in the Fluent UI documentation.
198
+
199
+ ## License
200
+
201
+ [MIT](./LICENSE) © Jorge Acosta
@@ -0,0 +1,2 @@
1
+ import type { AutoSuggestBoxContext } from './types.ts';
2
+ export declare const getAutoSuggestBoxContext: () => AutoSuggestBoxContext | null, setAutoSuggestBoxContext: (context: AutoSuggestBoxContext) => void;
@@ -0,0 +1,2 @@
1
+ import { createFSContext } from '../../internal/index.js';
2
+ export const [getAutoSuggestBoxContext, setAutoSuggestBoxContext] = createFSContext();
@@ -0,0 +1,457 @@
1
+ <script lang="ts">
2
+ import { onMount, tick } from 'svelte';
3
+ import { SvelteMap } from 'svelte/reactivity';
4
+ import { on } from 'svelte/events';
5
+ import { onClickOutside } from 'runed';
6
+ import { PREFIX } from '../../internal/constants.js';
7
+ import { setAutoSuggestBoxContext } from './auto-suggest-box.js';
8
+ import { Flyout, TextBox, ListView, ListViewItem } from '../../index.js';
9
+ import {
10
+ clearTabspotActive,
11
+ setTabspotActive,
12
+ setTabspotAttributes,
13
+ tabspotVirtual,
14
+ type TabspotNavigationEvent,
15
+ type TabspotNodeOptions
16
+ } from 'tabspot';
17
+ import { getGlobalFSContext } from '../../providers/fluentui-svelte/fluentui-svelte.js';
18
+ import type { AutoSuggestBoxContext, AutoSuggestBoxProps, OptionType } from './types.js';
19
+
20
+ const FALLBACK_ID = $props.id();
21
+ const ID = `${PREFIX}autosuggestbox-${FALLBACK_ID}`;
22
+ const INPUT_ID = `${ID}-input`;
23
+
24
+ let {
25
+ placeholder,
26
+ hideActionButtons,
27
+ type = 'search',
28
+ open = $bindable(false),
29
+ multiselect,
30
+ ref = $bindable(),
31
+ value = $bindable(''),
32
+ inputRef = $bindable(),
33
+ selectOnFocus,
34
+ virtualizer,
35
+ flyoutRef = $bindable(),
36
+ flyoutProps,
37
+ listViewRef = $bindable(),
38
+ selectedOptions = $bindable([]),
39
+ listViewProps,
40
+ showTextualMultiselect,
41
+ notFoundText = 'No results found',
42
+ maxItemsInView = 6,
43
+ textChanged,
44
+ querySubmitted,
45
+ suggestionChosen,
46
+ children
47
+ }: AutoSuggestBoxProps = $props();
48
+
49
+ // svelte-ignore state_referenced_locally
50
+ if (selectOnFocus && multiselect) throw new Error('selectOnFocus is not supported when multiselect is true');
51
+
52
+ let items = new SvelteMap<string, OptionType>();
53
+
54
+ let lastTypedValue: string = $state('');
55
+
56
+ let roundClass = $state('bottom');
57
+
58
+ let config: AutoSuggestBoxContext['config'] = $derived({
59
+ selectOnFocus,
60
+ virtualized: virtualizer,
61
+ multiselect
62
+ });
63
+
64
+ let activeOption: OptionType | null = $state(null);
65
+
66
+ /** The arrow key behind the move Tabspot is reporting, for `selectOnFocus` to hand on. */
67
+ let cursorKey: KeyboardEvent | undefined;
68
+
69
+ const OPTIONS = '.fs-autosuggest-option';
70
+
71
+ const NAVIGATION: TabspotNodeOptions = {
72
+ root: {},
73
+ mover: {
74
+ axis: 'vertical',
75
+ items: OPTIONS,
76
+ skip: "[aria-disabled='true']",
77
+ activation: {
78
+ mode: 'activedescendant',
79
+ controller: `#${INPUT_ID}`,
80
+ mark: { attribute: 'data-active' }
81
+ }
82
+ }
83
+ };
84
+
85
+ const _state: AutoSuggestBoxContext['state'] = {
86
+ get lastTypedValue() {
87
+ return lastTypedValue;
88
+ },
89
+ get open() {
90
+ return open;
91
+ },
92
+ set open(v) {
93
+ open = v;
94
+ },
95
+ get textBoxRef() {
96
+ return inputRef;
97
+ },
98
+ get activeOption() {
99
+ return activeOption;
100
+ },
101
+ set activeOption(v) {
102
+ activeOption = v;
103
+ }
104
+ };
105
+
106
+ let methods: AutoSuggestBoxContext['methods'] = {
107
+ setOption: ({ id, index, value, text, disabled }) => {
108
+ items.set(id, { id, index, value, text, disabled });
109
+ },
110
+ deleteOption: (id: string) => {
111
+ items.delete(id);
112
+ },
113
+ toggleSelection: (e, id) => {
114
+ const item = items.get(id);
115
+
116
+ if (item) {
117
+ if (multiselect) {
118
+ if (selectedOptions?.some((opt) => opt.id === id)) {
119
+ selectedOptions = selectedOptions.filter((opt) => opt.id !== id);
120
+ } else {
121
+ selectedOptions = [...selectedOptions, { id, value: item.value }];
122
+ }
123
+
124
+ if (showTextualMultiselect) value = selectedOptions.map((opt) => opt.value).join(', ');
125
+ } else {
126
+ selectedOptions = [item];
127
+
128
+ value = item.text || item.value;
129
+ }
130
+
131
+ suggestionChosen?.(e, item.value);
132
+ }
133
+ }
134
+ };
135
+
136
+ // svelte-ignore state_referenced_locally
137
+ setAutoSuggestBoxContext({ config, state: _state, methods, events: null });
138
+
139
+ /**
140
+ * Put the cursor on `option`, or send it home when there is none. `nearest`
141
+ * covers a match nothing can land on: the cursor walks to the closest option
142
+ * that can be, and only a list without one at all sends it home.
143
+ */
144
+ function pointCursorAt(option: OptionType | null) {
145
+ if (!listViewRef) return;
146
+
147
+ const element = option && listViewRef.querySelector<HTMLElement>(`#${CSS.escape(option.id)}`);
148
+ const pointed = element && setTabspotActive(element, { nearest: true }).ok;
149
+
150
+ // Tabspot reports the move it made through `navigate`; the empty cursor is
151
+ // the only one this box writes itself.
152
+ if (pointed) return;
153
+
154
+ clearTabspotActive(listViewRef);
155
+ readCursor(null, false);
156
+ }
157
+
158
+ function bestMatch(query: string): OptionType | null {
159
+ const needle = query.toLowerCase();
160
+ let best: OptionType | null = null;
161
+
162
+ for (const option of items.values()) {
163
+ if (!option.value.toLowerCase().startsWith(needle) && !option.text?.toLowerCase().startsWith(needle)) continue;
164
+ if (!best || option.index < best.index) best = option;
165
+ }
166
+
167
+ return best;
168
+ }
169
+
170
+ function readCursor(element: HTMLElement | null, keyed: boolean) {
171
+ const option = element ? (items.get(element.id) ?? null) : null;
172
+
173
+ if (option?.id === activeOption?.id) return;
174
+
175
+ activeOption = option;
176
+
177
+ if (option) document.getElementById(option.id)?.scrollIntoView({ block: 'nearest' });
178
+
179
+ // selectOnFocus mirrors the option into the text box, which must not happen
180
+ // while the user is the one typing in it.
181
+ if (!selectOnFocus || !keyed || !cursorKey) return;
182
+
183
+ if (option) {
184
+ methods.toggleSelection(cursorKey, option.id);
185
+ value = option.text || option.value;
186
+ } else {
187
+ value = lastTypedValue;
188
+ }
189
+ }
190
+
191
+ /** `atEdge` means the list ran out of options: hand the query back. */
192
+ function navigate(event: TabspotNavigationEvent) {
193
+ const keyed = event.direction !== 'programmatic';
194
+
195
+ if (!event.atEdge) {
196
+ readCursor(event.to, keyed);
197
+ return;
198
+ }
199
+
200
+ // The list ran out: send the cursor home and give the user their query back.
201
+ event.preventDefault();
202
+
203
+ if (listViewRef) clearTabspotActive(listViewRef);
204
+
205
+ readCursor(null, keyed);
206
+ }
207
+
208
+ /** The two keys that are the box's own business rather than the list's. */
209
+ function handleKeyDown(e: KeyboardEvent) {
210
+ if (e.target !== inputRef) return;
211
+
212
+ if (e.key === 'Escape') {
213
+ e.preventDefault();
214
+ open = false;
215
+ return;
216
+ }
217
+
218
+ if (e.key !== 'Enter') return;
219
+
220
+ e.preventDefault();
221
+
222
+ if (activeOption) {
223
+ methods.toggleSelection(e, activeOption.id);
224
+ open = false;
225
+ } else {
226
+ querySubmitted?.(e, value);
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Whether the next arrow would *enter* the list rather than move inside it, and
232
+ * would enter it in the wrong place if left alone.
233
+ */
234
+ function entryNeedsScrolling() {
235
+ return !activeOption && (!open || !!virtualizer);
236
+ }
237
+
238
+ /** Open, scroll to the end the key is heading for, then let Tabspot enter. */
239
+ async function enterList(key: 'ArrowUp' | 'ArrowDown') {
240
+ open = true;
241
+ await tick();
242
+
243
+ await (key === 'ArrowUp' ? virtualizer?.scrollToBottom?.() : virtualizer?.scrollToTop?.());
244
+ await tick();
245
+
246
+ // The key is replayed rather than turned into a `setTabspotActive` call on the
247
+ // end of the list: a second arrow pressed while this one is still in flight is
248
+ // a second move, and two cursor sets onto the same option would be one.
249
+ // Untrusted, so it passes straight through to Tabspot this time.
250
+ inputRef?.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true, cancelable: true }));
251
+ }
252
+
253
+ async function handleTextChanged(e: InputEvent, val: string) {
254
+ if (val.length > 0 && !open) open = true;
255
+ lastTypedValue = val;
256
+ textChanged?.(e, val);
257
+
258
+ await tick();
259
+
260
+ // Automatic selection, per the WAI-ARIA combobox pattern: the query points the
261
+ // cursor at the suggestion Enter would choose.
262
+ pointCursorAt(val ? bestMatch(val) : null);
263
+ }
264
+
265
+ function handleClear() {
266
+ value = '';
267
+ lastTypedValue = '';
268
+ open = false;
269
+ }
270
+
271
+ function handlePlacementChange(newPlacement: string) {
272
+ roundClass = newPlacement.includes('-') ? newPlacement.split('-')[0] : newPlacement;
273
+ }
274
+
275
+ onClickOutside(
276
+ () => ref,
277
+ () => (open = false)
278
+ );
279
+
280
+ onMount(() => {
281
+ if (!inputRef) return;
282
+
283
+ const off = [
284
+ on(inputRef, 'keydown', handleKeyDown),
285
+ // Tabspot listens on `document` in the capture phase,
286
+ // `window` comes first on the capture path, to note the
287
+ // key that caused a move,and to hold a move back until
288
+ // the list can answer it correctly.
289
+ on(
290
+ window,
291
+ 'keydown',
292
+ (event) => {
293
+ if (event.target !== inputRef || !event.isTrusted) return;
294
+
295
+ const key = event.key;
296
+
297
+ if (key !== 'ArrowUp' && key !== 'ArrowDown') return;
298
+
299
+ cursorKey = event;
300
+
301
+ if (!entryNeedsScrolling()) return;
302
+
303
+ // Keep the key from Tabspot entirely: it would enter on the wrong
304
+ // item, and there would be no undoing that from here.
305
+ event.preventDefault();
306
+ event.stopPropagation();
307
+
308
+ enterList(key);
309
+ },
310
+ { capture: true }
311
+ )
312
+ ];
313
+
314
+ return () => {
315
+ activeOption = null;
316
+ off.forEach((unsubscribe) => unsubscribe());
317
+ };
318
+ });
319
+
320
+ const globalContext = getGlobalFSContext();
321
+
322
+ // Closing takes the flyout down, and leaving `activeOption` behind,
323
+ // remove it..
324
+ $effect(() => {
325
+ if (!open) activeOption = null;
326
+ });
327
+
328
+ $effect(() => {
329
+ if (!open || !listViewRef) return;
330
+
331
+ setTabspotAttributes({ element: listViewRef, config: NAVIGATION });
332
+
333
+ const instance = globalContext?.state.tabspotInstance;
334
+ const detach = instance?.subscribe(listViewRef, navigate);
335
+
336
+ if (!virtualizer || !virtualizer?.scrollToIndex) return detach;
337
+
338
+ const detachVirtual = tabspotVirtual(listViewRef, {
339
+ count: () => virtualizer.size,
340
+ scrollToIndex: (index) => virtualizer.scrollToIndex?.(index),
341
+ tick
342
+ });
343
+
344
+ return () => {
345
+ detach?.();
346
+ detachVirtual();
347
+ };
348
+ });
349
+ </script>
350
+
351
+ <div
352
+ class={['fs-autosuggestbox', { open }]}
353
+ style="--max-items: {maxItemsInView}; --current-items: {items.size};"
354
+ bind:this={ref}
355
+ >
356
+ <TextBox
357
+ {type}
358
+ {placeholder}
359
+ {hideActionButtons}
360
+ id={INPUT_ID}
361
+ bind:value
362
+ bind:ref={inputRef}
363
+ querySubmitted={(e) => querySubmitted?.(e, value)}
364
+ textChanged={(e) => handleTextChanged(e, value)}
365
+ onClear={handleClear}
366
+ role="combobox"
367
+ aria-expanded={open}
368
+ aria-autocomplete="list"
369
+ aria-controls={open ? `${ID}-list` : undefined}
370
+ aria-haspopup="listbox"
371
+ class="square-{roundClass}"
372
+ />
373
+ {#if open}
374
+ <Flyout
375
+ floating
376
+ offset={0}
377
+ reference={ref}
378
+ bind:ref={flyoutRef}
379
+ roundCorners="bottom"
380
+ placementConfig={{ allowedPlacements: ['bottom', 'top'] }}
381
+ onPlacementChange={(place) => handlePlacementChange(place)}
382
+ class={['fs-autosuggestbox-flyout', roundClass === 'top' && 'top-shadow', flyoutProps?.class]}
383
+ {...flyoutProps}
384
+ >
385
+ <ListView
386
+ role="listbox"
387
+ navigationMode="items"
388
+ disableTabspot
389
+ id="{ID}-list"
390
+ selectedItems={Array.from(selectedOptions.values()).map((opt) => opt.value)}
391
+ bind:ref={listViewRef}
392
+ {...listViewProps as any}
393
+ >
394
+ {#if items.size === 0}
395
+ <ListViewItem disabled>{notFoundText}</ListViewItem>
396
+ {/if}
397
+ {@render children?.()}
398
+ </ListView>
399
+ </Flyout>
400
+ {/if}
401
+ </div>
402
+
403
+ <style>
404
+ .fs-autosuggestbox {
405
+ position: relative;
406
+ width: 100%;
407
+ & :global(.fs-flyout) {
408
+ position: absolute;
409
+ padding: 0;
410
+ top: 0;
411
+ left: -1px;
412
+ width: calc(100% + 2px);
413
+ }
414
+ &.open {
415
+ & :global(.fs-flyout) {
416
+ border-color: rgba(0, 0, 0, 0.06);
417
+ z-index: 10;
418
+ max-height: calc(var(--max-items) * 2.7rem);
419
+ height: calc(var(--current-items) * 2.7rem);
420
+ min-height: 3.4rem;
421
+ overflow: hidden;
422
+ &:global(.top-shadow) {
423
+ box-shadow: 0 -0.5rem 0.8rem #00000024;
424
+ }
425
+ }
426
+ & :global(.fs-list-view) {
427
+ overflow-y: auto;
428
+ padding: 4px;
429
+ }
430
+ & :global(.fs-textbox) {
431
+ &:global(.square-top) {
432
+ border-top-left-radius: 0;
433
+ border-top-right-radius: 0;
434
+ &::after,
435
+ &::before {
436
+ border-top-left-radius: 0;
437
+ border-top-right-radius: 0;
438
+ }
439
+ }
440
+ &:global(.square-bottom) {
441
+ border-bottom-left-radius: 0;
442
+ border-bottom-right-radius: 0;
443
+ &::after,
444
+ &::before {
445
+ border-bottom-left-radius: 0;
446
+ border-bottom-right-radius: 0;
447
+ }
448
+ }
449
+ }
450
+ & :global(.fs-textbox:after) {
451
+ width: calc(100% + 0.125rem);
452
+ left: -0.063rem;
453
+ background-color: var(--fs-accent-fill-default);
454
+ }
455
+ }
456
+ }
457
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { AutoSuggestBoxProps } from './types.ts';
2
+ declare const AutoSuggestBox: import("svelte").Component<AutoSuggestBoxProps, {}, "ref" | "value" | "open" | "inputRef" | "flyoutRef" | "listViewRef" | "selectedOptions">;
3
+ type AutoSuggestBox = ReturnType<typeof AutoSuggestBox>;
4
+ export default AutoSuggestBox;