fluentui-svelte 0.2.0 → 0.3.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 (84) hide show
  1. package/dist/components/auto-suggest-box/auto-suggest-box.svelte +68 -56
  2. package/dist/components/auto-suggest-box/docs/llms.md +46 -14
  3. package/dist/components/auto-suggest-box/option.svelte +2 -1
  4. package/dist/components/avatar/docs/llms.md +20 -14
  5. package/dist/components/badge/docs/llms.md +29 -16
  6. package/dist/components/button/button.svelte +11 -5
  7. package/dist/components/button/docs/llms.md +33 -24
  8. package/dist/components/calendar-date-picker/docs/llms.md +17 -7
  9. package/dist/components/calendar-view/calendar-view-header.svelte +2 -2
  10. package/dist/components/calendar-view/calendar-view-header.svelte.d.ts +2 -17
  11. package/dist/components/calendar-view/calendar-view-months.svelte +1 -1
  12. package/dist/components/calendar-view/calendar-view-years.svelte +1 -1
  13. package/dist/components/calendar-view/calendar-view.svelte +66 -52
  14. package/dist/components/calendar-view/docs/llms.md +42 -30
  15. package/dist/components/card/docs/llms.md +54 -34
  16. package/dist/components/checkbox/docs/llms.md +18 -12
  17. package/dist/components/date-picker/docs/llms.md +25 -15
  18. package/dist/components/dialog/dialog-surface.svelte +8 -8
  19. package/dist/components/dialog/dialog.svelte +41 -39
  20. package/dist/components/dialog/docs/llms.md +59 -34
  21. package/dist/components/divider/docs/llms.md +14 -9
  22. package/dist/components/dropdown/docs/llms.md +34 -15
  23. package/dist/components/dropdown/dropdown.svelte +4 -2
  24. package/dist/components/dropdown-select/docs/llms.md +28 -14
  25. package/dist/components/expander/docs/llms.md +19 -12
  26. package/dist/components/flyout/docs/llms.md +17 -11
  27. package/dist/components/hyperlink/docs/llms.md +11 -7
  28. package/dist/components/info-bar/docs/llms.md +15 -9
  29. package/dist/components/label/docs/llms.md +15 -11
  30. package/dist/components/list-view/docs/llms.md +34 -24
  31. package/dist/components/list-view/list-view-item.svelte +10 -4
  32. package/dist/components/list-view/list-view.svelte +68 -63
  33. package/dist/components/menu/context-menu-trigger.svelte +45 -40
  34. package/dist/components/menu/docs/llms.md +63 -34
  35. package/dist/components/menu/menu-item-checkbox.svelte +3 -1
  36. package/dist/components/menu/menu-item-radio.svelte +3 -1
  37. package/dist/components/menu/menu-item-switch.svelte +3 -1
  38. package/dist/components/menu/menu-item.svelte +5 -1
  39. package/dist/components/menu/menu-popover.svelte +21 -10
  40. package/dist/components/menu/menu-trigger.svelte +22 -9
  41. package/dist/components/menu/menu.svelte +98 -87
  42. package/dist/components/number-box/docs/llms.md +10 -6
  43. package/dist/components/persona/docs/llms.md +19 -14
  44. package/dist/components/progress-bar/docs/llms.md +18 -10
  45. package/dist/components/progress-ring/docs/llms.md +19 -12
  46. package/dist/components/radio-button/docs/llms.md +13 -8
  47. package/dist/components/skeleton/docs/llms.md +13 -8
  48. package/dist/components/slider/docs/llms.md +33 -21
  49. package/dist/components/slider/slider.svelte +13 -11
  50. package/dist/components/table/docs/llms.md +531 -0
  51. package/dist/components/table/table-body.svelte +31 -0
  52. package/dist/components/table/table-body.svelte.d.ts +25 -0
  53. package/dist/components/table/table-cell-actions.svelte +48 -0
  54. package/dist/components/table/table-cell-actions.svelte.d.ts +17 -0
  55. package/dist/components/table/table-cell-layout.svelte +88 -0
  56. package/dist/components/table/table-cell-layout.svelte.d.ts +14 -0
  57. package/dist/components/table/table-cell.svelte +74 -0
  58. package/dist/components/table/table-cell.svelte.d.ts +25 -0
  59. package/dist/components/table/table-header-cell.svelte +257 -0
  60. package/dist/components/table/table-header-cell.svelte.d.ts +25 -0
  61. package/dist/components/table/table-header.svelte +28 -0
  62. package/dist/components/table/table-header.svelte.d.ts +25 -0
  63. package/dist/components/table/table-row.svelte +57 -0
  64. package/dist/components/table/table-row.svelte.d.ts +25 -0
  65. package/dist/components/table/table-selection-cell.svelte +89 -0
  66. package/dist/components/table/table-selection-cell.svelte.d.ts +25 -0
  67. package/dist/components/table/table.svelte +189 -0
  68. package/dist/components/table/table.svelte.d.ts +49 -0
  69. package/dist/components/table/table.svelte.js +138 -0
  70. package/dist/components/table/types.d.ts +287 -0
  71. package/dist/components/table/types.js +1 -0
  72. package/dist/components/text-box/docs/llms.md +23 -17
  73. package/dist/components/textarea/docs/llms.md +16 -7
  74. package/dist/components/time-picker/docs/llms.md +20 -10
  75. package/dist/components/toggle-switch/docs/llms.md +13 -9
  76. package/dist/components/tooltip/docs/llms.md +24 -10
  77. package/dist/components/tree-view/docs/llms.md +53 -35
  78. package/dist/components/tree-view/tree-view-item-content.svelte +1 -1
  79. package/dist/components/tree-view/tree-view-item.svelte +3 -3
  80. package/dist/components/tree-view/tree-view.svelte +89 -93
  81. package/dist/index.d.ts +9 -0
  82. package/dist/index.js +11 -0
  83. package/dist/providers/fluentui-svelte/fluentui-svelte.svelte +15 -15
  84. package/package.json +1 -1
