fluentui-svelte 0.5.0 → 0.6.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 (41) hide show
  1. package/dist/components/dialog/dialog-actions.svelte +17 -2
  2. package/dist/components/dialog/dialog-content.svelte +2 -2
  3. package/dist/components/dialog/dialog-content.svelte.d.ts +1 -1
  4. package/dist/components/dialog/dialog-surface.svelte +32 -3
  5. package/dist/components/dialog/dialog-title.svelte +23 -6
  6. package/dist/components/dialog/dialog-trigger.svelte +8 -2
  7. package/dist/components/dialog/dialog-trigger.svelte.d.ts +1 -1
  8. package/dist/components/dialog/docs/llms.md +2 -2
  9. package/dist/components/dialog/types.d.ts +6 -2
  10. package/dist/components/dialog/utils.d.ts +2 -0
  11. package/dist/components/dialog/utils.js +1 -0
  12. package/dist/components/rating/docs/llms.md +110 -0
  13. package/dist/components/rating/rating.svelte +210 -0
  14. package/dist/components/rating/rating.svelte.d.ts +19 -0
  15. package/dist/components/rating/types.d.ts +44 -0
  16. package/dist/components/rating/types.js +1 -0
  17. package/dist/components/tag/docs/llms.md +383 -0
  18. package/dist/components/tag/tag-group.d.ts +2 -0
  19. package/dist/components/tag/tag-group.js +2 -0
  20. package/dist/components/tag/tag-group.svelte +117 -0
  21. package/dist/components/tag/tag-group.svelte.d.ts +20 -0
  22. package/dist/components/tag/tag.svelte +458 -0
  23. package/dist/components/tag/tag.svelte.d.ts +19 -0
  24. package/dist/components/tag/types.d.ts +135 -0
  25. package/dist/components/tag/types.js +1 -0
  26. package/dist/components/tag-picker/docs/llms.md +292 -0
  27. package/dist/components/tag-picker/option-group.svelte +38 -0
  28. package/dist/components/tag-picker/option-group.svelte.d.ts +4 -0
  29. package/dist/components/tag-picker/option.svelte +71 -0
  30. package/dist/components/tag-picker/option.svelte.d.ts +4 -0
  31. package/dist/components/tag-picker/tag-picker.d.ts +2 -0
  32. package/dist/components/tag-picker/tag-picker.js +2 -0
  33. package/dist/components/tag-picker/tag-picker.svelte +644 -0
  34. package/dist/components/tag-picker/tag-picker.svelte.d.ts +21 -0
  35. package/dist/components/tag-picker/types.d.ts +122 -0
  36. package/dist/components/tag-picker/types.js +1 -0
  37. package/dist/index.d.ts +7 -0
  38. package/dist/index.js +12 -0
  39. package/dist/internal/constants.d.ts +0 -4
  40. package/dist/internal/constants.js +0 -4002
  41. package/package.json +2 -2
