@human-kit/svelte-components 1.0.0-alpha.3 → 1.0.0-alpha.5
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/dist/FOCUS_STATE_CONTRACT.md +12 -0
- package/dist/calendar/body-cell/README.md +15 -0
- package/dist/calendar/grid/README.md +13 -0
- package/dist/calendar/grid-body/README.md +13 -0
- package/dist/calendar/grid-header/README.md +13 -0
- package/dist/calendar/header-cell/README.md +14 -0
- package/dist/calendar/heading/README.md +13 -0
- package/dist/calendar/root/README.md +24 -0
- package/dist/calendar/trigger-next/README.md +14 -0
- package/dist/calendar/trigger-previous/README.md +14 -0
- package/dist/checkbox/README.md +53 -0
- package/dist/checkbox/TODO.md +16 -0
- package/dist/checkbox/index.d.ts +6 -0
- package/dist/checkbox/index.js +6 -0
- package/dist/checkbox/index.parts.d.ts +2 -0
- package/dist/checkbox/index.parts.js +2 -0
- package/dist/checkbox/indicator/README.md +23 -0
- package/dist/checkbox/indicator/checkbox-indicator.svelte +43 -0
- package/dist/checkbox/indicator/checkbox-indicator.svelte.d.ts +10 -0
- package/dist/checkbox/root/README.md +47 -0
- package/dist/checkbox/root/checkbox-label-test.svelte +10 -0
- package/dist/checkbox/root/checkbox-label-test.svelte.d.ts +18 -0
- package/dist/checkbox/root/checkbox-root.svelte +361 -0
- package/dist/checkbox/root/checkbox-root.svelte.d.ts +23 -0
- package/dist/checkbox/root/checkbox-test.svelte +59 -0
- package/dist/checkbox/root/checkbox-test.svelte.d.ts +18 -0
- package/dist/checkbox/root/context.d.ts +21 -0
- package/dist/checkbox/root/context.js +15 -0
- package/dist/clock/README.md +75 -0
- package/dist/clock/axis/README.md +24 -0
- package/dist/clock/axis/clock-axis.svelte +37 -0
- package/dist/clock/axis/clock-axis.svelte.d.ts +8 -0
- package/dist/clock/hooks/use-wheel-scroll.svelte.d.ts +16 -0
- package/dist/clock/hooks/use-wheel-scroll.svelte.js +336 -0
- package/dist/clock/index.d.ts +10 -0
- package/dist/clock/index.js +10 -0
- package/dist/clock/index.parts.d.ts +4 -0
- package/dist/clock/index.parts.js +4 -0
- package/dist/clock/root/README.md +38 -0
- package/dist/clock/root/clock-root-test.svelte +62 -0
- package/dist/clock/root/clock-root-test.svelte.d.ts +14 -0
- package/dist/clock/root/clock-root.svelte +329 -0
- package/dist/clock/root/clock-root.svelte.d.ts +25 -0
- package/dist/clock/root/context.d.ts +22 -0
- package/dist/clock/root/context.js +15 -0
- package/dist/clock/root/resolve-visible-columns.d.ts +7 -0
- package/dist/clock/root/resolve-visible-columns.js +16 -0
- package/dist/clock/root/time-utils.d.ts +48 -0
- package/dist/clock/root/time-utils.js +314 -0
- package/dist/clock/root/wheel-options.d.ts +17 -0
- package/dist/clock/root/wheel-options.js +63 -0
- package/dist/clock/wheel-column/README.md +25 -0
- package/dist/clock/wheel-column/clock-wheel-column-bindable-test.svelte +16 -0
- package/dist/clock/wheel-column/clock-wheel-column-bindable-test.svelte.d.ts +3 -0
- package/dist/clock/wheel-column/clock-wheel-column-custom-snippet-test.svelte +29 -0
- package/dist/clock/wheel-column/clock-wheel-column-custom-snippet-test.svelte.d.ts +6 -0
- package/dist/clock/wheel-column/clock-wheel-column-default-height-test.svelte +11 -0
- package/dist/clock/wheel-column/clock-wheel-column-default-height-test.svelte.d.ts +3 -0
- package/dist/clock/wheel-column/clock-wheel-column-test.svelte +38 -0
- package/dist/clock/wheel-column/clock-wheel-column-test.svelte.d.ts +12 -0
- package/dist/clock/wheel-column/clock-wheel-column-tp-test.svelte +38 -0
- package/dist/clock/wheel-column/clock-wheel-column-tp-test.svelte.d.ts +12 -0
- package/dist/clock/wheel-column/clock-wheel-column-untagged-snippet-test.svelte +29 -0
- package/dist/clock/wheel-column/clock-wheel-column-untagged-snippet-test.svelte.d.ts +6 -0
- package/dist/clock/wheel-column/clock-wheel-column.svelte +499 -0
- package/dist/clock/wheel-column/clock-wheel-column.svelte.d.ts +17 -0
- package/dist/clock/wheel-item/README.md +17 -0
- package/dist/clock/wheel-item/clock-wheel-item.svelte +49 -0
- package/dist/clock/wheel-item/clock-wheel-item.svelte.d.ts +17 -0
- package/dist/combobox/list/combobox-listbox.svelte.d.ts +1 -1
- package/dist/datepicker/TODO.md +2 -2
- package/dist/datepicker/calendar/README.md +19 -0
- package/dist/datepicker/input/README.md +15 -0
- package/dist/datepicker/popover/README.md +20 -0
- package/dist/datepicker/root/README.md +38 -0
- package/dist/datepicker/segment/README.md +14 -0
- package/dist/datepicker/trigger/README.md +14 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/primitives/focus-trap.js +11 -12
- package/dist/primitives/input-modality.js +10 -1
- package/dist/table/IMPLEMENTATION_NOTES.md +8 -0
- package/dist/table/PLAN-HIDDEN-COLUMNS.md +152 -0
- package/dist/table/PLAN.md +924 -0
- package/dist/table/README.md +116 -0
- package/dist/table/SELECTION_CHECKBOX_PLAN.md +234 -0
- package/dist/table/TODO.md +100 -0
- package/dist/table/body/README.md +24 -0
- package/dist/table/body/table-body.svelte +25 -0
- package/dist/table/body/table-body.svelte.d.ts +9 -0
- package/dist/table/cell/README.md +25 -0
- package/dist/table/cell/table-cell.svelte +247 -0
- package/dist/table/cell/table-cell.svelte.d.ts +9 -0
- package/dist/table/checkbox/README.md +38 -0
- package/dist/table/checkbox/table-checkbox-test.svelte +121 -0
- package/dist/table/checkbox/table-checkbox-test.svelte.d.ts +16 -0
- package/dist/table/checkbox/table-checkbox.svelte +274 -0
- package/dist/table/checkbox/table-checkbox.svelte.d.ts +13 -0
- package/dist/table/checkbox-indicator/README.md +29 -0
- package/dist/table/checkbox-indicator/table-checkbox-indicator.svelte +22 -0
- package/dist/table/checkbox-indicator/table-checkbox-indicator.svelte.d.ts +10 -0
- package/dist/table/column/README.md +32 -0
- package/dist/table/column/table-column.svelte +108 -0
- package/dist/table/column/table-column.svelte.d.ts +18 -0
- package/dist/table/column-header-cell/README.md +28 -0
- package/dist/table/column-header-cell/table-column-header-cell.svelte +281 -0
- package/dist/table/column-header-cell/table-column-header-cell.svelte.d.ts +9 -0
- package/dist/table/column-resizer/README.md +32 -0
- package/dist/table/column-resizer/table-column-resizer-freeze-layout-test.svelte +51 -0
- package/dist/table/column-resizer/table-column-resizer-freeze-layout-test.svelte.d.ts +3 -0
- package/dist/table/column-resizer/table-column-resizer-selection-column-test.svelte +83 -0
- package/dist/table/column-resizer/table-column-resizer-selection-column-test.svelte.d.ts +3 -0
- package/dist/table/column-resizer/table-column-resizer-test.svelte +75 -0
- package/dist/table/column-resizer/table-column-resizer-test.svelte.d.ts +3 -0
- package/dist/table/column-resizer/table-column-resizer.svelte +616 -0
- package/dist/table/column-resizer/table-column-resizer.svelte.d.ts +11 -0
- package/dist/table/empty-state/README.md +25 -0
- package/dist/table/empty-state/table-empty-state.svelte +38 -0
- package/dist/table/empty-state/table-empty-state.svelte.d.ts +8 -0
- package/dist/table/footer/README.md +24 -0
- package/dist/table/footer/table-footer.svelte +19 -0
- package/dist/table/footer/table-footer.svelte.d.ts +9 -0
- package/dist/table/header/README.md +24 -0
- package/dist/table/header/table-header.svelte +19 -0
- package/dist/table/header/table-header.svelte.d.ts +9 -0
- package/dist/table/index.d.ts +16 -0
- package/dist/table/index.js +16 -0
- package/dist/table/index.parts.d.ts +12 -0
- package/dist/table/index.parts.js +12 -0
- package/dist/table/root/README.md +56 -0
- package/dist/table/root/context.d.ts +198 -0
- package/dist/table/root/context.js +1426 -0
- package/dist/table/root/table-reorder-test.svelte +64 -0
- package/dist/table/root/table-reorder-test.svelte.d.ts +3 -0
- package/dist/table/root/table-root.svelte +410 -0
- package/dist/table/root/table-root.svelte.d.ts +29 -0
- package/dist/table/root/table-test.svelte +165 -0
- package/dist/table/root/table-test.svelte.d.ts +25 -0
- package/dist/table/row/README.md +27 -0
- package/dist/table/row/table-row.svelte +321 -0
- package/dist/table/row/table-row.svelte.d.ts +13 -0
- package/dist/timepicker/IMPLEMENTATION_PLAN.md +254 -0
- package/dist/timepicker/README.md +97 -0
- package/dist/timepicker/TODO.md +86 -0
- package/dist/timepicker/clock/README.md +14 -0
- package/dist/timepicker/clock/time-picker-clock-test.svelte +45 -0
- package/dist/timepicker/clock/time-picker-clock-test.svelte.d.ts +11 -0
- package/dist/timepicker/clock/time-picker-clock.svelte +65 -0
- package/dist/timepicker/clock/time-picker-clock.svelte.d.ts +10 -0
- package/dist/timepicker/index.d.ts +14 -0
- package/dist/timepicker/index.js +14 -0
- package/dist/timepicker/index.parts.d.ts +8 -0
- package/dist/timepicker/index.parts.js +8 -0
- package/dist/timepicker/input/README.md +15 -0
- package/dist/timepicker/input/time-picker-input-forwarding-test.svelte +40 -0
- package/dist/timepicker/input/time-picker-input-forwarding-test.svelte.d.ts +3 -0
- package/dist/timepicker/input/time-picker-input.svelte +109 -0
- package/dist/timepicker/input/time-picker-input.svelte.d.ts +11 -0
- package/dist/timepicker/internal/strict-props.d.ts +4 -0
- package/dist/timepicker/internal/strict-props.js +51 -0
- package/dist/timepicker/popover/README.md +20 -0
- package/dist/timepicker/popover/time-picker-popover-unsafe-props-test.svelte +22 -0
- package/dist/timepicker/popover/time-picker-popover-unsafe-props-test.svelte.d.ts +3 -0
- package/dist/timepicker/popover/time-picker-popover.svelte +89 -0
- package/dist/timepicker/popover/time-picker-popover.svelte.d.ts +7 -0
- package/dist/timepicker/root/README.md +42 -0
- package/dist/timepicker/root/context.d.ts +51 -0
- package/dist/timepicker/root/context.js +15 -0
- package/dist/timepicker/root/time-picker-12h-test.svelte +22 -0
- package/dist/timepicker/root/time-picker-12h-test.svelte.d.ts +3 -0
- package/dist/timepicker/root/time-picker-bindable-test.svelte +25 -0
- package/dist/timepicker/root/time-picker-bindable-test.svelte.d.ts +3 -0
- package/dist/timepicker/root/time-picker-empty-test.svelte +20 -0
- package/dist/timepicker/root/time-picker-empty-test.svelte.d.ts +3 -0
- package/dist/timepicker/root/time-picker-root.svelte +625 -0
- package/dist/timepicker/root/time-picker-root.svelte.d.ts +28 -0
- package/dist/timepicker/root/time-picker-test.svelte +72 -0
- package/dist/timepicker/root/time-picker-test.svelte.d.ts +15 -0
- package/dist/timepicker/root/time-utils.d.ts +1 -0
- package/dist/timepicker/root/time-utils.js +3 -0
- package/dist/timepicker/segment/README.md +14 -0
- package/dist/timepicker/segment/time-picker-segment.svelte +365 -0
- package/dist/timepicker/segment/time-picker-segment.svelte.d.ts +9 -0
- package/dist/timepicker/trigger/README.md +14 -0
- package/dist/timepicker/trigger/time-picker-trigger-forwarding-test.svelte +35 -0
- package/dist/timepicker/trigger/time-picker-trigger-forwarding-test.svelte.d.ts +3 -0
- package/dist/timepicker/trigger/time-picker-trigger.svelte +122 -0
- package/dist/timepicker/trigger/time-picker-trigger.svelte.d.ts +9 -0
- package/package.json +21 -1
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<!-- markdownlint-disable MD010 -->
|
|
2
|
+
|
|
3
|
+
# Table
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
`Table` is a headless interactive table primitive with grid-style keyboard navigation, row selection, sortable column headers, and a composable part-based API.
|
|
8
|
+
|
|
9
|
+
## Anatomy
|
|
10
|
+
|
|
11
|
+
```svelte
|
|
12
|
+
<Table.Root aria-label="Users table">
|
|
13
|
+
<Table.Header>
|
|
14
|
+
<Table.Row>
|
|
15
|
+
<Table.Column id="selection">
|
|
16
|
+
<Table.ColumnHeaderCell>
|
|
17
|
+
<Table.Checkbox>
|
|
18
|
+
<Table.CheckboxIndicator>
|
|
19
|
+
<CheckIcon />
|
|
20
|
+
</Table.CheckboxIndicator>
|
|
21
|
+
</Table.Checkbox>
|
|
22
|
+
</Table.ColumnHeaderCell>
|
|
23
|
+
</Table.Column>
|
|
24
|
+
<Table.Column id="email" isRowHeader>
|
|
25
|
+
<Table.ColumnHeaderCell>Email</Table.ColumnHeaderCell>
|
|
26
|
+
</Table.Column>
|
|
27
|
+
<Table.Column id="group" allowsSorting>
|
|
28
|
+
<Table.ColumnHeaderCell>Group</Table.ColumnHeaderCell>
|
|
29
|
+
</Table.Column>
|
|
30
|
+
<Table.Column id="size" minWidth={120}>
|
|
31
|
+
<Table.ColumnHeaderCell>
|
|
32
|
+
Size
|
|
33
|
+
<Table.ColumnResizer />
|
|
34
|
+
</Table.ColumnHeaderCell>
|
|
35
|
+
</Table.Column>
|
|
36
|
+
</Table.Row>
|
|
37
|
+
</Table.Header>
|
|
38
|
+
|
|
39
|
+
<Table.Body>
|
|
40
|
+
<Table.Row id="danilo">
|
|
41
|
+
<Table.Cell>
|
|
42
|
+
<Table.Checkbox>
|
|
43
|
+
<Table.CheckboxIndicator>
|
|
44
|
+
<CheckIcon />
|
|
45
|
+
</Table.CheckboxIndicator>
|
|
46
|
+
</Table.Checkbox>
|
|
47
|
+
</Table.Cell>
|
|
48
|
+
<Table.Cell>danilo@example.com</Table.Cell>
|
|
49
|
+
<Table.Cell>Developer</Table.Cell>
|
|
50
|
+
</Table.Row>
|
|
51
|
+
<Table.EmptyState>No users found.</Table.EmptyState>
|
|
52
|
+
</Table.Body>
|
|
53
|
+
|
|
54
|
+
<Table.Footer>
|
|
55
|
+
<Table.Row>
|
|
56
|
+
<Table.Cell />
|
|
57
|
+
<Table.Cell>Total</Table.Cell>
|
|
58
|
+
<Table.Cell>1 user</Table.Cell>
|
|
59
|
+
</Table.Row>
|
|
60
|
+
</Table.Footer>
|
|
61
|
+
</Table.Root>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- `Table.Root`
|
|
65
|
+
- `Table.Column`
|
|
66
|
+
- `Table.Header`
|
|
67
|
+
- `Table.Body`
|
|
68
|
+
- `Table.EmptyState`
|
|
69
|
+
- `Table.Footer`
|
|
70
|
+
- `Table.Row`
|
|
71
|
+
- `Table.ColumnHeaderCell`
|
|
72
|
+
- `Table.ColumnResizer`
|
|
73
|
+
- `Table.Checkbox`
|
|
74
|
+
- `Table.CheckboxIndicator`
|
|
75
|
+
- `Table.Cell`
|
|
76
|
+
|
|
77
|
+
## Usage guidelines
|
|
78
|
+
|
|
79
|
+
- Use `Table.Root` as the stateful container for focus, selection, and sorting state.
|
|
80
|
+
- Use `selectionBehavior="toggle"` to allow deselecting an already selected row, or `selectionBehavior="replace"` to keep selected rows selected when pressed again.
|
|
81
|
+
- `Table.Column` is a logical-only wrapper for column metadata; it does not render DOM by itself and should wrap a single `Table.ColumnHeaderCell`.
|
|
82
|
+
- Wrap each header cell in `Table.Column` so the table can register stable column metadata.
|
|
83
|
+
- Add `Table.ColumnResizer` inside `Table.ColumnHeaderCell` to make the owning `Table.Column` resizable.
|
|
84
|
+
- Provide `aria-label` or `aria-labelledby` on `Table.Root`.
|
|
85
|
+
- Use `selectedKeys` / `onSelectionChange` for controlled row selection.
|
|
86
|
+
- Use `defaultSelectedKeys` for uncontrolled initial row selection.
|
|
87
|
+
- Use `sortDescriptor` / `onSortChange` for controlled sorting state.
|
|
88
|
+
- Use `defaultSortDescriptor` for uncontrolled initial sort state.
|
|
89
|
+
- Use `hiddenColumns` for controlled column visibility when consumers need to show or hide columns without changing the table markup.
|
|
90
|
+
- Use `defaultHiddenColumns` for uncontrolled initial column visibility.
|
|
91
|
+
- Use `columnWidths` / `onColumnWidthsChange` for controlled column width state.
|
|
92
|
+
- Use `defaultColumnWidths`, `Table.Column.defaultWidth`, and `Table.Column.width` to seed explicit widths.
|
|
93
|
+
- Setting `sortDescriptor` back to `undefined` clears the controlled sort state, matching React Aria Table semantics.
|
|
94
|
+
- Set `Table.Column.textValue` when the spoken column label should differ from the column id; `Table.Root` uses it for polite sort announcements.
|
|
95
|
+
- Use `Table.EmptyState` inside `Table.Body` instead of conditionally rendering freeform body content.
|
|
96
|
+
- Use `Table.Checkbox` when you need explicit selection UI inside cells instead of relying only on row or cell presses.
|
|
97
|
+
- Use `Table.CheckboxIndicator` to compose your own visual affordance for checked and indeterminate states.
|
|
98
|
+
- `Table.Checkbox` auto-hides in header cells unless `selectionMode="multiple"`, and auto-hides everywhere when `selectionMode="none"`.
|
|
99
|
+
- Hidden columns are excluded from grid navigation, visible column counts, and active resize behavior, but their registered widths are preserved so they can be restored when shown again.
|
|
100
|
+
- Dedicated utility columns like selection checkboxes should usually set an explicit `width`, `minWidth`, and `maxWidth` on `Table.Column` so sibling resizes do not redistribute their space.
|
|
101
|
+
- When `selectionMode` changes to `none`, the component clears any existing row selection internally.
|
|
102
|
+
- v1 leaves text selection and `Ctrl+C` behavior browser-native; the table does not implement custom copy handling or force a text-selection policy.
|
|
103
|
+
- In `replace` mode, clicking outside the table clears focus but does not clear selection.
|
|
104
|
+
- In body rows, pressing `ArrowLeft` before the first cell or `ArrowRight` after the last cell moves focus to the row itself. Repeating that same horizontal arrow loops back into the opposite edge cell of the same row.
|
|
105
|
+
- `Table.Checkbox` is the supported interactive control inside table cells for explicit row selection in v1.
|
|
106
|
+
|
|
107
|
+
## Accessibility
|
|
108
|
+
|
|
109
|
+
- `Table.Root` renders an interactive `grid` over native table markup.
|
|
110
|
+
- Keyboard navigation uses roving `tabindex` across header and body cells.
|
|
111
|
+
- Body rows can also become the active focus target when horizontal navigation moves past the start or end of a row, and repeated left/right navigation loops back into the opposite edge cell.
|
|
112
|
+
- `Table.Checkbox` can receive DOM focus directly while still participating in the table's roving-focus grid.
|
|
113
|
+
- First-column body cells become `rowheader` when their associated column has `isRowHeader`.
|
|
114
|
+
- Disabled rows remain rendered and non-selectable, but are skipped by focus navigation.
|
|
115
|
+
- Sort changes are mirrored into a polite live region so screen readers announce direction changes more reliably than `aria-sort` alone.
|
|
116
|
+
- Column resize handles are keyboard accessible separators. Press `Enter` to enter resize mode, use the horizontal arrow keys to resize, `Home` to jump to the minimum width, `End` to auto-fit to content width, and press `Enter` again to exit resize mode while keeping focus on the handle.
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
<!-- markdownlint-disable MD013 MD033 MD056 MD060 -->
|
|
2
|
+
|
|
3
|
+
# Table Selection Checkbox Plan
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
|
|
7
|
+
Document the intended design for `Table.Checkbox` and `Table.CheckboxIndicator` parts that integrate with the existing table selection model without adding a separate high-level selection API.
|
|
8
|
+
|
|
9
|
+
The component should be composable, predictable, and aligned with the current `Table` architecture:
|
|
10
|
+
|
|
11
|
+
- part-based composition
|
|
12
|
+
- centralized state in `Table.Root`
|
|
13
|
+
- typed context contracts
|
|
14
|
+
- Svelte 5 runes
|
|
15
|
+
- keyboard navigation consistent with the current grid model
|
|
16
|
+
|
|
17
|
+
## Confirmed Decisions
|
|
18
|
+
|
|
19
|
+
- Public names: `Table.Checkbox` and `Table.CheckboxIndicator`
|
|
20
|
+
- Composition model: the consumer places it manually inside `Table.Cell` and `Table.ColumnHeaderCell`
|
|
21
|
+
- Focus target: the checkbox itself, not the table cell
|
|
22
|
+
- Body behavior: toggles the row selection state and reflects it visually
|
|
23
|
+
- Header behavior: acts as select-all / deselect-all only in `selectionMode="multiple"`
|
|
24
|
+
- Header partial state: must support indeterminate state
|
|
25
|
+
- Auto-visibility:
|
|
26
|
+
- when `selectionMode="none"`, all selection checkboxes should disappear
|
|
27
|
+
- when `selectionMode="single"`, body checkboxes stay visible but the header checkbox should disappear
|
|
28
|
+
- The component should own its auto-hide behavior instead of pushing this responsibility to the consumer
|
|
29
|
+
|
|
30
|
+
## Non-Goals
|
|
31
|
+
|
|
32
|
+
- Do not add a separate slot-based table selection API for v1
|
|
33
|
+
- Do not require a dedicated selection column abstraction
|
|
34
|
+
- Do not reuse the generic `Checkbox.Root` implementation blindly if it complicates table focus/navigation integration
|
|
35
|
+
- Do not make row selection depend on clicking the full row when explicit checkbox UI is present
|
|
36
|
+
|
|
37
|
+
## Proposed Public Anatomy
|
|
38
|
+
|
|
39
|
+
```svelte
|
|
40
|
+
<Table.Root selectionMode="multiple" bind:selectedKeys>
|
|
41
|
+
<Table.Header>
|
|
42
|
+
<Table.Row>
|
|
43
|
+
<Table.Column id="selection">
|
|
44
|
+
<Table.ColumnHeaderCell>
|
|
45
|
+
<Table.Checkbox>
|
|
46
|
+
<Table.CheckboxIndicator>
|
|
47
|
+
<CheckIcon />
|
|
48
|
+
</Table.CheckboxIndicator>
|
|
49
|
+
</Table.Checkbox>
|
|
50
|
+
</Table.ColumnHeaderCell>
|
|
51
|
+
</Table.Column>
|
|
52
|
+
|
|
53
|
+
<Table.Column id="email" isRowHeader>
|
|
54
|
+
<Table.ColumnHeaderCell>Email</Table.ColumnHeaderCell>
|
|
55
|
+
</Table.Column>
|
|
56
|
+
</Table.Row>
|
|
57
|
+
</Table.Header>
|
|
58
|
+
|
|
59
|
+
<Table.Body>
|
|
60
|
+
{#each rows as row (row.id)}
|
|
61
|
+
<Table.Row id={row.id}>
|
|
62
|
+
<Table.Cell>
|
|
63
|
+
<Table.Checkbox>
|
|
64
|
+
<Table.CheckboxIndicator>
|
|
65
|
+
<CheckIcon />
|
|
66
|
+
</Table.CheckboxIndicator>
|
|
67
|
+
</Table.Checkbox>
|
|
68
|
+
</Table.Cell>
|
|
69
|
+
<Table.Cell>{row.email}</Table.Cell>
|
|
70
|
+
</Table.Row>
|
|
71
|
+
{/each}
|
|
72
|
+
</Table.Body>
|
|
73
|
+
</Table.Root>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Why This Shape
|
|
77
|
+
|
|
78
|
+
- It preserves the existing `Table` mental model: columns, header cells, body cells, footer cells.
|
|
79
|
+
- It avoids a parallel API such as `selection` slots or special root props that would only exist for one specific control.
|
|
80
|
+
- It keeps selection UI opt-in and composable.
|
|
81
|
+
- It allows consumers to position the checkbox column exactly where they need it.
|
|
82
|
+
|
|
83
|
+
## Section-Based Behavior
|
|
84
|
+
|
|
85
|
+
The component behavior depends on where it is rendered.
|
|
86
|
+
|
|
87
|
+
| Location | `selectionMode="none"` | `selectionMode="single"` | `selectionMode="multiple"` |
|
|
88
|
+
| ------------------------ | ---------------------- | ------------------------ | ---------------------------- |
|
|
89
|
+
| `Table.ColumnHeaderCell` | hidden | hidden | visible, select-all checkbox |
|
|
90
|
+
| `Table.Cell` in body row | hidden | visible, row toggle | visible, row toggle |
|
|
91
|
+
| footer cell | hidden | hidden | hidden |
|
|
92
|
+
|
|
93
|
+
## Behavior Details
|
|
94
|
+
|
|
95
|
+
### Body Checkbox
|
|
96
|
+
|
|
97
|
+
- Renders only inside body rows when `selectionMode` is not `none`
|
|
98
|
+
- Checked state mirrors whether the current row is selected
|
|
99
|
+
- Disabled state mirrors whether the current row is disabled
|
|
100
|
+
- Clicking it toggles the row through the same selection pipeline used by the table
|
|
101
|
+
- Keyboard interaction should behave like a table navigation target, not like an isolated form field
|
|
102
|
+
|
|
103
|
+
### Header Checkbox
|
|
104
|
+
|
|
105
|
+
- Renders only inside header cells when `selectionMode="multiple"`
|
|
106
|
+
- Checked when all selectable rows are selected
|
|
107
|
+
- Indeterminate when some selectable rows are selected but not all
|
|
108
|
+
- Unchecked when no selectable rows are selected
|
|
109
|
+
- Clicking it selects all selectable rows or clears the current selection
|
|
110
|
+
- Disabled when there are no selectable rows
|
|
111
|
+
|
|
112
|
+
## Focus Model
|
|
113
|
+
|
|
114
|
+
The current table uses a roving tabindex model on cells. For `Table.Checkbox`, the desired behavior is different:
|
|
115
|
+
|
|
116
|
+
- the checkbox should receive DOM focus directly
|
|
117
|
+
- the table should still treat that checkbox as the focus target for the parent cell's grid position
|
|
118
|
+
- arrow-key navigation must continue to work from the checkbox
|
|
119
|
+
|
|
120
|
+
This implies a focus-delegation mechanism from `Table.Cell` to a child control.
|
|
121
|
+
|
|
122
|
+
### Proposed Focus Delegation
|
|
123
|
+
|
|
124
|
+
- `Table.Cell` keeps owning the grid key and participation in layout/focus bookkeeping
|
|
125
|
+
- a child control can register itself as the focus delegate for that cell
|
|
126
|
+
- when the table moves focus to that cell, the registered delegate receives DOM focus
|
|
127
|
+
- when the delegate receives focus, it synchronizes the table's focused-cell state
|
|
128
|
+
|
|
129
|
+
This is preferable to making the checkbox a completely separate navigable entity because it preserves the current grid architecture.
|
|
130
|
+
|
|
131
|
+
## Keyboard Expectations
|
|
132
|
+
|
|
133
|
+
When a `Table.Checkbox` is focused:
|
|
134
|
+
|
|
135
|
+
- `Space` toggles selection
|
|
136
|
+
- `Enter` toggles selection
|
|
137
|
+
- `ArrowUp` / `ArrowDown` move within the table grid
|
|
138
|
+
- `ArrowLeft` / `ArrowRight` move within the table grid
|
|
139
|
+
- `Home` / `End` move to row boundaries
|
|
140
|
+
- `Ctrl/Cmd + Home` and `Ctrl/Cmd + End` use the existing table-wide navigation behavior
|
|
141
|
+
- `Ctrl/Cmd + A` keeps the current multiple-selection behavior
|
|
142
|
+
|
|
143
|
+
The checkbox should not become a keyboard dead-end inside the table.
|
|
144
|
+
|
|
145
|
+
## State Requirements in `TableContext`
|
|
146
|
+
|
|
147
|
+
To support the header checkbox cleanly, the table context should expose a small amount of additional selection state:
|
|
148
|
+
|
|
149
|
+
- a way to determine whether all selectable rows are selected
|
|
150
|
+
- a way to determine whether some selectable rows are selected
|
|
151
|
+
- a way to clear the current selection without changing `selectionMode`
|
|
152
|
+
|
|
153
|
+
One reasonable shape is:
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
type TableSelectionCheckboxState = 'none' | 'some' | 'all';
|
|
157
|
+
|
|
158
|
+
getSelectionCheckboxState(): TableSelectionCheckboxState;
|
|
159
|
+
deselectAllRows(): void;
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
This should build on the existing ordered selectable row computation rather than duplicating selection rules in the component.
|
|
163
|
+
|
|
164
|
+
## Accessibility Expectations
|
|
165
|
+
|
|
166
|
+
- The checkbox should expose `role="checkbox"`
|
|
167
|
+
- `aria-checked` should be:
|
|
168
|
+
- `"true"` when checked
|
|
169
|
+
- `"false"` when unchecked
|
|
170
|
+
- `"mixed"` when indeterminate
|
|
171
|
+
- Disabled state should map to `aria-disabled`
|
|
172
|
+
- The header checkbox should have an accessible label like `Select all rows`
|
|
173
|
+
- Row checkboxes should have an accessible label like `Select row` or a consumer-provided label if the row content needs more specificity
|
|
174
|
+
|
|
175
|
+
## Rendering Strategy
|
|
176
|
+
|
|
177
|
+
The component does not need to be a full form checkbox.
|
|
178
|
+
|
|
179
|
+
Because this control is tightly coupled to the table's grid navigation and selection logic, a lightweight table-specific implementation is likely more appropriate than directly wrapping the generic checkbox component. The generic checkbox has different responsibilities, including form integration, that are not required here.
|
|
180
|
+
|
|
181
|
+
## Open Questions
|
|
182
|
+
|
|
183
|
+
### Structural Validation
|
|
184
|
+
|
|
185
|
+
If consumers place `Table.Checkbox` manually, it becomes easier to create invalid layouts accidentally.
|
|
186
|
+
|
|
187
|
+
Examples:
|
|
188
|
+
|
|
189
|
+
- header includes a selection checkbox column but body rows do not
|
|
190
|
+
- some rows render fewer cells than others
|
|
191
|
+
- the checkbox is placed in a footer cell where it has no behavior
|
|
192
|
+
|
|
193
|
+
We should consider adding dev-time validation for mismatched column/cell counts and possibly invalid placement.
|
|
194
|
+
|
|
195
|
+
### Labeling API
|
|
196
|
+
|
|
197
|
+
The initial implementation can ship with sensible default labels, but we may want to support explicit labels later for highly customized row content.
|
|
198
|
+
|
|
199
|
+
### Generic Focus Delegates
|
|
200
|
+
|
|
201
|
+
If focus delegation is added for this part, it should be designed as a reusable mechanism rather than a one-off checkbox hack.
|
|
202
|
+
|
|
203
|
+
## Implementation Outline
|
|
204
|
+
|
|
205
|
+
### Phase 1: Context and Focus Plumbing
|
|
206
|
+
|
|
207
|
+
- extend table selection helpers for header checkbox state
|
|
208
|
+
- add `deselectAllRows()`
|
|
209
|
+
- add focus delegation support from cells to nested controls
|
|
210
|
+
|
|
211
|
+
### Phase 2: New Part
|
|
212
|
+
|
|
213
|
+
- add `packages/svelte/src/lib/table/selection-checkbox/`
|
|
214
|
+
- implement `table-selection-checkbox.svelte`
|
|
215
|
+
- export it from `index.parts.ts` and `index.ts`
|
|
216
|
+
|
|
217
|
+
### Phase 3: Tests
|
|
218
|
+
|
|
219
|
+
- body checkbox renders and toggles correctly
|
|
220
|
+
- header checkbox auto-hides correctly by selection mode
|
|
221
|
+
- header checkbox supports checked / mixed / unchecked states
|
|
222
|
+
- disabled rows stay disabled
|
|
223
|
+
- focus lands on the checkbox instead of the parent cell
|
|
224
|
+
- arrow navigation still works when the checkbox has focus
|
|
225
|
+
|
|
226
|
+
### Phase 4: Documentation and Demo
|
|
227
|
+
|
|
228
|
+
- add part README if the part becomes public
|
|
229
|
+
- update table README anatomy
|
|
230
|
+
- update the docs demo with a selection checkbox column
|
|
231
|
+
|
|
232
|
+
## Summary
|
|
233
|
+
|
|
234
|
+
`Table.Checkbox` should be a small, composable part that plugs into the existing table selection model rather than a new selection subsystem. `Table.CheckboxIndicator` keeps the visuals fully consumer-controlled. The key implementation challenge is not checkbox visuals, but integrating direct checkbox focus with the table's current roving-focus grid behavior.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Table TODO
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Ship a stable `Table` v1 with keyboard navigation, row selection, sorting, documentation, and reliable accessibility semantics.
|
|
6
|
+
|
|
7
|
+
## Backlog
|
|
8
|
+
|
|
9
|
+
### Bugs / Correctness
|
|
10
|
+
|
|
11
|
+
- [x] [M][P0][Area: Correctness][Owner: Unassigned][Target: Done] Deduplicate sync registration calls — `syncXxxRegistration()` runs both synchronously at mount and inside `$effect`, causing a redundant `notifyLayout()` per part on initial render (Column, Row, Cell, ColumnHeaderCell).
|
|
12
|
+
- [x] [S][P0][Area: Correctness][Owner: Unassigned][Target: Done] Make `cellIndex` in `Table.Cell` reactive — currently captured once via `row.registerCellToken(key)` and never updated if cells are dynamically reordered within a row.
|
|
13
|
+
- [x] [C][P1][Area: Correctness][Owner: Unassigned][Target: Done] Replace module-level monotonic counters (`columnInstanceId`, `rowInstanceId`, etc.) with per-`Table.Root` token generation so SSR and repeated tests do not share global instance state.
|
|
14
|
+
|
|
15
|
+
### Behavior
|
|
16
|
+
|
|
17
|
+
- [x] [M][P1][Area: Behavior][Owner: Unassigned][Target: Done] Align row focus state semantics — current row-level `data-focused` behaves more like `focus-within`; either rename/expose a distinct `data-focus-within` contract or implement true row focus semantics.
|
|
18
|
+
- [x] [M][P1][Area: Behavior][Owner: Unassigned][Target: Done] Define and document the text-selection policy when row selection is enabled — keep browser-native behavior or add an explicit opt-in/opt-out API instead of ad hoc styling in examples.
|
|
19
|
+
- [x] [S][P2][Area: Behavior][Owner: Unassigned][Target: Done] Verify that changing `selectionMode` between `multiple`, `single`, and `none` preserves the intended normalization rules in all controlled and uncontrolled flows.
|
|
20
|
+
- [x] [M][P0][Area: Behavior][Owner: Unassigned][Target: Done] Make `Table.ColumnResizer` keyboard interactions run the same resize lifecycle as pointer interactions so width freezing and resize callbacks stay consistent.
|
|
21
|
+
- [x] [M][P1][Area: Behavior][Owner: Unassigned][Target: Done] Allow cancelling an in-progress `Table.ColumnResizer` drag with `Escape`, restoring the starting width instead of forcing the partial drag result.
|
|
22
|
+
|
|
23
|
+
### Accessibility
|
|
24
|
+
|
|
25
|
+
- [x] [M][P1][Area: Accessibility][Owner: Unassigned][Target: Done] Enforce accessible name on `Table.Root` — warn in dev when neither `aria-label` nor `aria-labelledby` is provided (WCAG 4.1.2).
|
|
26
|
+
- [x] [M][P1][Area: Accessibility][Owner: Unassigned][Target: Done] Add `aria-disabled` to focusable body cells inside disabled rows — currently only the `<tr>` carries `aria-disabled`, leaving screen readers unaware at the cell level.
|
|
27
|
+
- [x] [M][P0][Area: Accessibility][Owner: Unassigned][Target: Done] Announce committed `Table.ColumnResizer` width changes through a polite live region and expose `aria-valuetext` so keyboard resizing has reliable screen reader feedback.
|
|
28
|
+
- [x] [M][P0][Area: Accessibility][Owner: Unassigned][Target: Done] Move `Table.ColumnResizer` drag handling to Pointer Events so touch, pen, and mouse resizing use the same path.
|
|
29
|
+
- [x] [S][P1][Area: Accessibility][Owner: Unassigned][Target: Done] Make `Table.ColumnResizer` keyboard arrow semantics respect RTL layouts so logical resize controls match the visual direction.
|
|
30
|
+
- [ ] [M][P1][Area: Accessibility][Owner: Unassigned][Target: TBD] Validate screen reader announcements for `rowheader`, `columnheader`, and `aria-sort` across NVDA and VoiceOver.
|
|
31
|
+
- [x] [M][P1][Area: Accessibility][Owner: Unassigned][Target: Done] Resolve footer grid semantics — footer cells live inside `role="grid"` but are unreachable by keyboard; either include footer in navigation or mark `<tfoot>` with `role="none"` to exclude it from the grid.
|
|
32
|
+
- [x] [S][P2][Area: Accessibility][Owner: Unassigned][Target: Done] Add `aria-rowcount` and `aria-colcount` to the grid element for screen reader dimension announcements.
|
|
33
|
+
- [x] [S][P2][Area: Accessibility][Owner: Unassigned][Target: Done] Add an `aria-live="polite"` visually-hidden region to announce sort changes (NVDA/JAWS do not always announce `aria-sort` updates).
|
|
34
|
+
- [x] [S][P2][Area: Accessibility][Owner: Unassigned][Target: Done] Add explicit `role="row"` to the `Table.EmptyState` `<tr>` for strict ARIA parent-child validation (`gridcell` requires `row` parent).
|
|
35
|
+
|
|
36
|
+
### Performance
|
|
37
|
+
|
|
38
|
+
- [x] [M][P1][Area: Performance][Owner: Unassigned][Target: Done] Cache `getOrderedRowTokens` result and invalidate on `notifyLayout` — currently re-sorts with `compareDocumentPosition` on every focus move, selection, and row count query.
|
|
39
|
+
- [x] [M][P1][Area: Performance][Owner: Unassigned][Target: Done] Replace repeated linear column-id scans in table resize helpers with an O(1) id-to-token lookup inside `Table.Root` context.
|
|
40
|
+
- [x] [S][P1][Area: Performance][Owner: Unassigned][Target: Done] Batch `Table.ColumnResizer` pointer drag updates with `requestAnimationFrame` so width writes and store updates stay aligned to paint frames.
|
|
41
|
+
- [x] [M][P2][Area: Performance][Owner: Unassigned][Target: Done] Cache `getNavigableCells()` / `getRowsWithCells()` and invalidate on layout changes — currently reconstructs full cell map on every `moveFocus` call.
|
|
42
|
+
- [ ] [S][P2][Area: Performance][Owner: Unassigned][Target: TBD] Migrate version stores from `writable` (svelte/store) to `$state` runes for idiomatic Svelte 5 reactivity and potential batching improvements.
|
|
43
|
+
|
|
44
|
+
### DX
|
|
45
|
+
|
|
46
|
+
- [x] [M][P1][Area: DX][Owner: Unassigned][Target: Done] Break the controlled `selectedKeys` feedback loop — the `$effect` that syncs `selectedKeys` to `ctx.setSelection` also fires after internal selection changes via `onSelectionChange`, causing a redundant `notifySelection`.
|
|
47
|
+
- [x] [S][P2][Area: DX][Owner: Unassigned][Target: Done] Remove inline `style="outline: none;"` from Cell and ColumnHeaderCell — it overrides consumer inline styles; let consumers handle focus-visible styling via `data-focus-visible` / `data-focused` attributes instead.
|
|
48
|
+
- [x] [S][P2][Area: DX][Owner: Unassigned][Target: Done] Document `defaultSelectedKeys` and `defaultSortDescriptor` props in the README and docs page.
|
|
49
|
+
- [ ] [C][P2][Area: DX][Owner: Unassigned][Target: TBD] Export component prop types (`TableRootProps`, `TableRowProps`, `TableCellProps`, etc.) so consumers can type wrapper components.
|
|
50
|
+
- [ ] [C][P2][Area: DX][Owner: Unassigned][Target: TBD] Evaluate whether exposing `context` as a `$bindable` prop on `Table.Root` is necessary or if a narrower public API would be safer.
|
|
51
|
+
- [ ] [C][P3][Area: DX][Owner: Unassigned][Target: TBD] Extract a shared registration helper to eliminate the duplicated sync-then-effect pattern across Column, Row, Cell, and ColumnHeaderCell.
|
|
52
|
+
|
|
53
|
+
### UX
|
|
54
|
+
|
|
55
|
+
- [x] [S][P2][Area: UX][Owner: Unassigned][Target: Done] Document the intentional behavior that clicking outside the table does not clear selection in `replace` mode.
|
|
56
|
+
- [ ] [C][P3][Area: UX][Owner: Unassigned][Target: TBD] Consider exposing `cursor` guidance via data attributes so sortable headers get `cursor: pointer` and non-sortable headers get `cursor: default` without custom CSS.
|
|
57
|
+
|
|
58
|
+
### API Design
|
|
59
|
+
|
|
60
|
+
- [x] [M][P1][Area: API][Owner: Unassigned][Target: Done] Decide whether controlled clearing of `sortDescriptor` should accept `undefined` explicitly or require an additional API.
|
|
61
|
+
- [x] [S][P2][Area: Behavior][Owner: Unassigned][Target: Done] Confirm whether disabled body rows should remain keyboard-focusable or be skipped by navigation.
|
|
62
|
+
- [ ] [S][P2][Area: API][Owner: Unassigned][Target: TBD] Decide whether `Table.Column` should hard-enforce a single `Table.ColumnHeaderCell` child.
|
|
63
|
+
- [ ] [S][P2][Area: API][Owner: Unassigned][Target: TBD] Decide whether clipboard-related behavior should remain fully browser-native in v1 or be deferred behind a future explicit cell-selection model.
|
|
64
|
+
|
|
65
|
+
### Features
|
|
66
|
+
|
|
67
|
+
- [ ] [C][P1][Area: Features][Owner: Unassigned][Target: TBD] Add controlled and uncontrolled column filtering APIs — support per-column filters plus a global filter hook so `Table` can cover common data-grid scenarios without forcing consumers to build parallel filter state from scratch.
|
|
68
|
+
- [ ] [C][P1][Area: Features][Owner: Unassigned][Target: TBD] Add column visibility controls — allow columns to be shown/hidden dynamically while preserving keyboard navigation, sort state, selection column layout, and resize state for still-visible columns.
|
|
69
|
+
- [ ] [C][P1][Area: Features][Owner: Unassigned][Target: TBD] Add multi-column sorting — support ordered sort descriptors so users can compose secondary and tertiary sorts instead of being limited to a single active sort key.
|
|
70
|
+
- [ ] [C][P1][Area: Features][Owner: Unassigned][Target: TBD] Add pagination primitives — define a controlled pagination model for large datasets, including page index, page size, and total row count integration without coupling `Table` to a specific data-fetching strategy.
|
|
71
|
+
- [ ] [C][P1][Area: Features][Owner: Unassigned][Target: TBD] Evaluate row virtualization support — provide a path for very large tables while preserving grid semantics, roving tabindex behavior, selection, and resize interactions.
|
|
72
|
+
- [ ] [C][P2][Area: Features][Owner: Unassigned][Target: TBD] Add sticky header and pinned-column support — enable frozen headers and utility columns (selection, row headers, actions) for wide or scroll-heavy tables.
|
|
73
|
+
- [ ] [C][P2][Area: Features][Owner: Unassigned][Target: TBD] Add expandable rows / subrows — support master-detail rows and tree-like disclosure patterns without forcing consumers to break the table's row and cell registration model.
|
|
74
|
+
- [ ] [C][P2][Area: Features][Owner: Unassigned][Target: TBD] Add inline cell and row editing primitives — define an opt-in editing model that can coexist with current focus, selection, and keyboard navigation contracts.
|
|
75
|
+
- [ ] [M][P2][Area: Features][Owner: Unassigned][Target: TBD] Add column action primitives — expose a path for header menus, quick sort/filter actions, and future column management UI without requiring consumers to hand-roll header action composition every time.
|
|
76
|
+
- [ ] [M][P3][Area: Features][Owner: Unassigned][Target: TBD] Add row actions patterns — document or expose a composable pattern for common trailing actions columns so selection, row press, and nested interactive controls do not conflict.
|
|
77
|
+
|
|
78
|
+
### Tests
|
|
79
|
+
|
|
80
|
+
- [x] [M][P1][Area: Tests][Owner: Unassigned][Target: Done] Add tests for `Home`, `End`, `Ctrl+Home`, and `Ctrl+End` keyboard navigation.
|
|
81
|
+
- [x] [S][P2][Area: Tests][Owner: Unassigned][Target: Done] Add test for full sort cycle via keyboard (ascending → descending) and verify no way to clear sort with keyboard is intentional.
|
|
82
|
+
- [x] [S][P2][Area: Tests][Owner: Unassigned][Target: Done] Add test verifying disabled rows are not selected when arrow-navigated in `replace` mode.
|
|
83
|
+
- [x] [S][P2][Area: Tests][Owner: Unassigned][Target: Done] Add tests covering `selectionMode` transitions after mount, including collapsing multiple selections to one on `single`.
|
|
84
|
+
|
|
85
|
+
### Docs
|
|
86
|
+
|
|
87
|
+
- [ ] [C][P2][Area: Docs][Owner: Unassigned][Target: TBD] Add richer styling examples and sorting guidance to the docs page.
|
|
88
|
+
- [x] [C][P3][Area: Docs][Owner: Unassigned][Target: Done] Document that `Table.Column` is a logical-only wrapper (no DOM output) prominently in the README anatomy section.
|
|
89
|
+
- [x] [S][P2][Area: Docs][Owner: Unassigned][Target: Done] Document `selectionMode="none"` normalization behavior and clarify that selection is cleared internally when selection is disabled.
|
|
90
|
+
|
|
91
|
+
### Selection Checkbox
|
|
92
|
+
|
|
93
|
+
- [ ] [M][P1][Area: Performance][Owner: Unassigned][Target: TBD] Cache `getOrderedSelectableRowIds()` and invalidate on layout/selection changes — currently rebuilds the full selectable-row array on every call to `getSelectionCheckboxState()`, `hasSelectableRows()`, `selectAllRows()`, and `extendSelectionToRow()`, causing O(n) work per selection change.
|
|
94
|
+
- [ ] [M][P1][Area: Accessibility][Owner: Unassigned][Target: TBD] Validate `Table.Checkbox` screen reader announcements across NVDA and VoiceOver — verify that `aria-checked="mixed"` transitions in the header checkbox and row selection toggles are announced correctly.
|
|
95
|
+
- [ ] [M][P1][Area: Correctness][Owner: Unassigned][Target: TBD] Document or resolve `Table.Checkbox` bypass of `pressRow()` — the checkbox always calls `toggleRowSelection()` directly, ignoring `selectionBehavior="replace"` semantics (Shift+click range, Ctrl+click toggle). This is intentional for checkbox UX but undocumented and inconsistent with row-click behavior.
|
|
96
|
+
- [ ] [M][P1][Area: Correctness][Owner: Unassigned][Target: TBD] Add dev-time structural validation for `Table.Checkbox` placement — warn when header includes a selection checkbox column but body rows do not, or when the checkbox is placed in a footer cell where it has no behavior.
|
|
97
|
+
|
|
98
|
+
### Code Quality
|
|
99
|
+
|
|
100
|
+
- [ ] [C][P3][Area: Code Quality][Owner: Unassigned][Target: TBD] Remove unused keyboard/click handlers from `Table.Cell` when rendering in footer scope — currently handlers are bound but short-circuit via guards.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<!-- markdownlint-disable MD024 -->
|
|
2
|
+
|
|
3
|
+
# Table.Body
|
|
4
|
+
|
|
5
|
+
## API reference
|
|
6
|
+
|
|
7
|
+
### Table.Body
|
|
8
|
+
|
|
9
|
+
Name: `Table.Body`
|
|
10
|
+
Description: Body rowgroup for table data rows. It also exposes empty-state markers when no body rows are registered.
|
|
11
|
+
|
|
12
|
+
| Prop | Type | Default | Description |
|
|
13
|
+
| ---------- | --------- | ----------- | ------------------------------------------ |
|
|
14
|
+
| `class` | `string` | `''` | Class names for the `tbody` element. |
|
|
15
|
+
| `children` | `Snippet` | `undefined` | Body rows and optional `Table.EmptyState`. |
|
|
16
|
+
|
|
17
|
+
### Context utilities
|
|
18
|
+
|
|
19
|
+
Name: `Table.Body` section context
|
|
20
|
+
Description: Publishes the `body` section scope for descendant rows, cells, and empty state.
|
|
21
|
+
|
|
22
|
+
| Prop | Type | Default | Description |
|
|
23
|
+
| ------------------------ | --------------------------- | ------- | -------------------------------- |
|
|
24
|
+
| `useTableSectionContext` | `() => TableSectionContext` | `-` | Reads the current section scope. |
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import { setTableSectionContext, useTableContext } from '../root/context';
|
|
5
|
+
|
|
6
|
+
type TableBodyProps = Omit<HTMLAttributes<HTMLTableSectionElement>, 'children'> & {
|
|
7
|
+
children?: Snippet;
|
|
8
|
+
class?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
let { children, class: className = '', ...restProps }: TableBodyProps = $props();
|
|
12
|
+
setTableSectionContext({ section: 'body' });
|
|
13
|
+
const table = useTableContext();
|
|
14
|
+
const layoutVersion = table.layoutVersion;
|
|
15
|
+
const isEmpty = $derived.by(() => {
|
|
16
|
+
void $layoutVersion;
|
|
17
|
+
return table.getBodyRowCount() === 0;
|
|
18
|
+
});
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<tbody class={className} data-table-body data-empty={isEmpty || undefined} {...restProps}>
|
|
22
|
+
{#if children}
|
|
23
|
+
{@render children()}
|
|
24
|
+
{/if}
|
|
25
|
+
</tbody>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
type TableBodyProps = Omit<HTMLAttributes<HTMLTableSectionElement>, 'children'> & {
|
|
4
|
+
children?: Snippet;
|
|
5
|
+
class?: string;
|
|
6
|
+
};
|
|
7
|
+
declare const TableBody: import("svelte").Component<TableBodyProps, {}, "">;
|
|
8
|
+
type TableBody = ReturnType<typeof TableBody>;
|
|
9
|
+
export default TableBody;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!-- markdownlint-disable MD024 -->
|
|
2
|
+
|
|
3
|
+
# Table.Cell
|
|
4
|
+
|
|
5
|
+
## API reference
|
|
6
|
+
|
|
7
|
+
### Table.Cell
|
|
8
|
+
|
|
9
|
+
Name: `Table.Cell`
|
|
10
|
+
Description: Table data cell part. In body scope it participates in roving focus and row selection. In footer scope it renders semantic summary cells only.
|
|
11
|
+
|
|
12
|
+
| Prop | Type | Default | Description |
|
|
13
|
+
| ---------- | --------- | ----------- | ------------------------------------------ |
|
|
14
|
+
| `class` | `string` | `''` | Class names for the rendered `td` or `th`. |
|
|
15
|
+
| `children` | `Snippet` | `undefined` | Cell content. |
|
|
16
|
+
|
|
17
|
+
### Context utilities
|
|
18
|
+
|
|
19
|
+
Name: `Table.Cell` dependencies
|
|
20
|
+
Description: Consumes row context and table context to derive column semantics and keyboard behavior.
|
|
21
|
+
|
|
22
|
+
| Prop | Type | Default | Description |
|
|
23
|
+
| -------------------- | ----------------------- | ------- | -------------------------------------------------------- |
|
|
24
|
+
| `useTableRowContext` | `() => TableRowContext` | `-` | Reads row scope and cell order. |
|
|
25
|
+
| `useTableContext` | `() => TableContext` | `-` | Reads table focus, column metadata, and selection state. |
|