fluentui-svelte 0.3.0 → 0.5.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.
- package/README.md +1 -1
- package/dist/components/auto-suggest-box/docs/llms.md +76 -15
- package/dist/components/breadcrumb/breadcrumb-button.svelte +130 -0
- package/dist/components/breadcrumb/breadcrumb-button.svelte.d.ts +35 -0
- package/dist/components/breadcrumb/breadcrumb-divider.svelte +39 -0
- package/dist/components/breadcrumb/breadcrumb-divider.svelte.d.ts +14 -0
- package/dist/components/breadcrumb/breadcrumb-item.svelte +30 -0
- package/dist/components/breadcrumb/breadcrumb-item.svelte.d.ts +16 -0
- package/dist/components/breadcrumb/breadcrumb.svelte +101 -0
- package/dist/components/breadcrumb/breadcrumb.svelte.d.ts +25 -0
- package/dist/components/breadcrumb/docs/llms.md +193 -0
- package/dist/components/breadcrumb/types.d.ts +80 -0
- package/dist/components/breadcrumb/types.js +1 -0
- package/dist/components/card/card-header.svelte +2 -2
- package/dist/components/dropdown/dropdown.svelte +1 -0
- package/dist/components/list-view/docs/llms.md +79 -27
- package/dist/components/list-view/list-view-item.svelte +14 -11
- package/dist/components/list-view/list-view.svelte +3 -2
- package/dist/components/list-view/types.d.ts +5 -1
- package/dist/components/menu/docs/llms.md +16 -0
- package/dist/components/menu/menu-popover.svelte +7 -12
- package/dist/components/menu/menu-popover.svelte.d.ts +2 -10
- package/dist/components/menu/types.d.ts +21 -0
- package/dist/components/tab-view/docs/llms.md +146 -0
- package/dist/components/tab-view/tab-view-tab.svelte +290 -0
- package/dist/components/tab-view/tab-view-tab.svelte.d.ts +20 -0
- package/dist/components/tab-view/tab-view.svelte +188 -0
- package/dist/components/tab-view/tab-view.svelte.d.ts +26 -0
- package/dist/components/tab-view/tab-view.svelte.js +15 -0
- package/dist/components/tab-view/types.d.ts +79 -0
- package/dist/components/tab-view/types.js +1 -0
- package/dist/components/top-nav/docs/llms.md +176 -0
- package/dist/components/top-nav/top-nav-item.svelte +234 -0
- package/dist/components/top-nav/top-nav-item.svelte.d.ts +41 -0
- package/dist/components/top-nav/top-nav.svelte +259 -0
- package/dist/components/top-nav/top-nav.svelte.d.ts +26 -0
- package/dist/components/top-nav/top-nav.svelte.js +18 -0
- package/dist/components/top-nav/types.d.ts +89 -0
- package/dist/components/top-nav/types.js +1 -0
- package/dist/components/tree-view/docs/llms.md +166 -1
- package/dist/components/tree-view/tree-view-item-content.svelte +41 -8
- package/dist/components/tree-view/tree-view-item.svelte +10 -1
- package/dist/components/tree-view/tree-view.svelte +17 -2
- package/dist/components/tree-view/tree-view.svelte.js +4 -2
- package/dist/components/tree-view/types.d.ts +4 -0
- package/dist/css/class-darkmode.css +1 -1
- package/dist/css/media-darkmode.css +1 -1
- package/dist/css/theme.css +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.js +14 -0
- package/package.json +2 -1
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Breadcrumb
|
|
2
|
+
|
|
3
|
+
A breadcrumb shows where a page sits in the hierarchy above it, and gives the reader a way back up. It renders a `nav` landmark around an ordered list, so a screen reader can jump straight to it.
|
|
4
|
+
|
|
5
|
+
The landmark names itself `Breadcrumb`, the trail is an `ol` so its order is part of the markup rather than of the styling, the last step carries `aria-current="page"`, and the dividers are held out of the accessibility tree — a trail of three steps reads as three, not as five. Every one of those can be overridden: pass your own `aria-label`, or an `aria-labelledby`, and the default steps aside.
|
|
6
|
+
|
|
7
|
+
To create a breadcrumb, you will need to use the following components:
|
|
8
|
+
|
|
9
|
+
- `Breadcrumb`: The landmark and the list. It creates the context the rest read.
|
|
10
|
+
- `BreadcrumbItem`: One step of the trail — the list item the step lives in. It takes anything, not only a step.
|
|
11
|
+
- `BreadcrumbButton`: A step that can be activated: an anchor when it goes somewhere, a button when it does something.
|
|
12
|
+
- `BreadcrumbDivider`: The mark between two steps. It is decoration, and is hidden from assistive technology.
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
A breadcrumb is a list of steps with a divider between them. The last step is the page the reader is already on, so it is marked `current` rather than linked away.
|
|
17
|
+
|
|
18
|
+
```svelte
|
|
19
|
+
<script>
|
|
20
|
+
import { Breadcrumb, BreadcrumbItem, BreadcrumbButton, BreadcrumbDivider } from 'fluentui-svelte';
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<Breadcrumb>
|
|
24
|
+
<BreadcrumbItem>
|
|
25
|
+
<BreadcrumbButton as="a" href="/">Home</BreadcrumbButton>
|
|
26
|
+
</BreadcrumbItem>
|
|
27
|
+
<BreadcrumbDivider />
|
|
28
|
+
<BreadcrumbItem>
|
|
29
|
+
<BreadcrumbButton as="a" href="/files">Files</BreadcrumbButton>
|
|
30
|
+
</BreadcrumbItem>
|
|
31
|
+
<BreadcrumbDivider />
|
|
32
|
+
<BreadcrumbItem>
|
|
33
|
+
<BreadcrumbButton current>Quarterly report</BreadcrumbButton>
|
|
34
|
+
</BreadcrumbItem>
|
|
35
|
+
</Breadcrumb>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Examples
|
|
39
|
+
|
|
40
|
+
### Size
|
|
41
|
+
|
|
42
|
+
`size` sets the type, the height and the divider of every step at once.
|
|
43
|
+
|
|
44
|
+
```svelte
|
|
45
|
+
<Breadcrumb size="small">...</Breadcrumb>
|
|
46
|
+
<Breadcrumb size="medium">...</Breadcrumb>
|
|
47
|
+
<Breadcrumb size="large">...</Breadcrumb>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Icon
|
|
51
|
+
|
|
52
|
+
`icon` takes a snippet or a component and renders it beside the label, on the side `iconPosition` asks for.
|
|
53
|
+
|
|
54
|
+
```svelte
|
|
55
|
+
<BreadcrumbItem>
|
|
56
|
+
<BreadcrumbButton as="a" href="/files" icon={FolderRegular}>Files</BreadcrumbButton>
|
|
57
|
+
</BreadcrumbItem>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Current Step
|
|
61
|
+
|
|
62
|
+
`current` marks the step as the page the trail ends at. It announces `aria-current="page"` and reads as text rather than as a way out, so the last step does not offer to take the reader where they already are.
|
|
63
|
+
|
|
64
|
+
```svelte
|
|
65
|
+
<BreadcrumbItem>
|
|
66
|
+
<BreadcrumbButton current>Quarterly report</BreadcrumbButton>
|
|
67
|
+
</BreadcrumbItem>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Overflow Menu
|
|
71
|
+
|
|
72
|
+
A step takes whatever you put in it, so a long trail folds its middle into a menu by making that menu a step of its own. Which steps fold is your call — slice the list wherever the layout needs it.
|
|
73
|
+
|
|
74
|
+
```svelte
|
|
75
|
+
<script>
|
|
76
|
+
// Keep the root and the last two steps; everything between them goes into the menu.
|
|
77
|
+
const head = trail.slice(0, 1);
|
|
78
|
+
const folded = trail.slice(1, -2);
|
|
79
|
+
const tail = trail.slice(-2);
|
|
80
|
+
</script>
|
|
81
|
+
|
|
82
|
+
<Breadcrumb>
|
|
83
|
+
{#each head as item (item.name)}
|
|
84
|
+
<BreadcrumbItem>
|
|
85
|
+
<BreadcrumbButton as="a" href={item.href}>{item.name}</BreadcrumbButton>
|
|
86
|
+
</BreadcrumbItem>
|
|
87
|
+
<BreadcrumbDivider />
|
|
88
|
+
{/each}
|
|
89
|
+
|
|
90
|
+
<BreadcrumbItem>
|
|
91
|
+
<Menu>
|
|
92
|
+
<MenuTrigger>
|
|
93
|
+
{#snippet children({ state, menuTriggerProps })}
|
|
94
|
+
<Button bind:ref={state.ref} {...menuTriggerProps} appearance="subtle" aria-label="More steps">
|
|
95
|
+
<MoreHorizontalRegular />
|
|
96
|
+
</Button>
|
|
97
|
+
{/snippet}
|
|
98
|
+
</MenuTrigger>
|
|
99
|
+
<MenuPopover>
|
|
100
|
+
<MenuList>
|
|
101
|
+
<!-- A step that goes somewhere is a link, in the menu as much as in the trail. -->
|
|
102
|
+
{#each folded as item (item.name)}
|
|
103
|
+
<MenuItem href={item.href}>{item.name}</MenuItem>
|
|
104
|
+
{/each}
|
|
105
|
+
</MenuList>
|
|
106
|
+
</MenuPopover>
|
|
107
|
+
</Menu>
|
|
108
|
+
</BreadcrumbItem>
|
|
109
|
+
<BreadcrumbDivider />
|
|
110
|
+
|
|
111
|
+
{#each tail as item (item.name)}
|
|
112
|
+
<BreadcrumbItem>
|
|
113
|
+
<BreadcrumbButton current>{item.name}</BreadcrumbButton>
|
|
114
|
+
</BreadcrumbItem>
|
|
115
|
+
{/each}
|
|
116
|
+
</Breadcrumb>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The label on the menu button is yours to write, and it is worth writing: a trigger with only an icon says nothing to a screen reader. Note also that what lives inside a `MenuPopover` is not rendered until the menu opens, so the folded steps are not in the HTML a crawler reads.
|
|
120
|
+
|
|
121
|
+
### Focus Mode
|
|
122
|
+
|
|
123
|
+
`focusMode` decides how the focus walks the trail. `tab`, the default, leaves every step its own tab stop, which is what a reader expects of a row of links. `arrow` gives the whole trail a single tab stop and moves between steps with the arrow keys, with `Home` and `End` for the ends.
|
|
124
|
+
|
|
125
|
+
```svelte
|
|
126
|
+
<Breadcrumb focusMode="arrow">...</Breadcrumb>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Component Props (Breadcrumb)
|
|
130
|
+
|
|
131
|
+
<!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
|
|
132
|
+
<!-- props:BreadcrumbProps -->
|
|
133
|
+
|
|
134
|
+
| Name | Type | Default | Description |
|
|
135
|
+
| -------------------- | -------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
136
|
+
| `ref` _bindable_ | `HTMLElement` | | The DOM reference of the `nav`. |
|
|
137
|
+
| `listRef` _bindable_ | `HTMLOListElement` | | The DOM reference of the list. |
|
|
138
|
+
| `listProps` | `HTMLOlAttributes` | | The props to spread on the list element. |
|
|
139
|
+
| `size` | `'small'` | `'medium'` | `'large'` | `'medium'` | The size of every step and divider of the trail. |
|
|
140
|
+
| `focusMode` | `'tab'` | `'arrow'` | `'tab'` | How the focus walks the trail: `tab` leaves every step its own tab stop, `arrow` gives the trail a single one and moves between steps with the arrow keys. |
|
|
141
|
+
| Element Attributes | | | |
|
|
142
|
+
|
|
143
|
+
<!-- /props:BreadcrumbProps -->
|
|
144
|
+
|
|
145
|
+
## Component Props (BreadcrumbItem)
|
|
146
|
+
|
|
147
|
+
<!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
|
|
148
|
+
<!-- props:BreadcrumbItemProps -->
|
|
149
|
+
|
|
150
|
+
| Name | Type | Default | Description |
|
|
151
|
+
| ------------------------- | --------------- | ------- | ------------------------------ |
|
|
152
|
+
| `ref` _bindable_ | `HTMLLIElement` | | The DOM reference of the item. |
|
|
153
|
+
| Element Attributes (`li`) | | | |
|
|
154
|
+
|
|
155
|
+
<!-- /props:BreadcrumbItemProps -->
|
|
156
|
+
|
|
157
|
+
## Component Props (BreadcrumbButton)
|
|
158
|
+
|
|
159
|
+
<!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
|
|
160
|
+
<!-- props:BreadcrumbButtonProps -->
|
|
161
|
+
|
|
162
|
+
| Name | Type | Default | Description |
|
|
163
|
+
| ------------------- | --------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------ |
|
|
164
|
+
| `ref` _bindable_ | `BreadcrumbItemElementDOMType[T]` | | The DOM reference of the step. |
|
|
165
|
+
| `as` | `'button'` | `'a'` | `'button'` | The DOM element to render. A step that goes somewhere is an anchor and takes `href`. |
|
|
166
|
+
| `icon` | `Snippet` | `Component` | | The icon to display beside the label. |
|
|
167
|
+
| `iconPosition` | `'before'` | `'after'` | `'before'` | Which side of the label the icon sits on. |
|
|
168
|
+
| `current` | `boolean` | `false` | Marks the step as the page the trail ends at, which is what `aria-current` announces. |
|
|
169
|
+
| `disabled` | `boolean` | | Disables the user interaction. |
|
|
170
|
+
| `disabledFocusable` | `boolean` | `false` | Keeps a disabled step focusable, so the tab order stays the same for a keyboard or screen reader user. |
|
|
171
|
+
| HTML Attributes | | | |
|
|
172
|
+
|
|
173
|
+
<!-- /props:BreadcrumbButtonProps -->
|
|
174
|
+
|
|
175
|
+
## Component Props (BreadcrumbDivider)
|
|
176
|
+
|
|
177
|
+
<!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
|
|
178
|
+
<!-- props:BreadcrumbDividerProps -->
|
|
179
|
+
|
|
180
|
+
| Name | Type | Default | Description |
|
|
181
|
+
| ------------------------- | --------------- | ------- | --------------------------------- |
|
|
182
|
+
| `ref` _bindable_ | `HTMLLIElement` | | The DOM reference of the divider. |
|
|
183
|
+
| Element Attributes (`li`) | | | |
|
|
184
|
+
|
|
185
|
+
<!-- /props:BreadcrumbDividerProps -->
|
|
186
|
+
|
|
187
|
+
## Accessibility
|
|
188
|
+
|
|
189
|
+
The landmark is named `Breadcrumb` by default, which is what a screen reader announces when jumping to it. Give it your own `aria-label` when a page carries more than one trail, so each is distinguishable, or an `aria-labelledby` pointing at a heading you already render — the default steps aside for both.
|
|
190
|
+
|
|
191
|
+
The dividers are decoration and are hidden, so the list reads as the number of steps it has. The last step carries `aria-current="page"`. A step with an icon and no label needs an `aria-label` of its own.
|
|
192
|
+
|
|
193
|
+
Structured data is left to you. A breadcrumb is one of the few components a search engine reads directly, through `BreadcrumbList` from schema.org, but the text of a step lives in your markup and the URLs live in your routing — so the trail you publish is yours to build, in whichever shape your site already uses.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { BeforeOrAfter, PolymorphicProps, Sizes } from '../../types/index.js';
|
|
2
|
+
import type { Snippet, Component } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes, HTMLOlAttributes } from 'svelte/elements';
|
|
4
|
+
/** How the focus walks the trail. */
|
|
5
|
+
export type BreadcrumbFocusMode = 'tab' | 'arrow';
|
|
6
|
+
export type BreadcrumbItemGenerics = 'button' | 'a';
|
|
7
|
+
export type BreadcrumbItemElementDOMType = {
|
|
8
|
+
button: HTMLButtonElement;
|
|
9
|
+
a: HTMLAnchorElement;
|
|
10
|
+
};
|
|
11
|
+
/** @propsmith BreadcrumbProps */
|
|
12
|
+
export type BreadcrumbProps = {
|
|
13
|
+
/** The DOM reference of the `nav`.
|
|
14
|
+
* @bindable
|
|
15
|
+
*/
|
|
16
|
+
ref?: HTMLElement;
|
|
17
|
+
/** The DOM reference of the list.
|
|
18
|
+
* @bindable
|
|
19
|
+
*/
|
|
20
|
+
listRef?: HTMLOListElement;
|
|
21
|
+
/** The props to spread on the list element.
|
|
22
|
+
* @type HTMLOlAttributes
|
|
23
|
+
*/
|
|
24
|
+
listProps?: HTMLOlAttributes;
|
|
25
|
+
/** The size of every step and divider of the trail.
|
|
26
|
+
* @default 'medium'
|
|
27
|
+
*/
|
|
28
|
+
size?: Sizes;
|
|
29
|
+
/** How the focus walks the trail: `tab` leaves every step its own tab stop, `arrow` gives the
|
|
30
|
+
* trail a single one and moves between steps with the arrow keys.
|
|
31
|
+
* @default 'tab'
|
|
32
|
+
*/
|
|
33
|
+
focusMode?: BreadcrumbFocusMode;
|
|
34
|
+
} & HTMLAttributes<HTMLElement>;
|
|
35
|
+
/** @propsmith BreadcrumbItemProps */
|
|
36
|
+
export type BreadcrumbItemProps = {
|
|
37
|
+
/** The DOM reference of the item.
|
|
38
|
+
* @bindable
|
|
39
|
+
*/
|
|
40
|
+
ref?: HTMLLIElement;
|
|
41
|
+
} & HTMLAttributes<HTMLLIElement>;
|
|
42
|
+
/** @propsmith BreadcrumbButtonProps */
|
|
43
|
+
export type BreadcrumbButtonProps<T extends BreadcrumbItemGenerics = 'button'> = {
|
|
44
|
+
/** The DOM reference of the step.
|
|
45
|
+
* @type BreadcrumbItemElementDOMType[T]
|
|
46
|
+
* @bindable
|
|
47
|
+
*/
|
|
48
|
+
ref?: BreadcrumbItemElementDOMType[T];
|
|
49
|
+
/** The DOM element to render. A step that goes somewhere is an anchor and takes `href`.
|
|
50
|
+
* @type 'button' | 'a'
|
|
51
|
+
* @default 'button'
|
|
52
|
+
*/
|
|
53
|
+
as?: T;
|
|
54
|
+
/** The icon to display beside the label.
|
|
55
|
+
* @type Snippet | Component
|
|
56
|
+
*/
|
|
57
|
+
icon?: Snippet | Component;
|
|
58
|
+
/** Which side of the label the icon sits on.
|
|
59
|
+
* @default 'before'
|
|
60
|
+
*/
|
|
61
|
+
iconPosition?: BeforeOrAfter;
|
|
62
|
+
/** Marks the step as the page the trail ends at, which is what `aria-current` announces.
|
|
63
|
+
* @default false
|
|
64
|
+
*/
|
|
65
|
+
current?: boolean;
|
|
66
|
+
/** Disables the user interaction. */
|
|
67
|
+
disabled?: boolean;
|
|
68
|
+
/** Keeps a disabled step focusable, so the tab order stays the same for a keyboard or screen
|
|
69
|
+
* reader user.
|
|
70
|
+
* @default false
|
|
71
|
+
*/
|
|
72
|
+
disabledFocusable?: boolean;
|
|
73
|
+
} & PolymorphicProps<T>;
|
|
74
|
+
/** @propsmith BreadcrumbDividerProps */
|
|
75
|
+
export type BreadcrumbDividerProps = {
|
|
76
|
+
/** The DOM reference of the divider.
|
|
77
|
+
* @bindable
|
|
78
|
+
*/
|
|
79
|
+
ref?: HTMLLIElement;
|
|
80
|
+
} & HTMLAttributes<HTMLLIElement>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
{#if typeof title === 'string'}
|
|
43
43
|
<h4 class="body" id={`${id}-title`}>{title}</h4>
|
|
44
44
|
{:else if title}
|
|
45
|
-
<RenderSoC SoC={title}
|
|
45
|
+
<RenderSoC SoC={title} args={{ id: `${id}-title` }} />
|
|
46
46
|
{/if}
|
|
47
47
|
{#if typeof description === 'string'}
|
|
48
48
|
<p class="caption">{description}</p>
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
{:else if typeof title === 'string'}
|
|
54
54
|
<h4 class="body" id={`${id}-title`}>{title}</h4>
|
|
55
55
|
{:else if title}
|
|
56
|
-
<RenderSoC SoC={title}
|
|
56
|
+
<RenderSoC SoC={title} args={{ id: `${id}-title` }} />
|
|
57
57
|
{/if}
|
|
58
58
|
{#if _action}
|
|
59
59
|
<RenderSoC SoC={_action} />
|
|
@@ -56,27 +56,78 @@ Use `selectionMode` (`single`, `multiselect`, or `extended`) to control how item
|
|
|
56
56
|
</ListView>
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
+
### Toggle on click
|
|
60
|
+
|
|
61
|
+
In a multiselect list, a plain click selects only that item and Ctrl/Cmd + click toggles it. Set `toggleOnClick` to make a plain click toggle the item directly.
|
|
62
|
+
|
|
63
|
+
```svelte
|
|
64
|
+
<ListView selectionMode="multiselect" toggleOnClick>
|
|
65
|
+
{#each ['Apple', 'Banana', 'Cherry'] as item (item)}
|
|
66
|
+
<ListViewItem value={item}>{item}</ListViewItem>
|
|
67
|
+
{/each}
|
|
68
|
+
</ListView>
|
|
69
|
+
```
|
|
70
|
+
|
|
59
71
|
### Checkmark
|
|
60
72
|
|
|
61
|
-
Add and configure a checkbox for the element. You can use the same props for the Checkbox component.
|
|
73
|
+
Add and configure a checkbox for the element. You can use the same props for the Checkbox component. The checkbox toggles the selection of its item without triggering `onAction`. Bind `selectedItems` to read or control the selection.
|
|
62
74
|
|
|
63
75
|
```svelte
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
<script>
|
|
77
|
+
let selected = $state(['Apple']);
|
|
78
|
+
</script>
|
|
79
|
+
|
|
80
|
+
<ListView selectionMode="multiselect" bind:selectedItems={selected}>
|
|
81
|
+
{#each ['Apple', 'Banana', 'Cherry'] as item (item)}
|
|
82
|
+
<ListViewItem value={item} checkmark={{}}>{item}</ListViewItem>
|
|
83
|
+
{/each}
|
|
84
|
+
</ListView>
|
|
85
|
+
|
|
86
|
+
<p>Selected: {selected.join(', ')}</p>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Cards with a primary action
|
|
90
|
+
|
|
91
|
+
With `navigationMode` set to `composite` and a `selectionMode`, every item is a row. Clicking it runs its primary action (`onAction`), the checkbox toggles its selection, and any button inside is a secondary action. Use `checkmark.wrapperAttributes.style` to place the checkbox where you need it, like the top-right corner of the card. Note: every actionable element inside this ListViewItem, except the checkbox, must call e.stopPropagation().
|
|
92
|
+
|
|
93
|
+
```svelte
|
|
94
|
+
<script>
|
|
95
|
+
const cards = [
|
|
96
|
+
{ name: 'Fluent', description: 'Design system' },
|
|
97
|
+
{ name: 'Svelte', description: 'UI framework' },
|
|
98
|
+
{ name: 'Vite', description: 'Build tool' }
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
let selected = $state([]);
|
|
102
|
+
let lastAction = $state('');
|
|
103
|
+
</script>
|
|
104
|
+
|
|
105
|
+
<ListView as="div" navigationMode="composite" selectionMode="multiselect" bind:selectedItems={selected}>
|
|
106
|
+
{#each cards as card (card.name)}
|
|
107
|
+
{#snippet install()}
|
|
108
|
+
<Button
|
|
109
|
+
onclick={(e) => {
|
|
110
|
+
e.stopPropagation();
|
|
111
|
+
lastAction = 'Installed ' + card.name;
|
|
112
|
+
}}>Install</Button
|
|
113
|
+
>
|
|
114
|
+
{/snippet}
|
|
115
|
+
<ListViewItem
|
|
116
|
+
value={card.name}
|
|
117
|
+
checkmark={{ wrapperAttributes: { style: 'position: absolute; top: 0.5rem; right: 0.5rem; z-index: 2;' } }}
|
|
118
|
+
style="padding: 0"
|
|
119
|
+
onAction={() => (lastAction = 'Opened ' + card.name)}
|
|
77
120
|
>
|
|
78
|
-
|
|
79
|
-
|
|
121
|
+
<Card orientation="vertical" style="width: 100%;">
|
|
122
|
+
<CardHeader title={card.name} description={card.description} />
|
|
123
|
+
<CardFooter action={install} />
|
|
124
|
+
</Card>
|
|
125
|
+
</ListViewItem>
|
|
126
|
+
{/each}
|
|
127
|
+
</ListView>
|
|
128
|
+
|
|
129
|
+
<p>Selected: {selected.join(', ')}</p>
|
|
130
|
+
<p>Last action: {lastAction}</p>
|
|
80
131
|
```
|
|
81
132
|
|
|
82
133
|
## ListView Props
|
|
@@ -84,17 +135,18 @@ Add and configure a checkbox for the element. You can use the same props for the
|
|
|
84
135
|
<!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
|
|
85
136
|
<!-- props:ListViewProps -->
|
|
86
137
|
|
|
87
|
-
| Name | Type | Default | Description
|
|
88
|
-
| -------------------------- | --------------------------------------------------------------------- | ----------- |
|
|
89
|
-
| `selectionMode` | `'none'` | `'single'` | `'multiselect'` | `'extended'` | `'none'` | How many items can be selected, and how the selection is made.
|
|
90
|
-
| `navigationMode` | `'items'` | `'composite'` | `'items'` | Whether the list itself takes a single tab stop, or every item takes its own.
|
|
91
|
-
| `selectedItems` _bindable_ | `string[]` | | The values of the selected items.
|
|
92
|
-
| `shape` | `'circular'` | `'rounded'` | `'square'` | `'rounded'` | The items can have a circular, rounded or square shape.
|
|
93
|
-
| `onSelectionChange` | `(e: Event, selectedItems: string[]` | `[]) => void` | | Called whenever the selection changes.
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
-
|
|
|
138
|
+
| Name | Type | Default | Description |
|
|
139
|
+
| -------------------------- | --------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
140
|
+
| `selectionMode` | `'none'` | `'single'` | `'multiselect'` | `'extended'` | `'none'` | How many items can be selected, and how the selection is made. |
|
|
141
|
+
| `navigationMode` | `'items'` | `'composite'` | `'items'` | Whether the list itself takes a single tab stop, or every item takes its own. |
|
|
142
|
+
| `selectedItems` _bindable_ | `string[]` | | The values of the selected items. |
|
|
143
|
+
| `shape` | `'circular'` | `'rounded'` | `'square'` | `'rounded'` | The items can have a circular, rounded or square shape. |
|
|
144
|
+
| `onSelectionChange` | `(e: Event, selectedItems: string[]` | `[]) => void` | | Called whenever the selection changes. |
|
|
145
|
+
| `toggleOnClick` | `boolean` | `false` | When `selectionMode` is `'multiselect'`, clicking an item toggles its selection directly instead of requiring Ctrl/Cmd + click. |
|
|
146
|
+
| `as` | `'ul'` | `'ol'` | `'div'` | `'ul'` | The HTML element to render the list as. |
|
|
147
|
+
| `ref` _bindable_ | `HTMLUListElement` | `HTMLOListElement` | `HTMLDivElement` | | The DOM reference of the list element. |
|
|
148
|
+
| `disableTabspot` | `boolean` | `false` | Opts the list out of the tabspot focus management, to wire your own. |
|
|
149
|
+
| HTML Attributes | | | |
|
|
98
150
|
|
|
99
151
|
<!-- /props:ListViewProps -->
|
|
100
152
|
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
const role = $derived(_role || getChildrenRole(as));
|
|
44
44
|
|
|
45
45
|
let active = $derived(_state.selectedItems.includes(value));
|
|
46
|
+
let checkmarkRef = $state<HTMLDivElement>();
|
|
46
47
|
|
|
47
48
|
// A row is a level of its own: right enters the cells, left comes back out. Anything
|
|
48
49
|
// that is not a row navigates as a plain item of the list above it.
|
|
@@ -82,21 +83,23 @@
|
|
|
82
83
|
{...tabspotAttrs}
|
|
83
84
|
onfocus={(e: FocusEvent) => invokeHandlers(e, disabled, [(event: FocusEvent) => onfocus?.(event, value || '')])}
|
|
84
85
|
onclick={(e: MouseEvent) => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
role !== 'row'
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
(event: MouseEvent) => onAction?.(event, value || '')
|
|
92
|
-
]
|
|
93
|
-
: [(event: MouseEvent) => onAction?.(event, value || '')]
|
|
94
|
-
);
|
|
86
|
+
// The checkmark only toggles the selection, even on a row. The primary action is for the rest of the item.
|
|
87
|
+
const fromCheckmark = !!checkmarkRef?.contains(e.target as Node);
|
|
88
|
+
invokeHandlers(e, disabled, [
|
|
89
|
+
(role !== 'row' || fromCheckmark) && ((event: MouseEvent) => handleSelection?.(event, value, fromCheckmark)),
|
|
90
|
+
!fromCheckmark && ((event: MouseEvent) => onAction?.(event, value || ''))
|
|
91
|
+
]);
|
|
95
92
|
}}
|
|
96
93
|
{...attributes}
|
|
97
94
|
>
|
|
98
95
|
{#if checkmark}
|
|
99
|
-
<Checkbox
|
|
96
|
+
<Checkbox
|
|
97
|
+
{disabled}
|
|
98
|
+
bind:checked={active}
|
|
99
|
+
bind:wrapperRef={checkmarkRef}
|
|
100
|
+
tabindex={-1}
|
|
101
|
+
{...!isObjectEmpty(checkmark) ? checkmark : {}}
|
|
102
|
+
/>
|
|
100
103
|
{/if}
|
|
101
104
|
{@render children?.()}
|
|
102
105
|
</svelte:element>
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
selectedItems = $bindable([]),
|
|
13
13
|
onSelectionChange,
|
|
14
14
|
disableTabspot = false,
|
|
15
|
+
toggleOnClick = false,
|
|
15
16
|
children,
|
|
16
17
|
...attributes
|
|
17
18
|
}: ListViewProps<Tag> = $props();
|
|
@@ -72,7 +73,7 @@
|
|
|
72
73
|
},
|
|
73
74
|
events: null,
|
|
74
75
|
methods: {
|
|
75
|
-
handleSelection: (e: MouseEvent, value: string) => {
|
|
76
|
+
handleSelection: (e: MouseEvent, value: string, toggle = false) => {
|
|
76
77
|
if (selectionMode === 'none') return;
|
|
77
78
|
|
|
78
79
|
const itemIndex = items.findIndex((item) => item.value === value);
|
|
@@ -89,7 +90,7 @@
|
|
|
89
90
|
.filter((i) => !i.disabled)
|
|
90
91
|
.map((i) => i.value);
|
|
91
92
|
selectedItems = Array.from(new Set([...selectedItems, ...rangeValues]));
|
|
92
|
-
} else if (MULTISELECTABLE && (e.ctrlKey || e.metaKey)) {
|
|
93
|
+
} else if (MULTISELECTABLE && (toggle || e.ctrlKey || e.metaKey || toggleOnClick)) {
|
|
93
94
|
selectedItems = selectedItems.includes(value)
|
|
94
95
|
? selectedItems.filter((v) => v !== value)
|
|
95
96
|
: [...selectedItems, value];
|
|
@@ -23,6 +23,10 @@ export type ListViewProps<T extends 'ul' | 'ol' | 'div'> = {
|
|
|
23
23
|
shape?: Shapes;
|
|
24
24
|
/** Called whenever the selection changes. */
|
|
25
25
|
onSelectionChange?: (e: Event, selectedItems: string[] | []) => void;
|
|
26
|
+
/** When `selectionMode` is `'multiselect'`, clicking an item toggles its selection directly instead of requiring Ctrl/Cmd + click.
|
|
27
|
+
* @default false
|
|
28
|
+
*/
|
|
29
|
+
toggleOnClick?: boolean;
|
|
26
30
|
/** The HTML element to render the list as.
|
|
27
31
|
* @type 'ul' | 'ol' | 'div'
|
|
28
32
|
* @default 'ul'
|
|
@@ -87,7 +91,7 @@ export type ListViewContextState = {
|
|
|
87
91
|
anchorIndex: number | null;
|
|
88
92
|
};
|
|
89
93
|
export type ListViewContextMethods = {
|
|
90
|
-
handleSelection: (e: MouseEvent, value: string) => void;
|
|
94
|
+
handleSelection: (e: MouseEvent, value: string, toggle?: boolean) => void;
|
|
91
95
|
getChildrenRole: (tag: 'li' | 'a' | 'div') => 'listitem' | 'option' | 'row' | undefined;
|
|
92
96
|
registerItem: (id: string, value: string, disabled: boolean) => void;
|
|
93
97
|
unregisterItem: (id: string) => void;
|
|
@@ -140,6 +140,22 @@ This variant of the `MenuTrigger` component allows you to create context menus t
|
|
|
140
140
|
|
|
141
141
|
<!-- /props:MenuProps -->
|
|
142
142
|
|
|
143
|
+
## Component Props (MenuPopover)
|
|
144
|
+
|
|
145
|
+
<!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
|
|
146
|
+
<!-- props:MenuPopoverProps -->
|
|
147
|
+
|
|
148
|
+
| Name | Type | Default | Description |
|
|
149
|
+
| ----------------- | ------------------------------------ | ------- | -------------------------------------------------------- |
|
|
150
|
+
| `ref` | `HTMLElement` | | The element used to position the popover. |
|
|
151
|
+
| `open` _bindable_ | `boolean` | | Controls the open state of the popover. |
|
|
152
|
+
| `keepMounted` | `boolean` | `false` | Keeps the popover mounted in the DOM while it is closed. |
|
|
153
|
+
| `placement` | `ComputePositionConfig['placement']` | | Where the popover is positioned relative to its trigger. |
|
|
154
|
+
| `positionConfig` | `ComputePositionConfig` | | The position configuration handed over to floating-ui. |
|
|
155
|
+
| `children` | `Snippet` | | The content of the popover. |
|
|
156
|
+
|
|
157
|
+
<!-- /props:MenuPopoverProps -->
|
|
158
|
+
|
|
143
159
|
## Component Props (MenuTrigger)
|
|
144
160
|
|
|
145
161
|
<!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
import { useDebounce } from 'runed';
|
|
7
7
|
import { floating, flyToOffset, getCSSDuration, reactiveBoundingRect } from '../../internal/index.js';
|
|
8
8
|
import { getReducedMotion } from '../../providers/fluentui-svelte/fluentui-svelte.js';
|
|
9
|
-
import type {
|
|
10
|
-
import type { ComputePositionConfig } from '@floating-ui/dom';
|
|
9
|
+
import type { MenuPopoverProps } from './types.js';
|
|
11
10
|
|
|
12
11
|
let {
|
|
13
12
|
ref: _ref,
|
|
14
13
|
open: _open = $bindable(),
|
|
14
|
+
keepMounted = false,
|
|
15
15
|
placement,
|
|
16
16
|
positionConfig: floatingPosition = {
|
|
17
17
|
placement,
|
|
@@ -19,13 +19,7 @@
|
|
|
19
19
|
strategy: 'fixed'
|
|
20
20
|
},
|
|
21
21
|
children
|
|
22
|
-
}:
|
|
23
|
-
ref?: HTMLElement;
|
|
24
|
-
open?: boolean;
|
|
25
|
-
placement?: ComputePositionConfig['placement'];
|
|
26
|
-
positionConfig?: Partial<ComputePositionConfig>;
|
|
27
|
-
children: Snippet;
|
|
28
|
-
} = $props();
|
|
22
|
+
}: MenuPopoverProps = $props();
|
|
29
23
|
|
|
30
24
|
const context = getMenuContext();
|
|
31
25
|
|
|
@@ -90,16 +84,17 @@
|
|
|
90
84
|
});
|
|
91
85
|
</script>
|
|
92
86
|
|
|
93
|
-
{#if opened && ref}
|
|
87
|
+
{#if keepMounted || (opened && ref)}
|
|
94
88
|
{@const isRefAnElement = ref instanceof HTMLElement}
|
|
95
89
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
96
90
|
<div
|
|
97
91
|
bind:this={element}
|
|
98
92
|
class="fs-menu-popover"
|
|
93
|
+
hidden={!opened}
|
|
99
94
|
onmouseleave={openOnHover ? (e: MouseEvent) => debounced(e) : undefined}
|
|
100
|
-
{@attach ref ? floating(ref, config) : undefined}
|
|
95
|
+
{@attach opened && ref ? floating(ref, config) : undefined}
|
|
101
96
|
style={`--position=${isSubMenu ? 'absolute' : 'fixed'} --min-width=${isRefAnElement && !isSubMenu ? `${boundingElement.rect.width}px` : 'max-content'}`}
|
|
102
|
-
in:flyToOffset={isRefAnElement
|
|
97
|
+
in:flyToOffset={opened && isRefAnElement
|
|
103
98
|
? {
|
|
104
99
|
...getIntroTransition({ config, anchor: boundingElement.rect, element, ref, isSubMenu }),
|
|
105
100
|
duration: duration,
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
type $$ComponentProps = {
|
|
4
|
-
ref?: HTMLElement;
|
|
5
|
-
open?: boolean;
|
|
6
|
-
placement?: ComputePositionConfig['placement'];
|
|
7
|
-
positionConfig?: Partial<ComputePositionConfig>;
|
|
8
|
-
children: Snippet;
|
|
9
|
-
};
|
|
10
|
-
declare const MenuPopover: import("svelte").Component<$$ComponentProps, {}, "open">;
|
|
1
|
+
import type { MenuPopoverProps } from './types.ts';
|
|
2
|
+
declare const MenuPopover: import("svelte").Component<MenuPopoverProps, {}, "open">;
|
|
11
3
|
type MenuPopover = ReturnType<typeof MenuPopover>;
|
|
12
4
|
export default MenuPopover;
|
|
@@ -59,6 +59,27 @@ export type MenuProps = {
|
|
|
59
59
|
/** The parts of the menu: a trigger and a popover. */
|
|
60
60
|
children?: Snippet;
|
|
61
61
|
};
|
|
62
|
+
/** @propsmith MenuPopoverProps */
|
|
63
|
+
export type MenuPopoverProps = {
|
|
64
|
+
/** The element used to position the popover. */
|
|
65
|
+
ref?: HTMLElement;
|
|
66
|
+
/** Controls the open state of the popover.
|
|
67
|
+
* @bindable
|
|
68
|
+
*/
|
|
69
|
+
open?: boolean;
|
|
70
|
+
/** Keeps the popover mounted in the DOM while it is closed.
|
|
71
|
+
* @default false
|
|
72
|
+
*/
|
|
73
|
+
keepMounted?: boolean;
|
|
74
|
+
/** Where the popover is positioned relative to its trigger. */
|
|
75
|
+
placement?: ComputePositionConfig['placement'];
|
|
76
|
+
/** The position configuration handed over to floating-ui.
|
|
77
|
+
* @type ComputePositionConfig
|
|
78
|
+
*/
|
|
79
|
+
positionConfig?: Partial<ComputePositionConfig>;
|
|
80
|
+
/** The content of the popover. */
|
|
81
|
+
children: Snippet;
|
|
82
|
+
};
|
|
62
83
|
/** @propsmith MenuTriggerProps */
|
|
63
84
|
export type MenuTriggerProps = {
|
|
64
85
|
/** Disables the user interaction. */
|