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,121 @@
1
+ <script lang="ts">
2
+ import { on } from 'svelte/events';
3
+ import { useDebounce } from 'runed';
4
+ import { onMount, tick } from 'svelte';
5
+ import { COMPONENT_NAME, getMenuContext } from './menu.svelte.js';
6
+ import ChevronRightFilled from 'fluentui-icons-svelte/ChevronRightFilled.svelte';
7
+ import type { MenuTriggerProps } from './types.js';
8
+ import type { ButtonProps } from '../button/types.js';
9
+
10
+ let FALLBACK_ID = $props.id();
11
+
12
+ let { disabled, shape, icon: Icon, children }: MenuTriggerProps = $props();
13
+
14
+ const context = getMenuContext();
15
+
16
+ if (!context) throw new Error(`No MenuContext found for ${COMPONENT_NAME}.`);
17
+
18
+ const { state: _state } = context;
19
+
20
+ const { toggle, open, close } = context.methods;
21
+
22
+ const { id, isSubMenu, openOnHover, openingDelay, parentHasCheckmarks, parentHasIcons } = context.config;
23
+
24
+ const paddingLeft = $derived(1.25 * (+!!(parentHasIcons && !Icon) + +!!parentHasCheckmarks) + 0.9);
25
+
26
+ const debounced = useDebounce((e: MouseEvent) => {
27
+ if (e.type === 'mouseover' && !_state.open) {
28
+ return open(e);
29
+ }
30
+
31
+ // Where the mouse is moving to
32
+ const to = e.relatedTarget as HTMLElement | null;
33
+
34
+ if (to && !to.closest(`#fs-menu-popover-${id}`) && to.parentElement !== _state.ref && !_state.locked) close(e);
35
+ }, openingDelay || 0);
36
+
37
+ onMount(() => {
38
+ const off = on(document, 'click', async (e: MouseEvent) => {
39
+ const target = e.target as HTMLElement;
40
+
41
+ if (target.closest(`#fs-menu-popover-${id}`)) {
42
+ return;
43
+ }
44
+
45
+ if (target === _state.ref) {
46
+ _state.locked = !_state.locked;
47
+ toggle(e);
48
+ // Focus the first button in the popover when opening
49
+ if (_state.open) {
50
+ await tick();
51
+ (document.querySelector(`#fs-menu-popover-${id} .fs-button:first-of-type`) as HTMLElement)?.focus();
52
+ }
53
+ return;
54
+ }
55
+
56
+ if (!target.closest(`#fs-menu-popover-${id}`)) {
57
+ _state.locked = false;
58
+ close(e);
59
+ }
60
+ });
61
+
62
+ return () => off();
63
+ });
64
+
65
+ const menuTriggerProps: Omit<ButtonProps<'button'>, 'ref'> = $derived({
66
+ disabled,
67
+ shape,
68
+ 'aria-controls': `fs-menu-popover-${id}`,
69
+ id: `fs-${isSubMenu ? 'submenu' : 'menu'}-trigger-${id}`,
70
+ class: `fs-${isSubMenu ? 'submenu' : 'menu'}-trigger ${
71
+ (parentHasIcons || parentHasCheckmarks) && isSubMenu ? 'align' : ''
72
+ } trigger-id-${FALLBACK_ID}`,
73
+ style: `--padding-left: ${paddingLeft}rem;`,
74
+ onmouseover: openOnHover && !disabled ? (e: MouseEvent) => debounced(e) : undefined,
75
+ onmouseleave: openOnHover && !disabled ? (e: MouseEvent) => debounced(e) : undefined,
76
+ 'aria-haspopup': 'menu',
77
+ 'aria-expanded': _state.open ? 'true' : 'false',
78
+ role: isSubMenu ? 'menuitem' : undefined,
79
+ appearance: isSubMenu ? 'subtle' : undefined,
80
+ isMenuButton: isSubMenu,
81
+ indicatorIcon: isSubMenu ? ChevronRightFilled : undefined
82
+ });
83
+
84
+ // svelte-ignore state_referenced_locally
85
+ const primaryButtonProps: ButtonProps<'button'> = $state.raw({
86
+ disabled,
87
+ shape
88
+ });
89
+ </script>
90
+
91
+ {@render children?.({ state: context.state, menuTriggerProps, primaryButtonProps })}
92
+
93
+ <style>
94
+ :global(.fs-button.fs-submenu-trigger) {
95
+ width: 100%;
96
+ padding: 0.375rem 0.45rem;
97
+ border-radius: var(--fs-control-inner-border-radius);
98
+ justify-content: space-between;
99
+ font-size: var(--fs-body2-font-size);
100
+ line-height: var(--fs-body2-line-height);
101
+ & :global(svg) {
102
+ width: 1rem;
103
+ height: 1rem;
104
+ height: auto;
105
+ fill: currentColor;
106
+ }
107
+ & :global(svg.indicator) {
108
+ width: 1rem;
109
+ height: auto;
110
+ margin-left: 0.6rem;
111
+ margin-right: 0.2rem;
112
+ pointer-events: none;
113
+ }
114
+ &:global(:is(:disabled, [disabled]) :is(svg, .indicator)) {
115
+ color: var(--fs-text-disabled);
116
+ }
117
+ &:global(.align) {
118
+ padding-left: var(--padding-left) !important;
119
+ }
120
+ }
121
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { MenuTriggerProps } from './types.ts';
2
+ declare const MenuTrigger: import("svelte").Component<MenuTriggerProps, {}, "">;
3
+ type MenuTrigger = ReturnType<typeof MenuTrigger>;
4
+ export default MenuTrigger;
@@ -0,0 +1,124 @@
1
+ <script lang="ts">
2
+ import { getMenuContext, setMenuContext } from './menu.svelte.js';
3
+ import type { MenuContext, MenuProps, MenuState } from './types.js';
4
+
5
+ let {
6
+ checkedValues = {} as Record<string, string[]>,
7
+ onCheckedValueChange,
8
+ hasCheckmarks,
9
+ hasIcons,
10
+ openingDelay = 0,
11
+ open,
12
+ onOpenChange,
13
+ openOnHover = false,
14
+ persistOnItemActivation = false,
15
+ positionConfig,
16
+ children
17
+ }: MenuProps = $props();
18
+
19
+ const id = $props.id();
20
+
21
+ // Check if is being used as submenu
22
+ const PARENT_CONTEXT = getMenuContext();
23
+
24
+ let ref: HTMLElement | null = $state(null);
25
+
26
+ let locked = $state(false);
27
+
28
+ let config: MenuContext['config'] = $derived.by(() => {
29
+ if (!PARENT_CONTEXT) {
30
+ return {
31
+ id,
32
+ isSubMenu: false,
33
+ hasCheckmarks,
34
+ hasIcons,
35
+ openingDelay,
36
+ openOnHover,
37
+ persistOnItemActivation,
38
+ positionConfig,
39
+ parentHasCheckmarks: false,
40
+ parentHasIcons: false
41
+ };
42
+ } else {
43
+ return {
44
+ ...PARENT_CONTEXT.config,
45
+ isSubMenu: true,
46
+ id,
47
+ hasCheckmarks,
48
+ hasIcons,
49
+ parentHasCheckmarks: !!PARENT_CONTEXT.config.hasCheckmarks,
50
+ parentHasIcons: !!PARENT_CONTEXT.config.hasIcons
51
+ };
52
+ }
53
+ });
54
+
55
+ const _state: MenuState = {
56
+ get open() {
57
+ return open;
58
+ },
59
+ set open(v) {
60
+ open = v;
61
+ },
62
+ get ref() {
63
+ return ref;
64
+ },
65
+ set ref(v) {
66
+ ref = v;
67
+ },
68
+ get locked() {
69
+ return locked;
70
+ },
71
+ set locked(v) {
72
+ locked = v;
73
+ },
74
+ get checkedValues() {
75
+ return checkedValues;
76
+ },
77
+ set checkedValues(v) {
78
+ checkedValues = v;
79
+ }
80
+ };
81
+
82
+ let events: MenuContext['events'] = $derived.by(() => {
83
+ if (!PARENT_CONTEXT) {
84
+ return {
85
+ onCheckedValueChange: (e, value) => onCheckedValueChange?.(e, value),
86
+ onOpenChange: (e, opened) => onOpenChange?.(e, opened)
87
+ };
88
+ } else {
89
+ return PARENT_CONTEXT.events;
90
+ }
91
+ });
92
+
93
+ let methods: MenuContext['methods'] = $derived({
94
+ toggle: (e: Event) => {
95
+ _state.open = !_state.open;
96
+ events.onOpenChange?.(e, _state.open);
97
+ },
98
+ open: (e: Event) => {
99
+ _state.open = true;
100
+ events.onOpenChange?.(e, _state.open);
101
+ },
102
+ close: (e: Event) => {
103
+ _state.open = false;
104
+ events.onOpenChange?.(e, _state.open);
105
+ },
106
+ toggleCheckbox: (e: Event, value: string, name: string) => {
107
+ const current = _state.checkedValues?.[name] ?? [];
108
+ const updated = current.includes(value) ? current.filter((v) => v !== value) : [...current, value];
109
+ _state.checkedValues = { ..._state.checkedValues, [name]: updated };
110
+ events.onCheckedValueChange?.(e, _state.checkedValues!);
111
+ },
112
+ toggleRadio: (e: Event, value: string, name: string) => {
113
+ if (!_state.checkedValues?.[name]?.includes(value)) {
114
+ _state.checkedValues = { ..._state.checkedValues, [name]: [value] };
115
+ events.onCheckedValueChange?.(e, _state.checkedValues!);
116
+ }
117
+ }
118
+ });
119
+
120
+ // svelte-ignore state_referenced_locally
121
+ setMenuContext({ config, state: _state, events, methods });
122
+ </script>
123
+
124
+ {@render children?.()}
@@ -0,0 +1,4 @@
1
+ import type { MenuProps } from './types.ts';
2
+ declare const Menu: import("svelte").Component<MenuProps, {}, "">;
3
+ type Menu = ReturnType<typeof Menu>;
4
+ export default Menu;
@@ -0,0 +1,77 @@
1
+ import { createFSContext, getOffset } from '../../internal/index.js';
2
+ export const COMPONENT_NAME = 'fs-menu-';
3
+ export const [getMenuContext, setMenuContext] = createFSContext();
4
+ /** Distance, in pixels, the popover keeps from the anchor edge when a vertical intro starts. */
5
+ const VERTICAL_INTRO_DISTANCE = 10;
6
+ /** Distance, in pixels, a horizontal intro slides out of the anchor edge. */
7
+ const HORIZONTAL_INTRO_DISTANCE = 15;
8
+ /**
9
+ * Rect of an element relative to its `offsetParent`, which is the coordinate space an absolutely
10
+ * positioned sibling lives in. It matches what FloatingUI resolves for such an element.
11
+ */
12
+ const getOffsetRect = (element) => ({
13
+ x: element.offsetLeft,
14
+ y: element.offsetTop,
15
+ top: element.offsetTop,
16
+ left: element.offsetLeft,
17
+ right: element.offsetLeft + element.offsetWidth,
18
+ bottom: element.offsetTop + element.offsetHeight,
19
+ width: element.offsetWidth,
20
+ height: element.offsetHeight
21
+ });
22
+ /**
23
+ * Resolves the coordinates FloatingUI settles the popover at for the given placement, in the same
24
+ * space as the anchor rect it is given: the viewport for a menu, the parent popover for a submenu.
25
+ *
26
+ * The `in:` transition runs before FloatingUI has computed anything, so the animation has to start
27
+ * and end on these coordinates for the handover to be seamless.
28
+ */
29
+ export const getIntroCoords = ({ placement, anchor, floating, offset = 0 }) => {
30
+ const [side, alignment] = placement.split('-');
31
+ const isVertical = side === 'top' || side === 'bottom';
32
+ // Main axis: the popover sits right outside the anchor edge, pushed away by the `offset` middleware.
33
+ const main = side === 'top'
34
+ ? anchor.top - floating.height - offset
35
+ : side === 'bottom'
36
+ ? anchor.bottom + offset
37
+ : side === 'left'
38
+ ? anchor.left - floating.width - offset
39
+ : anchor.right + offset;
40
+ // Cross axis: flush with the anchor start or end edge, centered when the placement has no alignment.
41
+ const anchorStart = isVertical ? anchor.left : anchor.top;
42
+ const anchorSize = isVertical ? anchor.width : anchor.height;
43
+ const floatingSize = isVertical ? floating.width : floating.height;
44
+ const cross = alignment === 'start'
45
+ ? anchorStart
46
+ : alignment === 'end'
47
+ ? anchorStart + anchorSize - floatingSize
48
+ : anchorStart + (anchorSize - floatingSize) / 2;
49
+ return isVertical ? { top: main, left: cross } : { top: cross, left: main };
50
+ };
51
+ /**
52
+ * Builds the `flyToOffset` options for the popover intro: the popover emerges from the anchor and
53
+ * lands exactly where FloatingUI will keep it (or should, if element is not crossing the viewport), so nothing jumps when the transition ends.
54
+ */
55
+ export const getIntroTransition = ({ config, anchor, element, ref, isSubMenu = false }) => {
56
+ const offset = getOffset(config);
57
+ const placement = config.placement ?? (isSubMenu ? 'right-start' : 'bottom-start');
58
+ const [side] = placement.split('-');
59
+ const isVertical = side === 'top' || side === 'bottom';
60
+ // A submenu is absolutely positioned inside its parent popover, so it is placed in the coordinate
61
+ // space of that popover instead of the viewport, the very same space FloatingUI resolves it in.
62
+ const rect = isSubMenu && ref instanceof HTMLElement ? getOffsetRect(ref) : anchor;
63
+ const { top, left } = getIntroCoords({
64
+ placement,
65
+ anchor: rect,
66
+ floating: element?.getBoundingClientRect() ?? { width: 0, height: 0 },
67
+ offset
68
+ });
69
+ // The popover starts overlapping the anchor and slides out of it, towards the resolved
70
+ // coordinates: a vertical intro emerges from the whole anchor, a horizontal one just peeks out.
71
+ const travel = isVertical ? rect.height + offset - VERTICAL_INTRO_DISTANCE : HORIZONTAL_INTRO_DISTANCE;
72
+ const distance = side === 'top' || side === 'left' ? travel : -travel;
73
+ return {
74
+ ...(isVertical ? { y: distance } : { x: distance }),
75
+ css: `top: ${top}px; left: ${left}px;`
76
+ };
77
+ };
@@ -0,0 +1,127 @@
1
+ import type { ComputePositionConfig, VirtualElement } from '@floating-ui/dom';
2
+ import type { FSContext } from '../../internal/index.js';
3
+ import type { Component, ComponentProps, Snippet } from 'svelte';
4
+ import type { NoUndefinedField, Shapes } from '../../types/index.js';
5
+ import type { Button } from '../../index.ts';
6
+ import type { ButtonProps } from '../button/types.ts';
7
+ export type MenuContext = FSContext<{
8
+ id: string;
9
+ isSubMenu: boolean;
10
+ parentHasCheckmarks: boolean;
11
+ parentHasIcons: boolean;
12
+ } & Omit<MenuProps, 'element' | 'children' | 'onOpenChange' | 'open' | 'onCheckedValueChange' | 'checkedValues'>, MenuState, NoUndefinedField<Pick<MenuProps, 'onCheckedValueChange' | 'onOpenChange'>>, {
13
+ toggle: (e: Event) => void;
14
+ open: (e: Event) => void;
15
+ close: (e: Event) => void;
16
+ toggleCheckbox: (e: Event, value: string, name: string) => void;
17
+ toggleRadio: (e: Event, value: string, name: string) => void;
18
+ }>;
19
+ export type MenuState = {
20
+ open: boolean | undefined;
21
+ ref: HTMLElement | VirtualElement | null | undefined;
22
+ locked: boolean;
23
+ checkedValues: MenuProps['checkedValues'];
24
+ };
25
+ /** @propsmith MenuProps */
26
+ export type MenuProps = {
27
+ /** The position configuration handed over to floating-ui.
28
+ * @type ComputePositionConfig
29
+ */
30
+ positionConfig?: Partial<ComputePositionConfig>;
31
+ /** Reserves the room every item needs for an icon, so the labels line up. */
32
+ hasIcons?: boolean;
33
+ /** Reserves the room every item needs for a checkmark, so the labels line up. */
34
+ hasCheckmarks?: boolean;
35
+ /** Controls the open state of the menu. */
36
+ open?: boolean;
37
+ /** Called whenever the menu opens or closes. */
38
+ onOpenChange?: (e: Event, open: boolean) => void;
39
+ /** The checked values of the menu, grouped by the `name` of each item.
40
+ * @default {}
41
+ */
42
+ checkedValues?: Record<string, string[]>;
43
+ /** Called whenever a checkbox or radio item changes.
44
+ * @type (e: Event, checkedValues: Record<string, string[]>) => void
45
+ */
46
+ onCheckedValueChange?: (e: Event, checkedValues: Record<string, string[]>) => void;
47
+ /** Opens the menu on hover instead of on click.
48
+ * @default false
49
+ */
50
+ openOnHover?: boolean;
51
+ /** How long to wait, in milliseconds, before opening the menu.
52
+ * @default 0
53
+ */
54
+ openingDelay?: number;
55
+ /** Keeps the menu open after an item is activated.
56
+ * @default false
57
+ */
58
+ persistOnItemActivation?: boolean;
59
+ /** The parts of the menu: a trigger and a popover. */
60
+ children?: Snippet;
61
+ };
62
+ /** @propsmith MenuTriggerProps */
63
+ export type MenuTriggerProps = {
64
+ /** Disables the user interaction. */
65
+ disabled?: boolean;
66
+ /** The trigger can be rounded, circular, or square. */
67
+ shape?: Shapes;
68
+ /** An optional icon for the trigger.
69
+ * @type Snippet | Component
70
+ */
71
+ icon?: Snippet | Component;
72
+ /** A custom trigger. It receives the menu state and the props to spread on your own buttons.
73
+ * @type Snippet
74
+ */
75
+ children?: Snippet<[
76
+ {
77
+ state: MenuState;
78
+ menuTriggerProps: ButtonProps<'button'>;
79
+ primaryButtonProps: ButtonProps<'button'>;
80
+ }
81
+ ]>;
82
+ };
83
+ /** @propsmith MenuGroupProps */
84
+ export type MenuGroupProps = {
85
+ /** The DOM reference of the group element.
86
+ * @bindable
87
+ */
88
+ ref?: HTMLDivElement;
89
+ /** The label of the group. */
90
+ header?: string;
91
+ /** The items of the group. */
92
+ children: Snippet;
93
+ };
94
+ /** @propsmith MenuItemProps */
95
+ export type MenuItemProps = {
96
+ /** The icon to display before the label.
97
+ * @type Snippet | Component
98
+ */
99
+ icon?: Snippet<[{
100
+ class: string;
101
+ }]> | Component<{
102
+ class: string;
103
+ }>;
104
+ /** Right aligned text, usually a keyboard shortcut. */
105
+ secondaryContent?: string;
106
+ /** Disables the user interaction. */
107
+ disabled?: boolean;
108
+ /** A second line of text below the label. */
109
+ subText?: string;
110
+ /** Renders the item as a link pointing to this URL. */
111
+ href?: string;
112
+ };
113
+ /** @propsmith MenuItemActionableProps */
114
+ export type MenuItemActionableProps = {
115
+ /** Whether the item is checked.
116
+ * @bindable
117
+ */
118
+ checked?: boolean;
119
+ /** The value reported to the menu when the item is checked. Falls back to the id of the item.
120
+ * @default id
121
+ */
122
+ value?: string;
123
+ /** The group of items this one belongs to. Falls back to the id of the item.
124
+ * @default id
125
+ */
126
+ name?: string;
127
+ } & Omit<MenuItemProps, 'href'> & ComponentProps<typeof Button<'div'>>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ # Number Box
2
+
3
+ The NumberBox component allows users to input numeric values with specified constraints such as minimum, maximum, and step values.
4
+
5
+ ## Usage
6
+
7
+ ```svelte
8
+ <script>
9
+ import { NumberBox } from 'fluentui-svelte';
10
+ </script>
11
+
12
+ <NumberBox value={0} min={0} max={100} step={1} />
13
+ ```
14
+
15
+ ## Component API
16
+
17
+ A brief explanation of the props that really need explaining. You can see the rest of the props in the Component Props table below.
18
+
19
+ ## Examples
20
+
21
+ ### Variant
22
+
23
+ The variant prop determines the visual style of the NumberBox.
24
+
25
+ ```svelte
26
+ <NumberBox hideActionButtons min={0} max={100} step={1} />
27
+ <NumberBox hideActionButtons min={0} max={100} step={1} variant="compact" />
28
+ ```
29
+
30
+ ## Component Props
31
+
32
+ | Name | Type | Description |
33
+ | ------------- | ----------------------- | ------------------------------------------------------------- |
34
+ | `value` | `number` | The current value of the NumberBox. |
35
+ | `variant` | `'inline' \| 'compact'` | The visual style of the NumberBox, e.g., 'inline', 'compact'. |
36
+ | `ref` | `HTMLInputElement` | The DOM reference to the input element. |
37
+ | TextBox Props | | Additional props inherited from the TextBox component. |
@@ -0,0 +1,115 @@
1
+ <script lang="ts">
2
+ import { on } from 'svelte/events';
3
+ import { floating } from '../../internal/index.js';
4
+ import { offset, shift } from '@floating-ui/dom';
5
+ import { Flyout, TextBoxButton, TextBox } from '../../index.js';
6
+ import { ChevronUpFilled, ChevronDownFilled, ChevronUpDownFilled } from 'fluentui-icons-svelte';
7
+ import type { NumberBoxProps } from './types.js';
8
+
9
+ const ID = $props.id();
10
+
11
+ let {
12
+ id = 'input-' + ID,
13
+ name = id,
14
+ value = $bindable(),
15
+ variant = 'inline',
16
+ ref = $bindable(),
17
+ placeholder = '',
18
+ class: classes,
19
+ ...attributes
20
+ }: NumberBoxProps = $props();
21
+
22
+ let openFlyout = $state(false);
23
+ let cleanEvent: () => void;
24
+
25
+ let handler = (e: Event) => {
26
+ if (!(e.target as HTMLElement).closest(`.${id}`)) {
27
+ openFlyout = false;
28
+ cleanEvent();
29
+ }
30
+ };
31
+
32
+ const compactToggler = () => {
33
+ openFlyout = true;
34
+ cleanEvent = on(document, 'click', handler);
35
+ };
36
+ </script>
37
+
38
+ <!-- @component
39
+ A number box component that displays a number input with an optional increase and decrease button.
40
+ - Usage:
41
+ ```tsx
42
+ <script>
43
+ import { NumberBox } from 'fluentui-svelte';
44
+ </script>
45
+
46
+ <NumberBox />
47
+ ```
48
+ -->
49
+ <TextBox class={['fs-number-box', classes]} type="number" {id} {name} {placeholder} bind:value bind:ref {...attributes}>
50
+ {#if variant === 'inline'}
51
+ <TextBoxButton aria-label="Increase Number" onclick={() => (value = value ? ++value : 1)}>
52
+ <ChevronUpFilled />
53
+ </TextBoxButton>
54
+ <TextBoxButton aria-label="Decrease Number" onclick={() => (value = value ? --value : -1)}>
55
+ <ChevronDownFilled />
56
+ </TextBoxButton>
57
+ {:else}
58
+ <TextBoxButton aria-label="Open" onclick={() => compactToggler()}>
59
+ <ChevronUpDownFilled />
60
+ </TextBoxButton>
61
+ {#if openFlyout}
62
+ <div
63
+ class="flyout-absolute"
64
+ {@attach ref
65
+ ? floating(ref, {
66
+ placement: 'right',
67
+ middleware: [
68
+ shift({
69
+ crossAxis: true
70
+ }),
71
+ offset(({ rects }) => {
72
+ return -rects.reference.width / 1.4;
73
+ })
74
+ ]
75
+ })
76
+ : undefined}
77
+ >
78
+ <Flyout>
79
+ <div class="flex-column">
80
+ <TextBoxButton aria-label="Increase Number" onclick={() => (value = value ? ++value : 1)}>
81
+ <ChevronUpFilled />
82
+ </TextBoxButton>
83
+ <TextBoxButton aria-label="Decrease Number" onclick={() => (value = value ? --value : -1)}>
84
+ <ChevronDownFilled />
85
+ </TextBoxButton>
86
+ </div>
87
+ </Flyout>
88
+ </div>
89
+ {/if}
90
+ {/if}
91
+ </TextBox>
92
+
93
+ <style>
94
+ .flyout-absolute {
95
+ position: absolute;
96
+ right: 0;
97
+ top: 0;
98
+ z-index: 2;
99
+ & :global(.fs-flyout) {
100
+ padding: 8px;
101
+ }
102
+ & div.flex-column {
103
+ display: flex;
104
+ flex-direction: column;
105
+ gap: 12px;
106
+ & :global(button) {
107
+ margin: 0;
108
+ & :global(svg) {
109
+ width: 1.6rem;
110
+ height: 1.6rem;
111
+ }
112
+ }
113
+ }
114
+ }
115
+ </style>
@@ -0,0 +1,15 @@
1
+ import type { NumberBoxProps } from './types.ts';
2
+ /**
3
+ * A number box component that displays a number input with an optional increase and decrease button.
4
+ * - Usage:
5
+ * ```tsx
6
+ * <script>
7
+ * import { NumberBox } from 'fluentui-svelte';
8
+ * </script>
9
+ *
10
+ * <NumberBox />
11
+ * ```
12
+ */
13
+ declare const NumberBox: import("svelte").Component<NumberBoxProps, {}, "ref" | "value">;
14
+ type NumberBox = ReturnType<typeof NumberBox>;
15
+ export default NumberBox;
@@ -0,0 +1,16 @@
1
+ import type { TextBoxProps } from '../text-box/types.ts';
2
+ /** @propsmith NumberBoxProps */
3
+ export type NumberBoxProps = {
4
+ /** The current value of the input.
5
+ * @bindable
6
+ */
7
+ value?: number;
8
+ /** How the increment and decrement buttons are laid out.
9
+ *
10
+ * `inline`: the buttons sit inside the input, one next to the other.
11
+ *
12
+ * `compact`: the buttons are stacked in a flyout, opened from inside the input.
13
+ * @default 'inline'
14
+ */
15
+ variant?: 'inline' | 'compact';
16
+ } & Omit<TextBoxProps, 'type'>;
@@ -0,0 +1 @@
1
+ export {};