@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.
Files changed (189) hide show
  1. package/dist/FOCUS_STATE_CONTRACT.md +12 -0
  2. package/dist/calendar/body-cell/README.md +15 -0
  3. package/dist/calendar/grid/README.md +13 -0
  4. package/dist/calendar/grid-body/README.md +13 -0
  5. package/dist/calendar/grid-header/README.md +13 -0
  6. package/dist/calendar/header-cell/README.md +14 -0
  7. package/dist/calendar/heading/README.md +13 -0
  8. package/dist/calendar/root/README.md +24 -0
  9. package/dist/calendar/trigger-next/README.md +14 -0
  10. package/dist/calendar/trigger-previous/README.md +14 -0
  11. package/dist/checkbox/README.md +53 -0
  12. package/dist/checkbox/TODO.md +16 -0
  13. package/dist/checkbox/index.d.ts +6 -0
  14. package/dist/checkbox/index.js +6 -0
  15. package/dist/checkbox/index.parts.d.ts +2 -0
  16. package/dist/checkbox/index.parts.js +2 -0
  17. package/dist/checkbox/indicator/README.md +23 -0
  18. package/dist/checkbox/indicator/checkbox-indicator.svelte +43 -0
  19. package/dist/checkbox/indicator/checkbox-indicator.svelte.d.ts +10 -0
  20. package/dist/checkbox/root/README.md +47 -0
  21. package/dist/checkbox/root/checkbox-label-test.svelte +10 -0
  22. package/dist/checkbox/root/checkbox-label-test.svelte.d.ts +18 -0
  23. package/dist/checkbox/root/checkbox-root.svelte +361 -0
  24. package/dist/checkbox/root/checkbox-root.svelte.d.ts +23 -0
  25. package/dist/checkbox/root/checkbox-test.svelte +59 -0
  26. package/dist/checkbox/root/checkbox-test.svelte.d.ts +18 -0
  27. package/dist/checkbox/root/context.d.ts +21 -0
  28. package/dist/checkbox/root/context.js +15 -0
  29. package/dist/clock/README.md +75 -0
  30. package/dist/clock/axis/README.md +24 -0
  31. package/dist/clock/axis/clock-axis.svelte +37 -0
  32. package/dist/clock/axis/clock-axis.svelte.d.ts +8 -0
  33. package/dist/clock/hooks/use-wheel-scroll.svelte.d.ts +16 -0
  34. package/dist/clock/hooks/use-wheel-scroll.svelte.js +336 -0
  35. package/dist/clock/index.d.ts +10 -0
  36. package/dist/clock/index.js +10 -0
  37. package/dist/clock/index.parts.d.ts +4 -0
  38. package/dist/clock/index.parts.js +4 -0
  39. package/dist/clock/root/README.md +38 -0
  40. package/dist/clock/root/clock-root-test.svelte +62 -0
  41. package/dist/clock/root/clock-root-test.svelte.d.ts +14 -0
  42. package/dist/clock/root/clock-root.svelte +329 -0
  43. package/dist/clock/root/clock-root.svelte.d.ts +25 -0
  44. package/dist/clock/root/context.d.ts +22 -0
  45. package/dist/clock/root/context.js +15 -0
  46. package/dist/clock/root/resolve-visible-columns.d.ts +7 -0
  47. package/dist/clock/root/resolve-visible-columns.js +16 -0
  48. package/dist/clock/root/time-utils.d.ts +48 -0
  49. package/dist/clock/root/time-utils.js +314 -0
  50. package/dist/clock/root/wheel-options.d.ts +17 -0
  51. package/dist/clock/root/wheel-options.js +63 -0
  52. package/dist/clock/wheel-column/README.md +25 -0
  53. package/dist/clock/wheel-column/clock-wheel-column-bindable-test.svelte +16 -0
  54. package/dist/clock/wheel-column/clock-wheel-column-bindable-test.svelte.d.ts +3 -0
  55. package/dist/clock/wheel-column/clock-wheel-column-custom-snippet-test.svelte +29 -0
  56. package/dist/clock/wheel-column/clock-wheel-column-custom-snippet-test.svelte.d.ts +6 -0
  57. package/dist/clock/wheel-column/clock-wheel-column-default-height-test.svelte +11 -0
  58. package/dist/clock/wheel-column/clock-wheel-column-default-height-test.svelte.d.ts +3 -0
  59. package/dist/clock/wheel-column/clock-wheel-column-test.svelte +38 -0
  60. package/dist/clock/wheel-column/clock-wheel-column-test.svelte.d.ts +12 -0
  61. package/dist/clock/wheel-column/clock-wheel-column-tp-test.svelte +38 -0
  62. package/dist/clock/wheel-column/clock-wheel-column-tp-test.svelte.d.ts +12 -0
  63. package/dist/clock/wheel-column/clock-wheel-column-untagged-snippet-test.svelte +29 -0
  64. package/dist/clock/wheel-column/clock-wheel-column-untagged-snippet-test.svelte.d.ts +6 -0
  65. package/dist/clock/wheel-column/clock-wheel-column.svelte +499 -0
  66. package/dist/clock/wheel-column/clock-wheel-column.svelte.d.ts +17 -0
  67. package/dist/clock/wheel-item/README.md +17 -0
  68. package/dist/clock/wheel-item/clock-wheel-item.svelte +49 -0
  69. package/dist/clock/wheel-item/clock-wheel-item.svelte.d.ts +17 -0
  70. package/dist/combobox/list/combobox-listbox.svelte.d.ts +1 -1
  71. package/dist/datepicker/TODO.md +2 -2
  72. package/dist/datepicker/calendar/README.md +19 -0
  73. package/dist/datepicker/input/README.md +15 -0
  74. package/dist/datepicker/popover/README.md +20 -0
  75. package/dist/datepicker/root/README.md +38 -0
  76. package/dist/datepicker/segment/README.md +14 -0
  77. package/dist/datepicker/trigger/README.md +14 -0
  78. package/dist/index.d.ts +9 -0
  79. package/dist/index.js +9 -0
  80. package/dist/primitives/focus-trap.js +11 -12
  81. package/dist/primitives/input-modality.js +10 -1
  82. package/dist/table/IMPLEMENTATION_NOTES.md +8 -0
  83. package/dist/table/PLAN-HIDDEN-COLUMNS.md +152 -0
  84. package/dist/table/PLAN.md +924 -0
  85. package/dist/table/README.md +116 -0
  86. package/dist/table/SELECTION_CHECKBOX_PLAN.md +234 -0
  87. package/dist/table/TODO.md +100 -0
  88. package/dist/table/body/README.md +24 -0
  89. package/dist/table/body/table-body.svelte +25 -0
  90. package/dist/table/body/table-body.svelte.d.ts +9 -0
  91. package/dist/table/cell/README.md +25 -0
  92. package/dist/table/cell/table-cell.svelte +247 -0
  93. package/dist/table/cell/table-cell.svelte.d.ts +9 -0
  94. package/dist/table/checkbox/README.md +38 -0
  95. package/dist/table/checkbox/table-checkbox-test.svelte +121 -0
  96. package/dist/table/checkbox/table-checkbox-test.svelte.d.ts +16 -0
  97. package/dist/table/checkbox/table-checkbox.svelte +274 -0
  98. package/dist/table/checkbox/table-checkbox.svelte.d.ts +13 -0
  99. package/dist/table/checkbox-indicator/README.md +29 -0
  100. package/dist/table/checkbox-indicator/table-checkbox-indicator.svelte +22 -0
  101. package/dist/table/checkbox-indicator/table-checkbox-indicator.svelte.d.ts +10 -0
  102. package/dist/table/column/README.md +32 -0
  103. package/dist/table/column/table-column.svelte +108 -0
  104. package/dist/table/column/table-column.svelte.d.ts +18 -0
  105. package/dist/table/column-header-cell/README.md +28 -0
  106. package/dist/table/column-header-cell/table-column-header-cell.svelte +281 -0
  107. package/dist/table/column-header-cell/table-column-header-cell.svelte.d.ts +9 -0
  108. package/dist/table/column-resizer/README.md +32 -0
  109. package/dist/table/column-resizer/table-column-resizer-freeze-layout-test.svelte +51 -0
  110. package/dist/table/column-resizer/table-column-resizer-freeze-layout-test.svelte.d.ts +3 -0
  111. package/dist/table/column-resizer/table-column-resizer-selection-column-test.svelte +83 -0
  112. package/dist/table/column-resizer/table-column-resizer-selection-column-test.svelte.d.ts +3 -0
  113. package/dist/table/column-resizer/table-column-resizer-test.svelte +75 -0
  114. package/dist/table/column-resizer/table-column-resizer-test.svelte.d.ts +3 -0
  115. package/dist/table/column-resizer/table-column-resizer.svelte +616 -0
  116. package/dist/table/column-resizer/table-column-resizer.svelte.d.ts +11 -0
  117. package/dist/table/empty-state/README.md +25 -0
  118. package/dist/table/empty-state/table-empty-state.svelte +38 -0
  119. package/dist/table/empty-state/table-empty-state.svelte.d.ts +8 -0
  120. package/dist/table/footer/README.md +24 -0
  121. package/dist/table/footer/table-footer.svelte +19 -0
  122. package/dist/table/footer/table-footer.svelte.d.ts +9 -0
  123. package/dist/table/header/README.md +24 -0
  124. package/dist/table/header/table-header.svelte +19 -0
  125. package/dist/table/header/table-header.svelte.d.ts +9 -0
  126. package/dist/table/index.d.ts +16 -0
  127. package/dist/table/index.js +16 -0
  128. package/dist/table/index.parts.d.ts +12 -0
  129. package/dist/table/index.parts.js +12 -0
  130. package/dist/table/root/README.md +56 -0
  131. package/dist/table/root/context.d.ts +198 -0
  132. package/dist/table/root/context.js +1426 -0
  133. package/dist/table/root/table-reorder-test.svelte +64 -0
  134. package/dist/table/root/table-reorder-test.svelte.d.ts +3 -0
  135. package/dist/table/root/table-root.svelte +410 -0
  136. package/dist/table/root/table-root.svelte.d.ts +29 -0
  137. package/dist/table/root/table-test.svelte +165 -0
  138. package/dist/table/root/table-test.svelte.d.ts +25 -0
  139. package/dist/table/row/README.md +27 -0
  140. package/dist/table/row/table-row.svelte +321 -0
  141. package/dist/table/row/table-row.svelte.d.ts +13 -0
  142. package/dist/timepicker/IMPLEMENTATION_PLAN.md +254 -0
  143. package/dist/timepicker/README.md +97 -0
  144. package/dist/timepicker/TODO.md +86 -0
  145. package/dist/timepicker/clock/README.md +14 -0
  146. package/dist/timepicker/clock/time-picker-clock-test.svelte +45 -0
  147. package/dist/timepicker/clock/time-picker-clock-test.svelte.d.ts +11 -0
  148. package/dist/timepicker/clock/time-picker-clock.svelte +65 -0
  149. package/dist/timepicker/clock/time-picker-clock.svelte.d.ts +10 -0
  150. package/dist/timepicker/index.d.ts +14 -0
  151. package/dist/timepicker/index.js +14 -0
  152. package/dist/timepicker/index.parts.d.ts +8 -0
  153. package/dist/timepicker/index.parts.js +8 -0
  154. package/dist/timepicker/input/README.md +15 -0
  155. package/dist/timepicker/input/time-picker-input-forwarding-test.svelte +40 -0
  156. package/dist/timepicker/input/time-picker-input-forwarding-test.svelte.d.ts +3 -0
  157. package/dist/timepicker/input/time-picker-input.svelte +109 -0
  158. package/dist/timepicker/input/time-picker-input.svelte.d.ts +11 -0
  159. package/dist/timepicker/internal/strict-props.d.ts +4 -0
  160. package/dist/timepicker/internal/strict-props.js +51 -0
  161. package/dist/timepicker/popover/README.md +20 -0
  162. package/dist/timepicker/popover/time-picker-popover-unsafe-props-test.svelte +22 -0
  163. package/dist/timepicker/popover/time-picker-popover-unsafe-props-test.svelte.d.ts +3 -0
  164. package/dist/timepicker/popover/time-picker-popover.svelte +89 -0
  165. package/dist/timepicker/popover/time-picker-popover.svelte.d.ts +7 -0
  166. package/dist/timepicker/root/README.md +42 -0
  167. package/dist/timepicker/root/context.d.ts +51 -0
  168. package/dist/timepicker/root/context.js +15 -0
  169. package/dist/timepicker/root/time-picker-12h-test.svelte +22 -0
  170. package/dist/timepicker/root/time-picker-12h-test.svelte.d.ts +3 -0
  171. package/dist/timepicker/root/time-picker-bindable-test.svelte +25 -0
  172. package/dist/timepicker/root/time-picker-bindable-test.svelte.d.ts +3 -0
  173. package/dist/timepicker/root/time-picker-empty-test.svelte +20 -0
  174. package/dist/timepicker/root/time-picker-empty-test.svelte.d.ts +3 -0
  175. package/dist/timepicker/root/time-picker-root.svelte +625 -0
  176. package/dist/timepicker/root/time-picker-root.svelte.d.ts +28 -0
  177. package/dist/timepicker/root/time-picker-test.svelte +72 -0
  178. package/dist/timepicker/root/time-picker-test.svelte.d.ts +15 -0
  179. package/dist/timepicker/root/time-utils.d.ts +1 -0
  180. package/dist/timepicker/root/time-utils.js +3 -0
  181. package/dist/timepicker/segment/README.md +14 -0
  182. package/dist/timepicker/segment/time-picker-segment.svelte +365 -0
  183. package/dist/timepicker/segment/time-picker-segment.svelte.d.ts +9 -0
  184. package/dist/timepicker/trigger/README.md +14 -0
  185. package/dist/timepicker/trigger/time-picker-trigger-forwarding-test.svelte +35 -0
  186. package/dist/timepicker/trigger/time-picker-trigger-forwarding-test.svelte.d.ts +3 -0
  187. package/dist/timepicker/trigger/time-picker-trigger.svelte +122 -0
  188. package/dist/timepicker/trigger/time-picker-trigger.svelte.d.ts +9 -0
  189. 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. |