@@ -0,0 +1,383 @@
1
+ # Tag
2
+
3
+ A tag is a visual representation of an attribute, person or asset. It can be dismissed, and with `interactive` it becomes an interaction tag that has a primary action of its own. A `TagGroup` holds several tags, gives them a shared size, appearance and disabled state, and handles their selection and keyboard navigation.
4
+
5
+ ## Usage
6
+
7
+ ```svelte
8
+ <script>
9
+ import { Tag, TagGroup } from 'fluentui-svelte';
10
+ import { CalendarMonthRegular } from 'fluentui-icons-svelte';
11
+ </script>
12
+
13
+ <Tag icon={CalendarMonthRegular}>Primary text</Tag>
14
+
15
+ <TagGroup aria-label="Fruits">
16
+ <Tag interactive value="apple">Apple</Tag>
17
+ <Tag interactive value="banana">Banana</Tag>
18
+ </TagGroup>
19
+ ```
20
+
21
+ ## Tag API
22
+
23
+ - A plain tag is a static `span`. With `dismissible` it is a single `button`: click, Enter, Space, Delete and Backspace dismiss it.
24
+ - With `interactive` the tag is a `div` holding a primary `button` (icon or media, primary and secondary text) that carries `onclick`, followed, when the tag is `dismissible`, by a separate dismiss `button`.
25
+ - The tag never removes itself: `onDismiss` only reports the `value`, the consumer owns the data.
26
+ - Do not use `media` together with `icon` on one tag.
27
+ - Use `interactive` only for a tag that has a primary action, and a plain tag for one that does not.
28
+
29
+ ## Examples
30
+
31
+ ### Appearance
32
+
33
+ A tag can have a `filled`, `outline` or `brand` appearance. The default is `filled`.
34
+
35
+ ```svelte
36
+ <Tag icon={CalendarMonthRegular} dismissible>filled</Tag>
37
+ <Tag appearance="outline" icon={CalendarMonthRegular} dismissible>outline</Tag>
38
+ <Tag appearance="brand" icon={CalendarMonthRegular} dismissible>brand</Tag>
39
+ ```
40
+
41
+ ### Disabled
42
+
43
+ A tag can show that it cannot be interacted with. Every part of it is blocked, the dismiss control included.
44
+
45
+ ```svelte
46
+ <Tag disabled secondaryText="appearance=filled" icon={CalendarMonthRegular} dismissible>Disabled</Tag>
47
+ <Tag disabled appearance="outline" secondaryText="appearance=outline" icon={CalendarMonthRegular} dismissible
48
+ >Disabled</Tag
49
+ >
50
+ <Tag disabled appearance="brand" secondaryText="appearance=brand" icon={CalendarMonthRegular} dismissible>Disabled</Tag>
51
+ ```
52
+
53
+ ### Dismiss
54
+
55
+ A dismissible tag shows a dismiss icon and becomes focusable. The tag only reports the dismissal through `onDismiss`, so remove it from your data. Ensure that focus is properly managed when all tags have been dismissed.
56
+
57
+ ```svelte
58
+ <script>
59
+ let dismissed = $state(false);
60
+ </script>
61
+
62
+ {#if !dismissed}
63
+ <Tag dismissible onDismiss={() => (dismissed = true)}>Tag 1</Tag>
64
+ {/if}
65
+ <Button disabled={!dismissed} onclick={() => (dismissed = false)}>Reset the example</Button>
66
+ ```
67
+
68
+ ### Accessible name
69
+
70
+ Tell screen readers about the dismiss action. Either let the dismiss icon name the button, with `dismissLabel` (`'Dismiss'` by default), or name the tag itself with `aria-label` or `aria-labelledby`, and the dismiss icon is hidden from assistive technologies.
71
+
72
+ ```svelte
73
+ <!-- The dismiss icon names the button: "Tag 1 Remove" -->
74
+ <Tag dismissible dismissLabel="Remove">Tag 1</Tag>
75
+
76
+ <!-- Name the tag itself, and the dismiss icon is hidden from assistive technologies -->
77
+ <Tag dismissible aria-label="Remove Tag 2">Tag 2</Tag>
78
+ ```
79
+
80
+ ### Interactive (primary action)
81
+
82
+ An interactive tag has a primary action (`onclick`) and, when it is dismissible, a secondary one that dismisses it. The dismiss button is named after both buttons, "Primary text Dismiss", through `aria-labelledby`. Use `primaryProps` and `dismissProps` to reach the buttons: a custom `id` on the primary button is followed by the dismiss button, and `aria-label` together with `aria-labelledby: undefined` gives the dismiss button a name that stands on its own.
83
+
84
+ ```svelte
85
+ <script>
86
+ let clicks = $state(0);
87
+ </script>
88
+
89
+ <Tag interactive dismissible onclick={() => clicks++}>Clicked {clicks} times</Tag>
90
+ <Tag interactive>Without dismiss</Tag>
91
+ <Tag interactive dismissible dismissProps={{ 'aria-label': 'Remove Tag', 'aria-labelledby': undefined }}>
92
+ Own accessible name
93
+ </Tag>
94
+ <Button disabled={clicks === 0} onclick={() => (clicks = 0)}>Reset the example</Button>
95
+ ```
96
+
97
+ ### Icon
98
+
99
+ A tag can render a custom icon.
100
+
101
+ ```svelte
102
+ <Tag icon={CalendarMonthRegular}>Primary text</Tag>
103
+ ```
104
+
105
+ ### Media
106
+
107
+ A tag can render media, for example an Avatar. Do not use it together with `icon`.
108
+
109
+ ```svelte
110
+ {#snippet avatar()}
111
+ <Avatar name="Katri Athokas" size={28} badge={{ status: 'busy' }} />
112
+ {/snippet}
113
+
114
+ <Tag media={avatar}>Primary text</Tag>
115
+ ```
116
+
117
+ ### Secondary text
118
+
119
+ A tag can have a second line of text, given as a string or a snippet.
120
+
121
+ ```svelte
122
+ <Tag secondaryText="Secondary text">Primary text</Tag>
123
+ ```
124
+
125
+ ### Selected
126
+
127
+ `selected` paints the tag as selected. Inside a `TagGroup`, an interactive tag takes its selected state from the `selectedValues` of the group instead.
128
+
129
+ ```svelte
130
+ <Tag selected secondaryText="appearance=filled" icon={CalendarMonthRegular} dismissible>Selected</Tag>
131
+ <Tag selected appearance="outline" secondaryText="appearance=outline" icon={CalendarMonthRegular} dismissible
132
+ >Selected</Tag
133
+ >
134
+ <Tag selected appearance="brand" secondaryText="appearance=brand" icon={CalendarMonthRegular} dismissible>Selected</Tag>
135
+ ```
136
+
137
+ ### Shape
138
+
139
+ A tag can be `circular`, `rounded` or `square`. The default is `circular`.
140
+
141
+ ```svelte
142
+ {#snippet avatar()}
143
+ <Avatar name="Katri Athokas" size={28} badge={{ status: 'busy' }} />
144
+ {/snippet}
145
+
146
+ <Tag media={avatar}>Circular</Tag>
147
+ <Tag shape="rounded" media={avatar}>Rounded</Tag>
148
+ <Tag shape="square" media={avatar}>Square</Tag>
149
+
150
+ <Tag dismissible icon={CalendarMonthRegular} secondaryText="Secondary text">Circular</Tag>
151
+ <Tag shape="rounded" dismissible icon={CalendarMonthRegular} secondaryText="Secondary text">Rounded</Tag>
152
+ <Tag shape="square" dismissible icon={CalendarMonthRegular} secondaryText="Secondary text">Square</Tag>
153
+ ```
154
+
155
+ ### Size
156
+
157
+ A tag supports `medium`, `small` and `extra-small` sizes. The default is `medium`.
158
+
159
+ ```svelte
160
+ {#snippet avatar()}
161
+ <Avatar name="Katri Athokas" size={28} badge={{ status: 'busy' }} />
162
+ {/snippet}
163
+ {#snippet smallAvatar()}
164
+ <Avatar name="Katri Athokas" size={20} badge={{ status: 'busy' }} />
165
+ {/snippet}
166
+ {#snippet extraSmallAvatar()}
167
+ <Avatar name="Katri Athokas" size={16} badge={{ status: 'busy' }} />
168
+ {/snippet}
169
+
170
+ <Tag>Medium</Tag>
171
+ <Tag dismissible media={avatar}>Medium dismissible</Tag>
172
+ <Tag icon={CalendarMonthRegular}>Medium with icon</Tag>
173
+
174
+ <Tag size="small">Small</Tag>
175
+ <Tag size="small" dismissible media={smallAvatar}>Small dismissible</Tag>
176
+ <Tag size="small" icon={CalendarMonthRegular}>Small with icon</Tag>
177
+
178
+ <Tag size="extra-small">Extra small</Tag>
179
+ <Tag size="extra-small" dismissible media={extraSmallAvatar}>Extra small dismissible</Tag>
180
+ <Tag size="extra-small" icon={CalendarMonthRegular}>Extra small with icon</Tag>
181
+ ```
182
+
183
+ ## Component Props (Tag)
184
+
185
+ <!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
186
+ <!-- props:TagProps -->
187
+
188
+ | Name | Type | Default | Description |
189
+ | ---------------- | --------------------------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
190
+ | `appearance` | `'filled'` &#124; `'outline'` &#124; `'brand'` | `'filled'` | The tag can have a filled, outlined or brand appearance. Falls back to the `appearance` of the `TagGroup` it is in. |
191
+ | `size` | `'extra-small'` &#124; `'small'` &#124; `'medium'` | `'medium'` | The tag comes in three sizes. Falls back to the `size` of the `TagGroup` it is in. |
192
+ | `shape` | `'circular'` &#124; `'rounded'` &#124; `'square'` | `'circular'` | The tag can have a circular, rounded or square shape. |
193
+ | `disabled` | `boolean` | `false` | Shows that the tag cannot be interacted with. A tag inside a disabled `TagGroup` is disabled too. |
194
+ | `selected` | `boolean` | `false` | Paints the tag as selected. Inside a `TagGroup` an interactive tag takes its selected state from the `selectedValues` of the group instead. |
195
+ | `dismissible` | `boolean` | `false` | Shows a dismiss icon and lets the tag be dismissed. Falls back to the `dismissible` of the `TagGroup` it is in. A tag that is not interactive becomes a single button: activating it, or pressing Delete or Backspace on it, dismisses it. |
196
+ | `interactive` | `boolean` | `false` | Renders the tag as an interaction tag: a primary button that carries the action, followed, when the tag is dismissible, by a separate dismiss button. Use it for a tag that has a primary action, and a plain tag for one that does not. |
197
+ | `value` | `string` | | Identifies the tag inside a `TagGroup`. It is what `onDismiss` and `onTagSelect` report. Falls back to a generated id. |
198
+ | `icon` | `Snippet` &#124; `Component` | | An icon rendered before the text. Do not use it together with `media`. |
199
+ | `media` | `Snippet` &#124; `Component` | | A visual element rendered before the text, usually an `Avatar`. Do not use it together with `icon`. |
200
+ | `secondaryText` | `string` &#124; `Snippet` | | A second line of text that describes or complements the main text. |
201
+ | `dismissIcon` | `Snippet` &#124; `Component` | `DismissRegular` | The icon of the dismiss control. |
202
+ | `dismissLabel` | `string` | `'Dismiss'` | The accessible name of the dismiss control. When the tag is named through `aria-label` or `aria-labelledby` instead, the dismiss icon is hidden from assistive technologies. |
203
+ | `onDismiss` | `(e: MouseEvent` &#124; `KeyboardEvent, value: string) => void` | | Called when the tag is dismissed. The consumer owns the data: the tag does not remove itself. |
204
+ | `onclick` | `(e: MouseEvent) => void` | | Called when the tag is activated. On an interactive tag it is the click of the primary button. |
205
+ | `onkeydown` | `(e: KeyboardEvent) => void` | | Called when a key is pressed on the tag. |
206
+ | `primaryProps` | `Omit<HTMLButtonAttributes, 'children'` &#124; `'onclick'>` | | Attributes for the primary button of an interactive tag, for example `aria-haspopup`. If you give it a custom `id`, the dismiss button is named after it. |
207
+ | `dismissProps` | `Omit<HTMLButtonAttributes, 'children'` &#124; `'onclick'>` | | Attributes for the dismiss button of an interactive tag. Use `aria-label` together with `aria-labelledby: undefined` to give the button a name that stands on its own. |
208
+ | `children` | `Snippet` | | The primary text of the tag. |
209
+ | `ref` _bindable_ | `HTMLElement` | | The DOM reference of the tag element. |
210
+ | HTML Attributes | | | |
211
+
212
+ <!-- /props:TagProps -->
213
+
214
+ ## TagGroup API
215
+
216
+ A tag group is a container for several tags. It gives its `size`, `appearance`, `disabled` and `dismissible` to the tags that do not set their own (a tag is disabled when either the tag or the group is), keeps the values of the selected interactive tags in `selectedValues`, and lets the arrow keys move focus between the tags, up and down included when the group wraps into several rows. Its default role is `toolbar`. When none of the tags is actionable, use `role="list"` on the group and `role="listitem"` on each tag.
217
+
218
+ ### Usage
219
+
220
+ ```svelte
221
+ <!-- Tags that are not actionable are a list -->
222
+ <TagGroup aria-label="Simple tag group with Tag" role="list">
223
+ <Tag role="listitem">Tag 1</Tag>
224
+ <Tag role="listitem">Tag 2</Tag>
225
+ <Tag role="listitem">Tag 3</Tag>
226
+ </TagGroup>
227
+
228
+ <!-- Interactive tags are a toolbar, which is the default role -->
229
+ <TagGroup aria-label="Simple tag group with interactive tags">
230
+ <Tag interactive>Tag 1</Tag>
231
+ <Tag interactive>Tag 2</Tag>
232
+ <Tag interactive>Tag 3</Tag>
233
+ </TagGroup>
234
+ ```
235
+
236
+ ### Disabled
237
+
238
+ A disabled group disables the whole collection of tags.
239
+
240
+ ```svelte
241
+ <TagGroup disabled aria-label="Disabled tag group with Tag" role="list">
242
+ <Tag role="listitem">Tag 1</Tag>
243
+ <Tag role="listitem">Tag 2</Tag>
244
+ <Tag role="listitem">Tag 3</Tag>
245
+ </TagGroup>
246
+
247
+ <TagGroup disabled aria-label="Disabled tag group with interactive tags">
248
+ <Tag interactive>Tag 1</Tag>
249
+ <Tag interactive>Tag 2</Tag>
250
+ <Tag interactive>Tag 3</Tag>
251
+ </TagGroup>
252
+ ```
253
+
254
+ ### Dismiss
255
+
256
+ A group can make its tags dismissible and reports every dismissal through `onDismiss`, with the `value` of the tag. The group does not remove the tag: do it in your data. Focus moves to a neighboring tag when one is dismissed, so the keyboard stays in the group; ensure that focus is properly managed when all tags have been dismissed.
257
+
258
+ ```svelte
259
+ <script>
260
+ const initialTags = [
261
+ { value: '1', text: 'Tag 1' },
262
+ { value: '2', text: 'Tag 2' },
263
+ { value: '3', text: 'Tag 3' }
264
+ ];
265
+
266
+ let plainTags = $state(initialTags);
267
+ let interactiveTags = $state(initialTags);
268
+ </script>
269
+
270
+ <TagGroup
271
+ dismissible
272
+ aria-label="TagGroup example with dismissible tags"
273
+ onDismiss={(e, { value }) => (plainTags = plainTags.filter((tag) => tag.value !== value))}
274
+ >
275
+ {#each plainTags as tag (tag.value)}
276
+ <Tag value={tag.value}>{tag.text}</Tag>
277
+ {/each}
278
+ </TagGroup>
279
+ <Button disabled={plainTags.length === initialTags.length} onclick={() => (plainTags = initialTags)}>
280
+ Reset the example
281
+ </Button>
282
+
283
+ <TagGroup
284
+ dismissible
285
+ aria-label="TagGroup example with dismissible interactive tags"
286
+ onDismiss={(e, { value }) => (interactiveTags = interactiveTags.filter((tag) => tag.value !== value))}
287
+ >
288
+ {#each interactiveTags as tag (tag.value)}
289
+ <Tag interactive value={tag.value}>{tag.text}</Tag>
290
+ {/each}
291
+ </TagGroup>
292
+ <Button disabled={interactiveTags.length === initialTags.length} onclick={() => (interactiveTags = initialTags)}>
293
+ Reset the example
294
+ </Button>
295
+ ```
296
+
297
+ ### Select
298
+
299
+ Activating the primary button of an interactive tag toggles its `value` in `selectedValues`, and `onTagSelect` reports it. Remove a dismissed value from `selectedValues` yourself.
300
+
301
+ ```svelte
302
+ <script>
303
+ const initialTags = [
304
+ { value: '1', text: 'Tag 1' },
305
+ { value: '2', text: 'Tag 2' },
306
+ { value: '3', text: 'Tag 3' }
307
+ ];
308
+
309
+ let selectedValues = $state([]);
310
+ let tags = $state(initialTags);
311
+ let dismissibleSelectedValues = $state([]);
312
+
313
+ const reset = () => {
314
+ selectedValues = [];
315
+ dismissibleSelectedValues = [];
316
+ tags = initialTags;
317
+ };
318
+ </script>
319
+
320
+ <p>Selected values: {selectedValues.join(', ')}</p>
321
+ <TagGroup bind:selectedValues aria-label="Tag group with multiselect tags">
322
+ <Tag interactive value="1">Tag 1</Tag>
323
+ <Tag interactive value="2">Tag 2</Tag>
324
+ <Tag interactive value="3">Tag 3</Tag>
325
+ </TagGroup>
326
+
327
+ <p>Selected values: {dismissibleSelectedValues.join(', ')}</p>
328
+ <TagGroup
329
+ dismissible
330
+ bind:selectedValues={dismissibleSelectedValues}
331
+ aria-label="Tag group with dismissible multiselect tags"
332
+ onDismiss={(e, { value }) => {
333
+ dismissibleSelectedValues = dismissibleSelectedValues.filter((v) => v !== value);
334
+ tags = tags.filter((tag) => tag.value !== value);
335
+ }}
336
+ >
337
+ {#each tags as tag (tag.value)}
338
+ <Tag interactive value={tag.value}>{tag.text}</Tag>
339
+ {/each}
340
+ </TagGroup>
341
+
342
+ <Button
343
+ disabled={!selectedValues.length && !dismissibleSelectedValues.length && tags.length === initialTags.length}
344
+ onclick={reset}
345
+ >
346
+ Reset the example
347
+ </Button>
348
+ ```
349
+
350
+ ### Sizes
351
+
352
+ A group can set the default size of all its tags. It supports `medium`, `small` and `extra-small`. The default is `medium`.
353
+
354
+ ```svelte
355
+ {#each ['medium', 'small', 'extra-small'] as size}
356
+ <TagGroup {size} aria-label="{size} tag group example">
357
+ <Tag interactive>{size}</Tag>
358
+ <Tag interactive icon={CalendarMonthRegular}>{size}</Tag>
359
+ <Tag interactive dismissible icon={CalendarMonthRegular}>{size}</Tag>
360
+ </TagGroup>
361
+ {/each}
362
+ ```
363
+
364
+ ## Component Props (TagGroup)
365
+
366
+ <!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
367
+ <!-- props:TagGroupProps -->
368
+
369
+ | Name | Type | Default | Description |
370
+ | --------------------------- | ------------------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------- |
371
+ | `size` | `'extra-small'` &#124; `'small'` &#124; `'medium'` | `'medium'` | The size given to the tags of the group that do not set their own. |
372
+ | `appearance` | `'filled'` &#124; `'outline'` &#124; `'brand'` | | The appearance given to the tags of the group that do not set their own. |
373
+ | `disabled` | `boolean` | `false` | Disables every tag of the group. |
374
+ | `dismissible` | `boolean` | | Makes the tags of the group that do not set their own dismissible. |
375
+ | `selectedValues` _bindable_ | `string[]` | `[]` | The values of the selected interactive tags. |
376
+ | `onTagSelect` | `(e: MouseEvent, data: { value: string; selected: boolean }) => void` | | Called when an interactive tag of the group is activated, with the value it toggles. |
377
+ | `onDismiss` | `(e: MouseEvent` &#124; `KeyboardEvent, data: { value: string }) => void` | | Called when a tag of the group is dismissed. The consumer owns the data: the group does not remove the tag. |
378
+ | `role` | `HTMLAttributes<HTMLDivElement>['role']` | `'toolbar'` | The role of the group. Use `list` together with `role="listitem"` on each tag when none of them is actionable. |
379
+ | `children` | `Snippet` | | The tags of the group. |
380
+ | `ref` _bindable_ | `HTMLDivElement` | | The DOM reference of the group element. |
381
+ | HTML Attributes | | | |
382
+
383
+ <!-- /props:TagGroupProps -->
@@ -0,0 +1,2 @@
1
+ import type { TagGroupContext } from './types.ts';
2
+ export declare const getTagGroupContext: () => TagGroupContext | null, setTagGroupContext: (context: TagGroupContext) => void;
@@ -0,0 +1,2 @@
1
+ import { createFSContext } from '../../internal/index.js';
2
+ export const [getTagGroupContext, setTagGroupContext] = createFSContext();
@@ -0,0 +1,117 @@
1
+ <script lang="ts">
2
+ import { tick } from 'svelte';
3
+ import { getTabspotAttributes } from 'tabspot';
4
+ import { getGlobalFSContext } from '../../providers/fluentui-svelte/fluentui-svelte.js';
5
+ import { setTagGroupContext } from './tag-group.js';
6
+ import type { TagGroupContext, TagGroupProps } from './types.js';
7
+
8
+ let {
9
+ size,
10
+ appearance,
11
+ disabled,
12
+ dismissible,
13
+ selectedValues = $bindable([]),
14
+ onTagSelect,
15
+ onDismiss,
16
+ role = 'toolbar',
17
+ class: classes,
18
+ ref = $bindable(),
19
+ children,
20
+ ...attributes
21
+ }: TagGroupProps = $props();
22
+
23
+ const globalFSContext = getGlobalFSContext();
24
+
25
+ // Arrow keys walk the tags, as the toolbar role promises. A wrapped group is a grid of rows told apart by geometry.
26
+ const tabspotAttrs = getTabspotAttributes({
27
+ root: {},
28
+ mover: { layout: 'grid', flow: 'linear', rows: { by: 'geometry' }, cyclic: true }
29
+ });
30
+
31
+ const context: TagGroupContext = $state({
32
+ config: {
33
+ get size() {
34
+ return size;
35
+ },
36
+ get appearance() {
37
+ return appearance;
38
+ },
39
+ get disabled() {
40
+ return disabled;
41
+ },
42
+ get dismissible() {
43
+ return dismissible;
44
+ }
45
+ },
46
+ state: {
47
+ get selectedValues() {
48
+ return selectedValues;
49
+ }
50
+ },
51
+ events: null,
52
+ methods: {
53
+ select(e, value) {
54
+ const selected = !selectedValues.includes(value);
55
+ selectedValues = selected ? [...selectedValues, value] : selectedValues.filter((v) => v !== value);
56
+ onTagSelect?.(e, { value, selected });
57
+ },
58
+ async dismiss(e, value) {
59
+ // Retain the next tag (or the last one before it) in case the consumer removes this tag.
60
+ const tag = (e.target as Element).closest('.fs-tag');
61
+ const others = [...(ref?.querySelectorAll<HTMLButtonElement>('button.fs-tag, .fs-tag-primary') ?? [])].filter(
62
+ (el) => !el.disabled && el.closest('.fs-tag') !== tag
63
+ );
64
+ const nextFocus =
65
+ others.find((el) => (tag?.compareDocumentPosition(el) ?? 0) & Node.DOCUMENT_POSITION_FOLLOWING) ??
66
+ others.at(-1);
67
+ onDismiss?.(e, { value });
68
+ // Tabspot does not notice a removed tag, so it learns the new tags once the consumer has dropped it.
69
+ // The last tag leaving takes the group with it, and a rebuild without a root would walk every one.
70
+ await tick();
71
+ // Preserve focus when the consumer has already moved it, such as to a picker input.
72
+ if (tag && !tag.isConnected && nextFocus?.isConnected && document.activeElement === document.body) {
73
+ nextFocus.focus();
74
+ }
75
+ if (ref?.isConnected) globalFSContext?.state?.tabspotInstance?.rebuild(ref);
76
+ }
77
+ }
78
+ });
79
+
80
+ setTagGroupContext(context);
81
+ </script>
82
+
83
+ <!--
84
+ @component
85
+ Fluent UI TagGroup: A container for multiple tags. It gives them a size, an appearance and a disabled state,
86
+ handles the selection of interactive tags and lets arrow keys move between them.
87
+ - Usage:
88
+ ```tsx
89
+ <script>
90
+ import { Tag, TagGroup } from 'fluentui-svelte';
91
+ let selectedValues = $state([]);
92
+ </script>
93
+
94
+ <TagGroup bind:selectedValues aria-label="Fruits">
95
+ <Tag interactive value="apple">Apple</Tag>
96
+ <Tag interactive value="banana">Banana</Tag>
97
+ </TagGroup>
98
+ ```
99
+ -->
100
+ <div bind:this={ref} class={['fs-tag-group', size ?? 'medium', classes]} {role} {...tabspotAttrs} {...attributes}>
101
+ {@render children?.()}
102
+ </div>
103
+
104
+ <style>
105
+ .fs-tag-group {
106
+ display: inline-flex;
107
+ flex-wrap: wrap;
108
+ max-width: 100%;
109
+ gap: 0.5rem;
110
+ &.small {
111
+ gap: 0.375rem;
112
+ }
113
+ &.extra-small {
114
+ gap: 0.25rem;
115
+ }
116
+ }
117
+ </style>
@@ -0,0 +1,20 @@
1
+ import type { TagGroupProps } from './types.ts';
2
+ /**
3
+ * Fluent UI TagGroup: A container for multiple tags. It gives them a size, an appearance and a disabled state,
4
+ * handles the selection of interactive tags and lets arrow keys move between them.
5
+ * - Usage:
6
+ * ```tsx
7
+ * <script>
8
+ * import { Tag, TagGroup } from 'fluentui-svelte';
9
+ * let selectedValues = $state([]);
10
+ * </script>
11
+ *
12
+ * <TagGroup bind:selectedValues aria-label="Fruits">
13
+ * <Tag interactive value="apple">Apple</Tag>
14
+ * <Tag interactive value="banana">Banana</Tag>
15
+ * </TagGroup>
16
+ * ```
17
+ */
18
+ declare const TagGroup: import("svelte").Component<TagGroupProps, {}, "ref" | "selectedValues">;
19
+ type TagGroup = ReturnType<typeof TagGroup>;
20
+ export default TagGroup;