@@ -0,0 +1,531 @@
1
+ # Table
2
+
3
+ A table displays sets of two dimensional data. Every part is a primitive that can be composed freely, so the table itself stays unopinionated about where the data comes from.
4
+
5
+ The components are deliberately low level: sorting, selection and filtering are not built in. The table renders what you hand it and reports the state you give it, so any data layer — plain runes, a store, a server load function — drives it without fighting an internal engine.
6
+
7
+ To create a table, you will need the following components:
8
+
9
+ - `Table`: The root component. It creates the context every other part reads: the size, whether the parts render as native table elements, whether the columns are sortable, and whether the arrow keys move between cells.
10
+ - `TableHeader`: The header section of the table. Renders a `thead`.
11
+ - `TableHeaderCell`: A column header. Renders a `th`, and turns into a sort button when the column is sortable.
12
+ - `TableBody`: The body section of the table. Renders a `tbody`.
13
+ - `TableRow`: A row of the table. Renders a `tr`, and can be filled to mark it as selected.
14
+ - `TableCell`: A single cell. Renders a `td`.
15
+
16
+ Other components that can be used within the table include:
17
+
18
+ - `TableCellLayout`: Lays a cell out as media, main content and an optional description.
19
+ - `TableSelectionCell`: A cell holding the checkbox or radio button that reports whether the row is selected.
20
+ - `TableCellActions`: Actions pinned to the end of a cell, revealed while the row is hovered or holds the focus.
21
+
22
+ ## Usage
23
+
24
+ ```svelte
25
+ <script>
26
+ import { Table, TableHeader, TableHeaderCell, TableBody, TableRow, TableCell } from 'fluentui-svelte';
27
+
28
+ const columns = ['File', 'Author', 'Last updated'];
29
+ const items = [{ file: 'Meeting notes', author: 'Max Mustermann', lastUpdated: '7h ago' }];
30
+ </script>
31
+
32
+ <Table aria-label="Files">
33
+ <TableHeader>
34
+ <TableRow>
35
+ {#each columns as column (column)}
36
+ <TableHeaderCell>{column}</TableHeaderCell>
37
+ {/each}
38
+ </TableRow>
39
+ </TableHeader>
40
+ <TableBody>
41
+ {#each items as item (item.file)}
42
+ <TableRow>
43
+ <TableCell>{item.file}</TableCell>
44
+ <TableCell>{item.author}</TableCell>
45
+ <TableCell>{item.lastUpdated}</TableCell>
46
+ </TableRow>
47
+ {/each}
48
+ </TableBody>
49
+ </Table>
50
+ ```
51
+
52
+ ## More Examples
53
+
54
+ ### Size
55
+
56
+ `size` sets the row height and the type scale of every part at once: `medium` (the default), `small` and `extra-small`.
57
+
58
+ ```svelte
59
+ <Table size="extra-small">
60
+ <TableHeader>
61
+ <TableRow>
62
+ <TableHeaderCell>File</TableHeaderCell>
63
+ </TableRow>
64
+ </TableHeader>
65
+ <TableBody>
66
+ <TableRow>
67
+ <TableCell>Meeting notes</TableCell>
68
+ </TableRow>
69
+ </TableBody>
70
+ </Table>
71
+ ```
72
+
73
+ ### Sort
74
+
75
+ `sortable` on the table turns every header cell into a button; a single cell can opt in or out with its own `sortable`. The table never sorts the data: give each header the `sortDirection` it should announce through `aria-sort`, handle `onclick`, and hand back the rows in the order you want.
76
+
77
+ ```svelte
78
+ <script>
79
+ let sortColumn = $state('file');
80
+ let sortDirection = $state('ascending');
81
+
82
+ const sorted = $derived(
83
+ [...items].sort((a, b) => (sortDirection === 'ascending' ? 1 : -1) * a[sortColumn].localeCompare(b[sortColumn]))
84
+ );
85
+
86
+ const toggleSort = (column) => {
87
+ if (sortColumn === column) {
88
+ sortDirection = sortDirection === 'ascending' ? 'descending' : 'ascending';
89
+ } else {
90
+ sortColumn = column;
91
+ sortDirection = 'ascending';
92
+ }
93
+ };
94
+ </script>
95
+
96
+ <Table sortable aria-label="Table with sort">
97
+ <TableHeader>
98
+ <TableRow>
99
+ <TableHeaderCell
100
+ sortDirection={sortColumn === 'file' ? sortDirection : undefined}
101
+ onclick={() => toggleSort('file')}
102
+ >
103
+ File
104
+ </TableHeaderCell>
105
+ </TableRow>
106
+ </TableHeader>
107
+ <TableBody>
108
+ {#each sorted as item (item.file)}
109
+ <TableRow>
110
+ <TableCell>{item.file}</TableCell>
111
+ </TableRow>
112
+ {/each}
113
+ </TableBody>
114
+ </Table>
115
+ ```
116
+
117
+ ### Multiple Selection
118
+
119
+ `TableSelectionCell` displays the selection, it does not own it. The click on the indicator also reaches the row underneath, so the row is the single place to wire `onclick`: hitting the checkbox and hitting the rest of the row run the same handler. Mark the row with `aria-selected` and set `appearance="brand"` to fill it, then feed `checked` back from your own state. A header selection cell accepts `checked="mixed"` for the partial state, and `type="radio"` swaps the checkbox for a radio button when only one row can be selected.
120
+
121
+ ```svelte
122
+ <script>
123
+ let selected = $state([items[0].file]);
124
+
125
+ const allSelected = $derived(selected.length === items.length);
126
+ const someSelected = $derived(selected.length > 0 && !allSelected);
127
+
128
+ const toggleRow = (file) => {
129
+ selected = selected.includes(file) ? selected.filter((f) => f !== file) : [...selected, file];
130
+ };
131
+ </script>
132
+
133
+ <Table aria-label="Table with multiselect">
134
+ <TableHeader>
135
+ <TableRow>
136
+ <TableSelectionCell
137
+ checked={allSelected ? true : someSelected ? 'mixed' : false}
138
+ checkboxIndicator={{ 'aria-label': 'Select all rows' }}
139
+ onclick={() => (selected = allSelected ? [] : items.map((item) => item.file))}
140
+ />
141
+ <TableHeaderCell>File</TableHeaderCell>
142
+ </TableRow>
143
+ </TableHeader>
144
+ <TableBody>
145
+ {#each items as item (item.file)}
146
+ {@const isSelected = selected.includes(item.file)}
147
+ <TableRow
148
+ aria-selected={isSelected}
149
+ appearance={isSelected ? 'brand' : 'none'}
150
+ onclick={() => toggleRow(item.file)}
151
+ >
152
+ <TableSelectionCell checked={isSelected} checkboxIndicator={{ 'aria-label': 'Select row' }} />
153
+ <TableCell>{item.file}</TableCell>
154
+ </TableRow>
155
+ {/each}
156
+ </TableBody>
157
+ </Table>
158
+ ```
159
+
160
+ ### Single Selection
161
+
162
+ Set `type="radio"` on the selection cell when only one row can be selected at a time. Give every indicator the same `name` so the browser groups them, and use `invisible` on the header cell to keep the column aligned without offering a "select all".
163
+
164
+ ```svelte
165
+ <script>
166
+ let picked = $state(items[0].file);
167
+ </script>
168
+
169
+ <Table aria-label="Table with single select">
170
+ <TableHeader>
171
+ <TableRow>
172
+ <TableSelectionCell type="radio" invisible />
173
+ <TableHeaderCell>File</TableHeaderCell>
174
+ </TableRow>
175
+ </TableHeader>
176
+ <TableBody>
177
+ {#each items as item (item.file)}
178
+ {@const isPicked = picked === item.file}
179
+ <TableRow aria-selected={isPicked} appearance={isPicked ? 'brand' : 'none'} onclick={() => (picked = item.file)}>
180
+ <TableSelectionCell
181
+ type="radio"
182
+ checked={isPicked}
183
+ radioIndicator={{ name: 'file', 'aria-label': 'Select row' }}
184
+ />
185
+ <TableCell>{item.file}</TableCell>
186
+ </TableRow>
187
+ {/each}
188
+ </TableBody>
189
+ </Table>
190
+ ```
191
+
192
+ ### Subtle Selection
193
+
194
+ `subtle` keeps the indicator out of sight until the row is hovered, holds the focus, or is checked. It calms a dense table without losing the affordance. `invisible` is the sibling knob: it hides the indicator while it still takes up the same space, which keeps a row without a selection aligned with the rest.
195
+
196
+ ```svelte
197
+ <TableRow aria-selected={isSelected} appearance={isSelected ? 'brand' : 'none'} onclick={() => toggleSubtle(item.file)}>
198
+ <TableSelectionCell subtle checked={isSelected} checkboxIndicator={{ 'aria-label': 'Select row' }} />
199
+ <TableCell>{item.file}</TableCell>
200
+ </TableRow>
201
+ ```
202
+
203
+ ### Cell Layout
204
+
205
+ `TableCellLayout` gives a cell the standard Fluent arrangement: `media` for an icon, an avatar or any other visual element, the children as the main content, and `description` for a second line below it. `appearance="primary"` enlarges the media and bolds the main content, which suits the first column of a table. `truncate` clips the content with an ellipsis instead of letting it wrap.
206
+
207
+ ```svelte
208
+ <TableCell>
209
+ <TableCellLayout media={DocumentRegular} description="7h ago" appearance="primary" truncate>
210
+ Meeting notes
211
+ </TableCellLayout>
212
+ </TableCell>
213
+ ```
214
+
215
+ ### Cell Actions
216
+
217
+ `TableCellActions` pins content to the end of a cell and reveals it while the row is hovered or holds the focus. It is usually a row of buttons, and `visible` keeps them on screen at all times. Give every action an accessible name: the row it belongs to is not announced with it.
218
+
219
+ ```svelte
220
+ <TableCell>
221
+ <TableCellLayout media={item.icon}>{item.file}</TableCellLayout>
222
+ <TableCellActions>
223
+ <Button appearance="subtle" shape="circular" aria-label="Edit {item.file}">
224
+ <EditRegular />
225
+ </Button>
226
+ <Button appearance="subtle" shape="circular" aria-label="More actions for {item.file}">
227
+ <MoreHorizontalRegular />
228
+ </Button>
229
+ </TableCellActions>
230
+ </TableCell>
231
+ ```
232
+
233
+ ### Keyboard Navigation
234
+
235
+ `enableTabspot` gives the table a single tab stop and moves the focus between cells with the arrow keys, with `Home`, `End`, `PageUp` and `PageDown` jumping further. It also switches the table over to the `role="grid"` pattern that navigation requires: the root reports `grid` and every data cell reports `gridcell`. There is no separate DataGrid component here, this prop is the whole opt-in. It needs the `FluentUISvelte` provider around the table, which is what starts the focus engine.
236
+
237
+ A cell decides how the cursor treats it with `focusMode`:
238
+
239
+ - `'cell'` (the default) makes the cell a stop of its own.
240
+ - `'group'` is for a cell with focusable content. The content is held out of the tab order, `Enter` steps into it, and `Escape` steps back out to the cell. Without it every button in every row would be its own tab stop, which is what a single tab stop exists to avoid.
241
+ - `'none'` skips the cell: the cursor passes over it rather than landing on it.
242
+
243
+ ```svelte
244
+ <Table enableTabspot aria-label="Table with keyboard navigation">
245
+ <TableHeader>
246
+ <TableRow>
247
+ <TableHeaderCell>File</TableHeaderCell>
248
+ <TableHeaderCell>Actions</TableHeaderCell>
249
+ </TableRow>
250
+ </TableHeader>
251
+ <TableBody>
252
+ {#each items as item (item.file)}
253
+ <TableRow>
254
+ <TableCell>{item.file}</TableCell>
255
+
256
+ <!-- Enter steps into the cell to reach the buttons, Escape steps back out. -->
257
+ <TableCell focusMode="group">
258
+ <TableCellActions visible>
259
+ <Button appearance="subtle" shape="circular" aria-label="Edit {item.file}">
260
+ <EditRegular />
261
+ </Button>
262
+ </TableCellActions>
263
+ </TableCell>
264
+ </TableRow>
265
+ {/each}
266
+ </TableBody>
267
+ </Table>
268
+ ```
269
+
270
+ ### Resizable Columns
271
+
272
+ `resizableColumns` gives every header cell a handle that drags its column wider or narrower, and pins the table to a fixed layout so the width sticks. A single column opts in or out with its own `resizable`.
273
+
274
+ - `columnSizing` sets a floor per column, by column position. Without one a column stops at 40px.
275
+ - `columnWidths` is the width of each column in pixels, by position. Bind it to read the widths back or to restore them later; leave it alone and the table keeps them itself.
276
+ - `onColumnResize` reports every change as `{ index, width }`.
277
+
278
+ The handle is a [WAI window splitter](https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/): it takes the focus and answers the left and right arrows, so the columns are reachable without a pointer. Name it with `resizeLabel` on the header cell — attributes you put on the cell reach the cell, never the handle inside it, so that prop is the only way in. It falls back to `"Resize column"`, which is worth replacing both to translate it and to say which column it resizes. Under `enableTabspot` a resizable header cell defaults to `focusMode="group"`, so `Enter` steps in to reach the handle and `Escape` steps back out.
279
+
280
+ ```svelte
281
+ <script>
282
+ // Bind only if you want to read the widths back, or restore them later.
283
+ let columnWidths = $state([200, 180, 160]);
284
+ </script>
285
+
286
+ <Table
287
+ resizableColumns
288
+ bind:columnWidths
289
+ columnSizing={[{ minWidth: 120 }, { minWidth: 100 }, { minWidth: 100 }]}
290
+ onColumnResize={(e, { index, width }) => console.log(index, width)}
291
+ aria-label="Table with resizable columns"
292
+ >
293
+ <TableHeader>
294
+ <TableRow>
295
+ <TableHeaderCell>File</TableHeaderCell>
296
+ <TableHeaderCell>Author</TableHeaderCell>
297
+ </TableRow>
298
+ </TableHeader>
299
+ <TableBody>
300
+ {#each items as item (item.file)}
301
+ <TableRow>
302
+ <TableCell>{item.file}</TableCell>
303
+ <TableCell>{item.author}</TableCell>
304
+ </TableRow>
305
+ {/each}
306
+ </TableBody>
307
+ </Table>
308
+ ```
309
+
310
+ A fixed table sizes to the sum of its columns, so wrap it in a container with `overflow-x: auto` when the columns can grow past the space you have. Pair the cells with `<TableCellLayout truncate>` so a narrowed column clips its text instead of pushing it out.
311
+
312
+ ### No Row Borders
313
+
314
+ `noRowBorders` drops the line that separates one row from the next, for a table that sits inside a card or another surface that already frames it.
315
+
316
+ ```svelte
317
+ <Table noRowBorders aria-label="Table without row borders">
318
+ <TableHeader>
319
+ <TableRow>
320
+ <TableHeaderCell>File</TableHeaderCell>
321
+ </TableRow>
322
+ </TableHeader>
323
+ <TableBody>
324
+ <TableRow>
325
+ <TableCell>Meeting notes</TableCell>
326
+ </TableRow>
327
+ </TableBody>
328
+ </Table>
329
+ ```
330
+
331
+ ### Non Native Elements
332
+
333
+ `noNativeElements` renders every part as a `div` and lays the rows out with flexbox instead of `display: table`. The ARIA roles that a native table provides implicitly are written out, so the semantics stay the same. Use it when the strict table layout gets in the way — virtualized rows and sticky headers are the usual reasons. A single part can still be overridden through its own `as` prop.
334
+
335
+ ```svelte
336
+ <Table noNativeElements aria-label="Table rendered with divs">
337
+ <TableHeader>
338
+ <TableRow>
339
+ <TableHeaderCell>File</TableHeaderCell>
340
+ </TableRow>
341
+ </TableHeader>
342
+ <TableBody>
343
+ <TableRow>
344
+ <TableCell>Meeting notes</TableCell>
345
+ </TableRow>
346
+ </TableBody>
347
+ </Table>
348
+ ```
349
+
350
+ ### Virtualization
351
+
352
+ The table renders whatever you put inside it, so any virtualization library works. A semantic `table` is laid out by the browser in a single pass and fights a windowed list, so pair it with `noNativeElements`. Only a window of rows exists in the DOM, so report the real size of the data set with `aria-rowcount` on the table and `aria-rowindex` on each row — the header row is index 1, so the data starts at 2.
353
+
354
+ ```svelte
355
+ <script>
356
+ import SvelteVirtualList from '@humanspeak/svelte-virtual-list';
357
+
358
+ const rows = Array.from({ length: 2000 }, (_, i) => ({ id: i, file: `Document ${i + 1}` }));
359
+ </script>
360
+
361
+ <Table noNativeElements style="min-width: 520px;" aria-label="Virtualized table" aria-rowcount={rows.length + 1}>
362
+ <TableHeader>
363
+ <TableRow aria-rowindex={1}>
364
+ <TableHeaderCell>File</TableHeaderCell>
365
+ </TableRow>
366
+ </TableHeader>
367
+ <TableBody style="height: 320px;">
368
+ <SvelteVirtualList items={rows} defaultEstimatedItemHeight={44}>
369
+ {#snippet renderItem(row, index)}
370
+ <TableRow aria-rowindex={index + 2}>
371
+ <TableCell>{row.file}</TableCell>
372
+ </TableRow>
373
+ {/snippet}
374
+ </SvelteVirtualList>
375
+ </TableBody>
376
+ </Table>
377
+ ```
378
+
379
+ `enableTabspot` does not follow the rows out of the rendered window on its own: the arrow keys stop at the edge of the slice the list has rendered. Wiring the two together needs a virtualization adapter, the way `TreeView` does with its `virtualizer` prop.
380
+
381
+ ## Accessibility
382
+
383
+ - Always include a `TableHeader` row.
384
+ - When the table is preceded by a heading, point `aria-labelledby` at the id of that heading. When it has no visible label, give it an `aria-label`.
385
+ - Set a `min-width` so the table still reads at high zoom levels and on small screens.
386
+ - Do not override the `role` of the table parts. Keyboard navigation is the one case where the roles change, and `enableTabspot` handles it for you: it applies the [`role="grid"` pattern](https://www.w3.org/WAI/ARIA/apg/patterns/grid/) the navigation requires.
387
+
388
+ ## Component Props (Table)
389
+
390
+ <!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
391
+ <!-- props:TableProps -->
392
+
393
+ | Name | Type | Default | Description |
394
+ | ------------------------- | ------------------------------------------------------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
395
+ | `as` | `'table'` &#124; `'div'` | `'table'` | The HTML element to render the table as. Defaults to a `div` when `noNativeElements` is set. |
396
+ | `ref` _bindable_ | `HTMLTableElement` &#124; `HTMLDivElement` | | The DOM reference of the table element. |
397
+ | `size` | `'extra-small'` &#124; `'small'` &#124; `'medium'` | `'medium'` | Affects the sizes of all table subcomponents. |
398
+ | `noNativeElements` | `boolean` | `false` | Renders every table part as a `div` instead of a semantic table element. The layout switches from `display: table` to `display: flex`, which virtualization and sticky headers need. |
399
+ | `sortable` | `boolean` | `false` | Turns every header cell into a sort button. Individual cells can still opt in on their own. |
400
+ | `noRowBorders` | `boolean` | `false` | Drops the line that separates one row from the next. |
401
+ | `enableTabspot` | `boolean` | `false` | Moves the focus between cells with the arrow keys, through a single tab stop. It also switches the table over to the `role="grid"` pattern, which that navigation requires. |
402
+ | `virtualizer` | `TableVirtualizer` | | Lets the arrow keys reach rows a windowed list has not rendered. Only read when `enableTabspot` is set, and the rows need an `aria-rowindex` for it to find them again. |
403
+ | `resizableColumns` | `boolean` | `false` | Gives every header cell a handle that drags its column wider or narrower. It also pins the table to a fixed layout, so a column keeps the width it was given. |
404
+ | `columnWidths` _bindable_ | `number[]` | | The width of each column in pixels, by column position. Columns left out keep their own size. |
405
+ | `columnSizing` | `ColumnSizing[]` | | Per column limits, by column position. |
406
+ | `onColumnResize` | `(e: Event, data: { index: number; width: number }) => void` | | Called whenever a column is dragged or nudged to a new width. |
407
+ | HTML Attributes | | | |
408
+
409
+ <!-- /props:TableProps -->
410
+
411
+ ## Component Props (TableHeader)
412
+
413
+ <!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
414
+ <!-- props:TableHeaderProps -->
415
+
416
+ | Name | Type | Default | Description |
417
+ | ---------------- | ------------------------------------------------- | --------- | ------------------------------------------------------------------------------ |
418
+ | `as` | `'thead'` &#124; `'div'` | `'thead'` | The HTML element to render the header as. Inherited from the table by default. |
419
+ | `ref` _bindable_ | `HTMLTableSectionElement` &#124; `HTMLDivElement` | | The DOM reference of the header element. |
420
+ | HTML Attributes | | | |
421
+
422
+ <!-- /props:TableHeaderProps -->
423
+
424
+ ## Component Props (TableHeaderCell)
425
+
426
+ <!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
427
+ <!-- props:TableHeaderCellProps -->
428
+
429
+ | Name | Type | Default | Description |
430
+ | ---------------- | ---------------------------------------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
431
+ | `as` | `'th'` &#124; `'div'` | `'th'` | The HTML element to render the header cell as. Inherited from the table by default. |
432
+ | `ref` _bindable_ | `HTMLTableCellElement` &#124; `HTMLDivElement` | | The DOM reference of the header cell element. |
433
+ | `sortable` | `boolean` | | Whether the column is sortable. Inherited from the table by default. |
434
+ | `sortDirection` | `'ascending'` &#124; `'descending'` | | The direction the column is currently sorted in. Also sets `aria-sort`. |
435
+ | `sortIcon` | `Snippet` &#124; `Component` | | The indicator rendered next to the label of a sortable column. |
436
+ | `aside` | `Snippet` &#124; `Component` | | Content placed after the label, outside the sort button. |
437
+ | `focusMode` | `'cell'` &#124; `'group'` &#124; `'none'` | `'cell'` | How the arrow keys treat the cell. `'group'` holds the focusable content of the cell out of the tab order until `Enter` steps in, and `Escape` steps back out. `'none'` skips the cell altogether. Only read when the table sets `enableTabspot`. A resizable header cell defaults to `'group'`, so the arrows reach its handle instead of leaving the column behind. |
438
+ | `resizable` | `boolean` | | Whether the column can be resized. Inherited from the table by default. |
439
+ | `resizeLabel` | `string` | `'Resize column'` | The accessible name of the resize handle. Give it a translated, and ideally per column, string — the attributes of the cell reach the cell, never the handle inside it. |
440
+ | HTML Attributes | | | |
441
+
442
+ <!-- /props:TableHeaderCellProps -->
443
+
444
+ ## Component Props (TableBody)
445
+
446
+ <!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
447
+ <!-- props:TableBodyProps -->
448
+
449
+ | Name | Type | Default | Description |
450
+ | ---------------- | ------------------------------------------------- | --------- | ---------------------------------------------------------------------------- |
451
+ | `as` | `'tbody'` &#124; `'div'` | `'tbody'` | The HTML element to render the body as. Inherited from the table by default. |
452
+ | `ref` _bindable_ | `HTMLTableSectionElement` &#124; `HTMLDivElement` | | The DOM reference of the body element. |
453
+ | HTML Attributes | | | |
454
+
455
+ <!-- /props:TableBodyProps -->
456
+
457
+ ## Component Props (TableRow)
458
+
459
+ <!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
460
+ <!-- props:TableRowProps -->
461
+
462
+ | Name | Type | Default | Description |
463
+ | ---------------- | --------------------------------------------- | -------- | --------------------------------------------------------------------------- |
464
+ | `as` | `'tr'` &#124; `'div'` | `'tr'` | The HTML element to render the row as. Inherited from the table by default. |
465
+ | `ref` _bindable_ | `HTMLTableRowElement` &#124; `HTMLDivElement` | | The DOM reference of the row element. |
466
+ | `appearance` | `'none'` &#124; `'brand'` &#124; `'neutral'` | `'none'` | How the row is filled. Intended to mark a selected row. |
467
+ | HTML Attributes | | | |
468
+
469
+ <!-- /props:TableRowProps -->
470
+
471
+ ## Component Props (TableCell)
472
+
473
+ <!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
474
+ <!-- props:TableCellProps -->
475
+
476
+ | Name | Type | Default | Description |
477
+ | ---------------- | ---------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
478
+ | `as` | `'td'` &#124; `'div'` | `'td'` | The HTML element to render the cell as. Inherited from the table by default. |
479
+ | `ref` _bindable_ | `HTMLTableCellElement` &#124; `HTMLDivElement` | | The DOM reference of the cell element. |
480
+ | `focusMode` | `'cell'` &#124; `'group'` &#124; `'none'` | `'cell'` | How the arrow keys treat the cell. `'group'` holds the focusable content of the cell out of the tab order until `Enter` steps in, and `Escape` steps back out. `'none'` skips the cell altogether. Only read when the table sets `enableTabspot`. |
481
+ | HTML Attributes | | | |
482
+
483
+ <!-- /props:TableCellProps -->
484
+
485
+ ## Component Props (TableSelectionCell)
486
+
487
+ <!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
488
+ <!-- props:TableSelectionCellProps -->
489
+
490
+ | Name | Type | Default | Description |
491
+ | ------------------- | ---------------------------------------------- | ------------ | ------------------------------------------------------------------------------ |
492
+ | `as` | `'td'` &#124; `'div'` | `'td'` | The HTML element to render the cell as. Inherited from the table by default. |
493
+ | `ref` _bindable_ | `HTMLTableCellElement` &#124; `HTMLDivElement` | | The DOM reference of the cell element. |
494
+ | `type` | `'checkbox'` &#124; `'radio'` | `'checkbox'` | A table can have two kinds of selection modes. |
495
+ | `checked` | `boolean` &#124; `'mixed'` | `false` | Whether the row is selected. `'mixed'` renders the indeterminate state. |
496
+ | `subtle` | `boolean` | `false` | Only shows the indicator when it is checked, or the row is hovered or focused. |
497
+ | `invisible` | `boolean` | `false` | Hides the indicator while it keeps taking up the same space. |
498
+ | `checkboxIndicator` | `CheckboxProps` | | The props to spread on the checkbox rendered when `type` is `'checkbox'`. |
499
+ | `radioIndicator` | `RadioButtonProps` | | The props to spread on the radio button rendered when `type` is `'radio'`. |
500
+ | HTML Attributes | | | |
501
+
502
+ <!-- /props:TableSelectionCellProps -->
503
+
504
+ ## Component Props (TableCellLayout)
505
+
506
+ <!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
507
+ <!-- props:TableCellLayoutProps -->
508
+
509
+ | Name | Type | Default | Description |
510
+ | ---------------- | ---------------------------- | ------- | ------------------------------------------------------------------------------ |
511
+ | `ref` _bindable_ | `HTMLDivElement` | | The DOM reference of the layout element. |
512
+ | `media` | `Snippet` &#124; `Component` | | An icon, an avatar or any other visual element placed before the main content. |
513
+ | `description` | `string` | | A second line of text that describes or complements the main content. |
514
+ | `appearance` | `'primary'` | | Emphasizes the cell by enlarging the media and bolding the main content. |
515
+ | `truncate` | `boolean` | | Clips the content with an ellipsis instead of letting it wrap. |
516
+ | HTML Attributes | | | |
517
+
518
+ <!-- /props:TableCellLayoutProps -->
519
+
520
+ ## Component Props (TableCellActions)
521
+
522
+ <!-- DO NOT EDIT THIS SECTION (propsmith generated) -->
523
+ <!-- props:TableCellActionsProps -->
524
+
525
+ | Name | Type | Default | Description |
526
+ | ---------------- | ---------------- | ------- | ------------------------------------------------------------------------------------ |
527
+ | `ref` _bindable_ | `HTMLDivElement` | | The DOM reference of the actions element. |
528
+ | `visible` | `boolean` | | Keeps the actions on screen instead of waiting for the row to be hovered or focused. |
529
+ | HTML Attributes | | | |
530
+
531
+ <!-- /props:TableCellActionsProps -->
@@ -0,0 +1,31 @@
1
+ <script lang="ts" generics="Tag extends 'tbody' | 'div' = 'tbody'">
2
+ import { getTag, requireTableContext } from './table.svelte.js';
3
+ import type { TableBodyProps, TableDOM } from './types.js';
4
+
5
+ let { as, ref = $bindable(), class: classes, children, ...attributes }: TableBodyProps<Tag> = $props();
6
+
7
+ const context = requireTableContext();
8
+
9
+ const noNativeElements = $derived(context.config.noNativeElements);
10
+
11
+ const _as = $derived(getTag(as, 'tbody', noNativeElements));
12
+ </script>
13
+
14
+ <svelte:element
15
+ this={_as}
16
+ bind:this={ref as TableDOM[Tag]}
17
+ role={_as === 'div' ? 'rowgroup' : undefined}
18
+ class={['fs-table-body', { flex: _as === 'div' }, classes]}
19
+ {...attributes}
20
+ >
21
+ {@render children?.()}
22
+ </svelte:element>
23
+
24
+ <style>
25
+ .fs-table-body {
26
+ &.flex {
27
+ display: flex;
28
+ flex-direction: column;
29
+ }
30
+ }
31
+ </style>
@@ -0,0 +1,25 @@
1
+ import type { TableBodyProps } from './types.ts';
2
+ declare function $$render<Tag extends 'tbody' | 'div' = 'tbody'>(): {
3
+ props: TableBodyProps<Tag>;
4
+ exports: {};
5
+ bindings: "ref";
6
+ slots: {};
7
+ events: {};
8
+ };
9
+ declare class __sveltets_Render<Tag extends 'tbody' | 'div' = 'tbody'> {
10
+ props(): ReturnType<typeof $$render<Tag>>['props'];
11
+ events(): ReturnType<typeof $$render<Tag>>['events'];
12
+ slots(): ReturnType<typeof $$render<Tag>>['slots'];
13
+ bindings(): "ref";
14
+ exports(): {};
15
+ }
16
+ interface $$IsomorphicComponent {
17
+ new <Tag extends 'tbody' | 'div' = 'tbody'>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<Tag>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<Tag>['props']>, ReturnType<__sveltets_Render<Tag>['events']>, ReturnType<__sveltets_Render<Tag>['slots']>> & {
18
+ $$bindings?: ReturnType<__sveltets_Render<Tag>['bindings']>;
19
+ } & ReturnType<__sveltets_Render<Tag>['exports']>;
20
+ <Tag extends 'tbody' | 'div' = 'tbody'>(internal: unknown, props: ReturnType<__sveltets_Render<Tag>['props']> & {}): ReturnType<__sveltets_Render<Tag>['exports']>;
21
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
22
+ }
23
+ declare const TableBody: $$IsomorphicComponent;
24
+ type TableBody<Tag extends 'tbody' | 'div' = 'tbody'> = InstanceType<typeof TableBody<Tag>>;
25
+ export default TableBody;
@@ -0,0 +1,48 @@
1
+ <script lang="ts">
2
+ import { requireTableContext } from './table.svelte.js';
3
+ import type { TableCellActionsProps } from './types.js';
4
+
5
+ let { ref = $bindable(), visible = false, class: classes, children, ...attributes }: TableCellActionsProps = $props();
6
+
7
+ requireTableContext();
8
+ </script>
9
+
10
+ <!--
11
+ @component
12
+ Actions pinned to the end of a cell, revealed while the row is hovered or holds the focus.
13
+
14
+ - Usage:
15
+ ```tsx
16
+ <TableCell>
17
+ <TableCellLayout>Meeting notes</TableCellLayout>
18
+ <TableCellActions>
19
+ <Button appearance="subtle" icon={EditRegular} aria-label="Edit" />
20
+ </TableCellActions>
21
+ </TableCell>
22
+ ```
23
+ -->
24
+ <div bind:this={ref} class={['fs-table-cell-actions', { visible }, classes]} {...attributes}>
25
+ {@render children?.()}
26
+ </div>
27
+
28
+ <style>
29
+ .fs-table-cell-actions {
30
+ position: absolute;
31
+ top: 0;
32
+ right: 0;
33
+ display: flex;
34
+ align-items: center;
35
+ gap: 0.25rem;
36
+ height: 100%;
37
+ padding-right: var(--fs-table-cell-padding);
38
+ /* Set by the row, so the actions surface on hover and while the focus is inside it. */
39
+ opacity: var(--fs-table-row-active, 0);
40
+ transition: opacity var(--fs-fast-duration) var(--fs-point-to-point);
41
+ @media (prefers-reduced-motion: reduce) {
42
+ transition: none;
43
+ }
44
+ &.visible {
45
+ opacity: 1;
46
+ }
47
+ }
48
+ </style>