@chromvoid/uikit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +8 -0
- package/README.md +96 -0
- package/dist/components/cv-accordion-item.d.ts +69 -0
- package/dist/components/cv-accordion-item.js +176 -0
- package/dist/components/cv-accordion.d.ts +79 -0
- package/dist/components/cv-accordion.js +310 -0
- package/dist/components/cv-alert-dialog.d.ts +86 -0
- package/dist/components/cv-alert-dialog.js +393 -0
- package/dist/components/cv-alert.d.ts +48 -0
- package/dist/components/cv-alert.js +156 -0
- package/dist/components/cv-badge.d.ts +56 -0
- package/dist/components/cv-badge.js +280 -0
- package/dist/components/cv-breadcrumb-item.d.ts +35 -0
- package/dist/components/cv-breadcrumb-item.js +64 -0
- package/dist/components/cv-breadcrumb.d.ts +39 -0
- package/dist/components/cv-breadcrumb.js +160 -0
- package/dist/components/cv-button.d.ts +83 -0
- package/dist/components/cv-button.js +541 -0
- package/dist/components/cv-callout.d.ts +32 -0
- package/dist/components/cv-callout.js +221 -0
- package/dist/components/cv-card.d.ts +48 -0
- package/dist/components/cv-card.js +269 -0
- package/dist/components/cv-carousel-slide.d.ts +25 -0
- package/dist/components/cv-carousel-slide.js +51 -0
- package/dist/components/cv-carousel.d.ts +96 -0
- package/dist/components/cv-carousel.js +457 -0
- package/dist/components/cv-checkbox.d.ts +84 -0
- package/dist/components/cv-checkbox.js +274 -0
- package/dist/components/cv-combobox-group.d.ts +15 -0
- package/dist/components/cv-combobox-group.js +34 -0
- package/dist/components/cv-combobox-option.d.ts +30 -0
- package/dist/components/cv-combobox-option.js +66 -0
- package/dist/components/cv-combobox.d.ts +135 -0
- package/dist/components/cv-combobox.js +834 -0
- package/dist/components/cv-command-item.d.ts +30 -0
- package/dist/components/cv-command-item.js +68 -0
- package/dist/components/cv-command-palette.d.ts +105 -0
- package/dist/components/cv-command-palette.js +578 -0
- package/dist/components/cv-context-menu.d.ts +98 -0
- package/dist/components/cv-context-menu.js +515 -0
- package/dist/components/cv-copy-button.d.ts +61 -0
- package/dist/components/cv-copy-button.js +318 -0
- package/dist/components/cv-date-picker.d.ts +161 -0
- package/dist/components/cv-date-picker.js +803 -0
- package/dist/components/cv-dialog.d.ts +89 -0
- package/dist/components/cv-dialog.js +459 -0
- package/dist/components/cv-disclosure.d.ts +57 -0
- package/dist/components/cv-disclosure.js +241 -0
- package/dist/components/cv-drawer.d.ts +102 -0
- package/dist/components/cv-drawer.js +595 -0
- package/dist/components/cv-feed-article.d.ts +26 -0
- package/dist/components/cv-feed-article.js +52 -0
- package/dist/components/cv-feed.d.ts +62 -0
- package/dist/components/cv-feed.js +310 -0
- package/dist/components/cv-grid-cell.d.ts +30 -0
- package/dist/components/cv-grid-cell.js +57 -0
- package/dist/components/cv-grid-column.d.ts +30 -0
- package/dist/components/cv-grid-column.js +43 -0
- package/dist/components/cv-grid-row.d.ts +30 -0
- package/dist/components/cv-grid-row.js +42 -0
- package/dist/components/cv-grid.d.ts +119 -0
- package/dist/components/cv-grid.js +567 -0
- package/dist/components/cv-icon.d.ts +57 -0
- package/dist/components/cv-icon.js +352 -0
- package/dist/components/cv-input.d.ts +127 -0
- package/dist/components/cv-input.js +482 -0
- package/dist/components/cv-landmark.d.ts +32 -0
- package/dist/components/cv-landmark.js +62 -0
- package/dist/components/cv-link.d.ts +22 -0
- package/dist/components/cv-link.js +99 -0
- package/dist/components/cv-listbox-group.d.ts +15 -0
- package/dist/components/cv-listbox-group.js +42 -0
- package/dist/components/cv-listbox.d.ts +81 -0
- package/dist/components/cv-listbox.js +388 -0
- package/dist/components/cv-menu-button.d.ts +118 -0
- package/dist/components/cv-menu-button.js +822 -0
- package/dist/components/cv-menu-group.d.ts +20 -0
- package/dist/components/cv-menu-group.js +48 -0
- package/dist/components/cv-menu-item.d.ts +52 -0
- package/dist/components/cv-menu-item.js +105 -0
- package/dist/components/cv-menu.d.ts +62 -0
- package/dist/components/cv-menu.js +414 -0
- package/dist/components/cv-meter.d.ts +66 -0
- package/dist/components/cv-meter.js +154 -0
- package/dist/components/cv-number.d.ts +139 -0
- package/dist/components/cv-number.js +553 -0
- package/dist/components/cv-option.d.ts +30 -0
- package/dist/components/cv-option.js +84 -0
- package/dist/components/cv-popover.d.ts +87 -0
- package/dist/components/cv-popover.js +373 -0
- package/dist/components/cv-progress-ring.d.ts +45 -0
- package/dist/components/cv-progress-ring.js +169 -0
- package/dist/components/cv-progress.d.ts +45 -0
- package/dist/components/cv-progress.js +148 -0
- package/dist/components/cv-radio-group.d.ts +79 -0
- package/dist/components/cv-radio-group.js +398 -0
- package/dist/components/cv-radio.d.ts +36 -0
- package/dist/components/cv-radio.js +123 -0
- package/dist/components/cv-select-group.d.ts +15 -0
- package/dist/components/cv-select-group.js +44 -0
- package/dist/components/cv-select-option.d.ts +30 -0
- package/dist/components/cv-select-option.js +66 -0
- package/dist/components/cv-select.d.ts +128 -0
- package/dist/components/cv-select.js +666 -0
- package/dist/components/cv-sidebar-item.d.ts +26 -0
- package/dist/components/cv-sidebar-item.js +142 -0
- package/dist/components/cv-sidebar.d.ts +171 -0
- package/dist/components/cv-sidebar.js +767 -0
- package/dist/components/cv-slider-multi-thumb.d.ts +73 -0
- package/dist/components/cv-slider-multi-thumb.js +374 -0
- package/dist/components/cv-slider.d.ts +84 -0
- package/dist/components/cv-slider.js +328 -0
- package/dist/components/cv-spinbutton.d.ts +121 -0
- package/dist/components/cv-spinbutton.js +486 -0
- package/dist/components/cv-spinner.d.ts +18 -0
- package/dist/components/cv-spinner.js +95 -0
- package/dist/components/cv-switch.d.ts +81 -0
- package/dist/components/cv-switch.js +285 -0
- package/dist/components/cv-tab-panel.d.ts +20 -0
- package/dist/components/cv-tab-panel.js +37 -0
- package/dist/components/cv-tab.d.ts +40 -0
- package/dist/components/cv-tab.js +132 -0
- package/dist/components/cv-table-cell.d.ts +31 -0
- package/dist/components/cv-table-cell.js +49 -0
- package/dist/components/cv-table-column.d.ts +37 -0
- package/dist/components/cv-table-column.js +63 -0
- package/dist/components/cv-table-row.d.ts +30 -0
- package/dist/components/cv-table-row.js +45 -0
- package/dist/components/cv-table.d.ts +147 -0
- package/dist/components/cv-table.js +607 -0
- package/dist/components/cv-tabs.d.ts +70 -0
- package/dist/components/cv-tabs.js +524 -0
- package/dist/components/cv-textarea.d.ts +108 -0
- package/dist/components/cv-textarea.js +328 -0
- package/dist/components/cv-toast-region.d.ts +39 -0
- package/dist/components/cv-toast-region.js +162 -0
- package/dist/components/cv-toast.d.ts +67 -0
- package/dist/components/cv-toast.js +315 -0
- package/dist/components/cv-toolbar-item.d.ts +25 -0
- package/dist/components/cv-toolbar-item.js +72 -0
- package/dist/components/cv-toolbar-separator.d.ts +25 -0
- package/dist/components/cv-toolbar-separator.js +45 -0
- package/dist/components/cv-toolbar.d.ts +63 -0
- package/dist/components/cv-toolbar.js +295 -0
- package/dist/components/cv-tooltip.d.ts +83 -0
- package/dist/components/cv-tooltip.js +455 -0
- package/dist/components/cv-treegrid-cell.d.ts +30 -0
- package/dist/components/cv-treegrid-cell.js +57 -0
- package/dist/components/cv-treegrid-column.d.ts +37 -0
- package/dist/components/cv-treegrid-column.js +53 -0
- package/dist/components/cv-treegrid-row.d.ts +55 -0
- package/dist/components/cv-treegrid-row.js +90 -0
- package/dist/components/cv-treegrid.d.ts +96 -0
- package/dist/components/cv-treegrid.js +632 -0
- package/dist/components/cv-treeitem.d.ts +58 -0
- package/dist/components/cv-treeitem.js +144 -0
- package/dist/components/cv-treeview.d.ts +70 -0
- package/dist/components/cv-treeview.js +396 -0
- package/dist/components/cv-window-splitter.d.ts +79 -0
- package/dist/components/cv-window-splitter.js +316 -0
- package/dist/components/index.d.ts +94 -0
- package/dist/components/index.js +79 -0
- package/dist/dialog/create-dialog-controller.d.ts +31 -0
- package/dist/dialog/create-dialog-controller.js +320 -0
- package/dist/dialog/index.d.ts +2 -0
- package/dist/dialog/index.js +1 -0
- package/dist/form-associated/FormAssociatedReatomElement.d.ts +25 -0
- package/dist/form-associated/FormAssociatedReatomElement.js +70 -0
- package/dist/form-associated/withFormAssociated.d.ts +5 -0
- package/dist/form-associated/withFormAssociated.js +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +9 -0
- package/dist/reatom-lit/ReatomLitElement.d.ts +27 -0
- package/dist/reatom-lit/ReatomLitElement.js +118 -0
- package/dist/reatom-lit/html.d.ts +4 -0
- package/dist/reatom-lit/html.js +10 -0
- package/dist/reatom-lit/index.d.ts +4 -0
- package/dist/reatom-lit/index.js +4 -0
- package/dist/reatom-lit/watch.d.ts +15 -0
- package/dist/reatom-lit/watch.js +40 -0
- package/dist/reatom-lit/withReatomElement.d.ts +4 -0
- package/dist/reatom-lit/withReatomElement.js +57 -0
- package/dist/register.d.ts +1 -0
- package/dist/register.js +84 -0
- package/dist/styles/component-styles.d.ts +4 -0
- package/dist/styles/component-styles.js +78 -0
- package/dist/theme/cv-theme-provider.d.ts +32 -0
- package/dist/theme/cv-theme-provider.js +110 -0
- package/dist/theme/index.d.ts +4 -0
- package/dist/theme/index.js +2 -0
- package/dist/theme/theme-engine.d.ts +4 -0
- package/dist/theme/theme-engine.js +67 -0
- package/dist/theme/tokens.css +265 -0
- package/dist/theme/types.d.ts +7 -0
- package/dist/theme/types.js +1 -0
- package/dist/toast/create-toast-controller.d.ts +12 -0
- package/dist/toast/create-toast-controller.js +12 -0
- package/dist/toast/index.d.ts +2 -0
- package/dist/toast/index.js +1 -0
- package/package.json +146 -0
- package/specs/_template.md +110 -0
- package/specs/components/accordion.md +207 -0
- package/specs/components/alert.md +83 -0
- package/specs/components/badge.md +183 -0
- package/specs/components/breadcrumb.md +152 -0
- package/specs/components/button.md +227 -0
- package/specs/components/callout.md +153 -0
- package/specs/components/card.md +192 -0
- package/specs/components/carousel.md +232 -0
- package/specs/components/checkbox.md +141 -0
- package/specs/components/combobox.md +427 -0
- package/specs/components/context-menu.md +375 -0
- package/specs/components/copy-button.md +236 -0
- package/specs/components/date-picker.md +290 -0
- package/specs/components/dialog.md +184 -0
- package/specs/components/disclosure.md +151 -0
- package/specs/components/drawer.md +216 -0
- package/specs/components/feed.md +266 -0
- package/specs/components/grid.md +423 -0
- package/specs/components/input.md +237 -0
- package/specs/components/landmark.md +92 -0
- package/specs/components/link.md +117 -0
- package/specs/components/listbox.md +327 -0
- package/specs/components/menu.md +508 -0
- package/specs/components/meter.md +148 -0
- package/specs/components/number.md +268 -0
- package/specs/components/option.md +167 -0
- package/specs/components/popover.md +207 -0
- package/specs/components/progress-ring.md +134 -0
- package/specs/components/progress.md +110 -0
- package/specs/components/radio.md +208 -0
- package/specs/components/select.md +305 -0
- package/specs/components/sidebar.md +204 -0
- package/specs/components/spinbutton.md +157 -0
- package/specs/components/spinner.md +83 -0
- package/specs/components/switch.md +145 -0
- package/specs/components/table.md +372 -0
- package/specs/components/tabs.md +242 -0
- package/specs/components/textarea.md +166 -0
- package/specs/components/theme.md +364 -0
- package/specs/components/toast.md +198 -0
- package/specs/components/toolbar.md +258 -0
- package/specs/components/tooltip.md +152 -0
- package/specs/components/treegrid.md +363 -0
- package/specs/components/treeview.md +263 -0
- package/specs/components/window-splitter.md +225 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
# cv-grid
|
|
2
|
+
|
|
3
|
+
Interactive data grid component with keyboard navigation, cell selection, and accessible tabular data display following the WAI-ARIA Grid pattern.
|
|
4
|
+
|
|
5
|
+
**Headless:** [`createGrid`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/grid.md)
|
|
6
|
+
|
|
7
|
+
## Cross-Spec Consistency
|
|
8
|
+
|
|
9
|
+
This document is the UIKit surface contract for Grid.
|
|
10
|
+
|
|
11
|
+
- Headless `createGrid` is the source of truth for state, transitions, and invariants.
|
|
12
|
+
- UIKit mirrors headless contracts through DOM attributes and events.
|
|
13
|
+
- Any intentional divergence between UIKit and headless MUST be documented in both specs.
|
|
14
|
+
|
|
15
|
+
## Anatomy
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
<cv-grid> (host)
|
|
19
|
+
└── <div part="base" role="grid">
|
|
20
|
+
├── <div role="rowgroup" part="head">
|
|
21
|
+
│ └── <div role="row" part="head-row">
|
|
22
|
+
│ └── <slot name="columns"> ← cv-grid-column elements
|
|
23
|
+
├── <div role="rowgroup" part="body">
|
|
24
|
+
│ └── <slot name="rows"> ← cv-grid-row elements
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Attributes
|
|
28
|
+
|
|
29
|
+
| Attribute | Type | Default | Description |
|
|
30
|
+
| ------------------------- | ------- | ------------------- | -------------------------------------------------------------------------------------------- |
|
|
31
|
+
| `value` | String | `""` | Active cell key in `"rowId::colId"` format. Reflects the currently focused cell. |
|
|
32
|
+
| `selection-mode` | String | `"single"` | Selection mode: `"single"` \| `"multiple"` |
|
|
33
|
+
| `focus-strategy` | String | `"roving-tabindex"` | Focus management strategy: `"roving-tabindex"` \| `"aria-activedescendant"` |
|
|
34
|
+
| `selection-follows-focus` | Boolean | `false` | Auto-select cell on focus move |
|
|
35
|
+
| `page-size` | Number | `10` | Rows per page for PageUp/PageDown navigation (minimum 1) |
|
|
36
|
+
| `readonly` | Boolean | `false` | Marks all cells as `aria-readonly` |
|
|
37
|
+
| `aria-label` | String | `""` | Accessible label for the grid root. Falls back to `"Grid"` when no `aria-labelledby` is set. |
|
|
38
|
+
| `aria-labelledby` | String | `""` | `aria-labelledby` reference for the grid root |
|
|
39
|
+
| `total-row-count` | Number | `0` | Logical row count for virtualization. When `> 0`, overrides `aria-rowcount`. |
|
|
40
|
+
| `total-column-count` | Number | `0` | Logical column count for virtualization. When `> 0`, overrides `aria-colcount`. |
|
|
41
|
+
|
|
42
|
+
**JS-only property:**
|
|
43
|
+
|
|
44
|
+
| Property | Type | Default | Description |
|
|
45
|
+
| ---------------- | ---------- | ------- | ------------------------------------------------------ |
|
|
46
|
+
| `selectedValues` | `string[]` | `[]` | Array of selected cell keys in `"rowId::colId"` format |
|
|
47
|
+
|
|
48
|
+
## Slots
|
|
49
|
+
|
|
50
|
+
| Slot | Description |
|
|
51
|
+
| --------- | ------------------------------------------------------------------------- |
|
|
52
|
+
| `columns` | One or more `<cv-grid-column>` elements defining the column headers |
|
|
53
|
+
| `rows` | One or more `<cv-grid-row>` elements containing `<cv-grid-cell>` children |
|
|
54
|
+
|
|
55
|
+
## CSS Parts
|
|
56
|
+
|
|
57
|
+
| Part | Element | Description |
|
|
58
|
+
| ---------- | ------- | ---------------------------------------------------- |
|
|
59
|
+
| `base` | `<div>` | Root grid container with `role="grid"`, table layout |
|
|
60
|
+
| `head` | `<div>` | Column header row group (`role="rowgroup"`) |
|
|
61
|
+
| `head-row` | `<div>` | Row wrapping the column header slots (`role="row"`) |
|
|
62
|
+
| `body` | `<div>` | Data row group (`role="rowgroup"`) |
|
|
63
|
+
|
|
64
|
+
## CSS Custom Properties
|
|
65
|
+
|
|
66
|
+
The grid component does not expose dedicated `--cv-grid-*` custom properties on the host. Styling is controlled through theme tokens consumed as fallback values:
|
|
67
|
+
|
|
68
|
+
| Theme Property | Default | Description |
|
|
69
|
+
| -------------------- | --------- | --------------------------------- |
|
|
70
|
+
| `--cv-color-border` | `#2a3245` | Border color for the grid outline |
|
|
71
|
+
| `--cv-color-surface` | `#141923` | Background color of the grid base |
|
|
72
|
+
| `--cv-radius-md` | `10px` | Border radius of the grid base |
|
|
73
|
+
|
|
74
|
+
## Visual States
|
|
75
|
+
|
|
76
|
+
| Host selector | Description |
|
|
77
|
+
| ------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
78
|
+
| `:host` | Block display |
|
|
79
|
+
| `:host([selection-mode="multiple"])` | Multiple cell selection enabled; grid root reflects `aria-multiselectable="true"` |
|
|
80
|
+
| `:host([focus-strategy="aria-activedescendant"])` | Grid root gets `tabindex="0"` and `aria-activedescendant`; cells all get `tabindex="-1"` |
|
|
81
|
+
| `:host([readonly])` | All cells reflect `aria-readonly="true"` |
|
|
82
|
+
|
|
83
|
+
## ARIA Contract
|
|
84
|
+
|
|
85
|
+
- Grid root role is `grid`
|
|
86
|
+
- Row group elements use `role="rowgroup"`
|
|
87
|
+
- Head row uses `role="row"`
|
|
88
|
+
- Column headers use `role="columnheader"` (set by UIKit on `cv-grid-column`)
|
|
89
|
+
- Data rows use `role="row"` (from headless `getRowProps`)
|
|
90
|
+
- Data cells use `role="gridcell"` (from headless `getCellProps`)
|
|
91
|
+
- Focus strategies:
|
|
92
|
+
- `roving-tabindex` (default) -- active cell gets `tabindex="0"`, all others `tabindex="-1"`, grid root gets `tabindex="-1"`
|
|
93
|
+
- `aria-activedescendant` -- grid root gets `tabindex="0"` and `aria-activedescendant` pointing to active cell DOM id; all cells get `tabindex="-1"`
|
|
94
|
+
- Required attributes on root: `aria-label` or `aria-labelledby`, `aria-multiselectable`, `aria-colcount`, `aria-rowcount`
|
|
95
|
+
- Required attributes on rows: `aria-rowindex`
|
|
96
|
+
- Required attributes on cells: `aria-colindex`, `aria-selected`, `tabindex`, `data-active`
|
|
97
|
+
- Conditional cell attributes: `aria-readonly` (when `readonly`), `aria-disabled` (when cell is disabled)
|
|
98
|
+
|
|
99
|
+
All ARIA attributes are derived from headless contracts (`getGridProps`, `getRowProps`, `getCellProps`). UIKit does not compute ARIA state independently.
|
|
100
|
+
|
|
101
|
+
## Events
|
|
102
|
+
|
|
103
|
+
| Event | Detail | Description |
|
|
104
|
+
| ----------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
105
|
+
| `cv-input` | `{value: string \| null, activeCell: GridCellId \| null, selectedValues: string[]}` | Fires when active cell or selection changes due to interaction |
|
|
106
|
+
| `cv-change` | `{value: string \| null, activeCell: GridCellId \| null, selectedValues: string[]}` | Fires when active cell or selection changes due to interaction |
|
|
107
|
+
|
|
108
|
+
**Event detail shape:**
|
|
109
|
+
|
|
110
|
+
| Field | Type | Description |
|
|
111
|
+
| ---------------- | -------------------- | ------------------------------------------------- |
|
|
112
|
+
| `value` | `string \| null` | Active cell key (`"rowId::colId"`) or `null` |
|
|
113
|
+
| `activeCell` | `GridCellId \| null` | Active cell object `{rowId, colId}` or `null` |
|
|
114
|
+
| `selectedValues` | `string[]` | All selected cell keys in `"rowId::colId"` format |
|
|
115
|
+
|
|
116
|
+
Both `cv-input` and `cv-change` fire together whenever active cell or selection state changes as a result of keyboard navigation, click interaction, or programmatic `value`/`selectedValues` updates that alter model state.
|
|
117
|
+
|
|
118
|
+
## Reactive State Mapping
|
|
119
|
+
|
|
120
|
+
`cv-grid` is a visual adapter over headless `createGrid`.
|
|
121
|
+
|
|
122
|
+
### Attribute to Headless (UIKit -> Headless)
|
|
123
|
+
|
|
124
|
+
| UIKit Property | Direction | Headless Binding |
|
|
125
|
+
| ------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
126
|
+
| `value` | attr -> action | Parsed to `GridCellId`, calls `actions.setActiveCell(cell)` if valid and different from current |
|
|
127
|
+
| `selectedValues` | prop -> action | Parsed to cell ids, calls `actions.selectCell()` (single) or `actions.toggleCellSelection()` (multiple) after clearing |
|
|
128
|
+
| `selection-mode` | attr -> option | Passed as `selectionMode` in `createGrid(options)` -- triggers model rebuild |
|
|
129
|
+
| `focus-strategy` | attr -> option | Passed as `focusStrategy` in `createGrid(options)` -- triggers model rebuild |
|
|
130
|
+
| `selection-follows-focus` | attr -> option | Passed as `selectionFollowsFocus` in `createGrid(options)` -- triggers model rebuild |
|
|
131
|
+
| `page-size` | attr -> option | Passed as `pageSize` in `createGrid(options)` -- triggers model rebuild |
|
|
132
|
+
| `readonly` | attr -> option | Passed as `isReadOnly` in `createGrid(options)` -- triggers model rebuild |
|
|
133
|
+
| `aria-label` | attr -> option | Passed as `ariaLabel` in `createGrid(options)` -- triggers model rebuild |
|
|
134
|
+
| `aria-labelledby` | attr -> option | Passed as `ariaLabelledBy` in `createGrid(options)` -- triggers model rebuild |
|
|
135
|
+
| `total-row-count` | attr -> option | Passed as `totalRowCount` in `createGrid(options)` -- triggers model rebuild |
|
|
136
|
+
| `total-column-count` | attr -> option | Passed as `totalColumnCount` in `createGrid(options)` -- triggers model rebuild |
|
|
137
|
+
|
|
138
|
+
### Headless to DOM (Headless -> UIKit)
|
|
139
|
+
|
|
140
|
+
| Headless State | Direction | DOM Reflection |
|
|
141
|
+
| -------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
142
|
+
| `state.activeCellId()` | state -> attr | `[value]` host attribute as `"rowId::colId"` |
|
|
143
|
+
| `state.selectedCellIds()` | state -> prop | `selectedValues` JS property as `string[]` |
|
|
144
|
+
| `contracts.getGridProps()` | state -> render | Spread onto `[part="base"]`: `role`, `tabindex`, `aria-label`, `aria-labelledby`, `aria-multiselectable`, `aria-colcount`, `aria-rowcount`, `aria-activedescendant` |
|
|
145
|
+
| `contracts.getRowProps(rowId)` | state -> render | Spread onto `cv-grid-row` elements: `id`, `role`, `aria-rowindex` |
|
|
146
|
+
| `contracts.getCellProps(rowId, colId)` | state -> render | Spread onto `cv-grid-cell` elements: `id`, `role`, `tabindex`, `aria-colindex`, `aria-selected`, `aria-readonly`, `aria-disabled`, `data-active` |
|
|
147
|
+
|
|
148
|
+
### UIKit-Only Concerns (NOT in headless)
|
|
149
|
+
|
|
150
|
+
- Column header ARIA attributes (`role="columnheader"`, `aria-colindex`, `aria-disabled`) -- applied by UIKit directly to `cv-grid-column` elements
|
|
151
|
+
- Slot-based model rebuilding: UIKit scans `cv-grid-column`, `cv-grid-row`, and `cv-grid-cell` from the light DOM and rebuilds the headless model on slot changes and direct child mutations (via `MutationObserver` on the host `childList`)
|
|
152
|
+
- DOM focus management: when `activeCellId` changes in `roving-tabindex` mode, UIKit calls `.focus()` on the corresponding `cv-grid-cell`
|
|
153
|
+
- Click handling with modifier keys: Ctrl/Meta+click toggles selection in multiple mode; plain click selects
|
|
154
|
+
- `cv-grid-row-slotchange` internal event: `cv-grid-row` dispatches this when its cells change, triggering model rebuild
|
|
155
|
+
- Auto-generated fallback values for column (`column-{n}`) and row (`row-{n}`) identifiers when `value` is empty
|
|
156
|
+
- Cell validity filtering: cells referencing non-existent columns are hidden
|
|
157
|
+
- `preventDefault()` on grid-handled keyboard events (arrows, Home, End, PageUp, PageDown, Enter, Space)
|
|
158
|
+
|
|
159
|
+
## Behavioral Contract
|
|
160
|
+
|
|
161
|
+
### Model Rebuild
|
|
162
|
+
|
|
163
|
+
The headless model is rebuilt when:
|
|
164
|
+
|
|
165
|
+
- The component connects to the DOM
|
|
166
|
+
- Any option attribute changes (`selection-mode`, `focus-strategy`, `selection-follows-focus`, `page-size`, `readonly`, `aria-label`, `aria-labelledby`, `total-row-count`, `total-column-count`)
|
|
167
|
+
- Slot content changes (columns added/removed, rows added/removed, cells within rows changed)
|
|
168
|
+
|
|
169
|
+
During rebuild with `preserveState=true`, the current active cell and selected cells are restored if they remain valid in the new model.
|
|
170
|
+
|
|
171
|
+
### Click Interaction
|
|
172
|
+
|
|
173
|
+
- Plain click on a cell: sets active cell and selects it
|
|
174
|
+
- Ctrl/Meta+click on a cell (multiple mode): sets active cell and toggles selection for that cell
|
|
175
|
+
- Disabled cells ignore click interaction
|
|
176
|
+
- After click, DOM focus moves to the active cell (roving-tabindex mode)
|
|
177
|
+
|
|
178
|
+
### Keyboard Navigation
|
|
179
|
+
|
|
180
|
+
All keyboard interaction is delegated to headless `actions.handleKeyDown(event)`. UIKit calls `preventDefault()` on handled keys and manages DOM focus after state changes.
|
|
181
|
+
|
|
182
|
+
| Key | Modifier | Action |
|
|
183
|
+
| ------------ | ----------- | -------------------------------------------------------------------- |
|
|
184
|
+
| `ArrowUp` | -- | Move active cell up |
|
|
185
|
+
| `ArrowDown` | -- | Move active cell down |
|
|
186
|
+
| `ArrowLeft` | -- | Move active cell left |
|
|
187
|
+
| `ArrowRight` | -- | Move active cell right |
|
|
188
|
+
| `Home` | -- | Move to first cell in current row |
|
|
189
|
+
| `End` | -- | Move to last cell in current row |
|
|
190
|
+
| `Home` | Ctrl / Meta | Move to first cell in grid |
|
|
191
|
+
| `End` | Ctrl / Meta | Move to last cell in grid |
|
|
192
|
+
| `PageUp` | -- | Move up by `page-size` rows |
|
|
193
|
+
| `PageDown` | -- | Move down by `page-size` rows |
|
|
194
|
+
| `Enter` | -- | Move active cell down |
|
|
195
|
+
| `Space` | -- | Select active cell (single mode) or toggle selection (multiple mode) |
|
|
196
|
+
|
|
197
|
+
## Out of Scope
|
|
198
|
+
|
|
199
|
+
The following features are explicitly out of scope for the current implementation:
|
|
200
|
+
|
|
201
|
+
- **Cell editing** -- inline input mode for cell content
|
|
202
|
+
- **Column reordering** -- drag-and-drop or programmatic column order changes
|
|
203
|
+
- **Column resizing** -- adjustable column widths via drag handles
|
|
204
|
+
- **Context menus** -- right-click or long-press context menu integration
|
|
205
|
+
|
|
206
|
+
## Usage
|
|
207
|
+
|
|
208
|
+
```html
|
|
209
|
+
<!-- Basic grid -->
|
|
210
|
+
<cv-grid aria-label="Users">
|
|
211
|
+
<cv-grid-column value="name" label="Name">Name</cv-grid-column>
|
|
212
|
+
<cv-grid-column value="status" label="Status">Status</cv-grid-column>
|
|
213
|
+
<cv-grid-column value="role" label="Role">Role</cv-grid-column>
|
|
214
|
+
|
|
215
|
+
<cv-grid-row value="r1">
|
|
216
|
+
<cv-grid-cell column="name">Alice</cv-grid-cell>
|
|
217
|
+
<cv-grid-cell column="status">Active</cv-grid-cell>
|
|
218
|
+
<cv-grid-cell column="role">Admin</cv-grid-cell>
|
|
219
|
+
</cv-grid-row>
|
|
220
|
+
<cv-grid-row value="r2">
|
|
221
|
+
<cv-grid-cell column="name">Bob</cv-grid-cell>
|
|
222
|
+
<cv-grid-cell column="status">Inactive</cv-grid-cell>
|
|
223
|
+
<cv-grid-cell column="role">Editor</cv-grid-cell>
|
|
224
|
+
</cv-grid-row>
|
|
225
|
+
</cv-grid>
|
|
226
|
+
|
|
227
|
+
<!-- Multiple selection -->
|
|
228
|
+
<cv-grid aria-label="Tasks" selection-mode="multiple">
|
|
229
|
+
<cv-grid-column value="task">Task</cv-grid-column>
|
|
230
|
+
<cv-grid-column value="priority">Priority</cv-grid-column>
|
|
231
|
+
|
|
232
|
+
<cv-grid-row value="t1">
|
|
233
|
+
<cv-grid-cell column="task">Review PR</cv-grid-cell>
|
|
234
|
+
<cv-grid-cell column="priority">High</cv-grid-cell>
|
|
235
|
+
</cv-grid-row>
|
|
236
|
+
<cv-grid-row value="t2">
|
|
237
|
+
<cv-grid-cell column="task">Write docs</cv-grid-cell>
|
|
238
|
+
<cv-grid-cell column="priority">Low</cv-grid-cell>
|
|
239
|
+
</cv-grid-row>
|
|
240
|
+
</cv-grid>
|
|
241
|
+
|
|
242
|
+
<!-- With disabled cells and rows -->
|
|
243
|
+
<cv-grid aria-label="Inventory" readonly>
|
|
244
|
+
<cv-grid-column value="item">Item</cv-grid-column>
|
|
245
|
+
<cv-grid-column value="qty">Quantity</cv-grid-column>
|
|
246
|
+
|
|
247
|
+
<cv-grid-row value="r1">
|
|
248
|
+
<cv-grid-cell column="item">Widget A</cv-grid-cell>
|
|
249
|
+
<cv-grid-cell column="qty">42</cv-grid-cell>
|
|
250
|
+
</cv-grid-row>
|
|
251
|
+
<cv-grid-row value="r2" disabled>
|
|
252
|
+
<cv-grid-cell column="item">Widget B</cv-grid-cell>
|
|
253
|
+
<cv-grid-cell column="qty">0</cv-grid-cell>
|
|
254
|
+
</cv-grid-row>
|
|
255
|
+
</cv-grid>
|
|
256
|
+
|
|
257
|
+
<!-- aria-activedescendant focus strategy -->
|
|
258
|
+
<cv-grid aria-label="Data" focus-strategy="aria-activedescendant">
|
|
259
|
+
<cv-grid-column value="col1">Column 1</cv-grid-column>
|
|
260
|
+
<cv-grid-column value="col2">Column 2</cv-grid-column>
|
|
261
|
+
|
|
262
|
+
<cv-grid-row value="r1">
|
|
263
|
+
<cv-grid-cell column="col1">A1</cv-grid-cell>
|
|
264
|
+
<cv-grid-cell column="col2">A2</cv-grid-cell>
|
|
265
|
+
</cv-grid-row>
|
|
266
|
+
</cv-grid>
|
|
267
|
+
|
|
268
|
+
<!-- Selection follows focus -->
|
|
269
|
+
<cv-grid aria-label="Files" selection-follows-focus>
|
|
270
|
+
<cv-grid-column value="name">Name</cv-grid-column>
|
|
271
|
+
<cv-grid-column value="size">Size</cv-grid-column>
|
|
272
|
+
|
|
273
|
+
<cv-grid-row value="f1">
|
|
274
|
+
<cv-grid-cell column="name">readme.md</cv-grid-cell>
|
|
275
|
+
<cv-grid-cell column="size">2KB</cv-grid-cell>
|
|
276
|
+
</cv-grid-row>
|
|
277
|
+
<cv-grid-row value="f2">
|
|
278
|
+
<cv-grid-cell column="name">index.ts</cv-grid-cell>
|
|
279
|
+
<cv-grid-cell column="size">4KB</cv-grid-cell>
|
|
280
|
+
</cv-grid-row>
|
|
281
|
+
</cv-grid>
|
|
282
|
+
|
|
283
|
+
<!-- Virtualized grid with total counts -->
|
|
284
|
+
<cv-grid aria-label="Log" total-row-count="10000" total-column-count="5" page-size="50">
|
|
285
|
+
<cv-grid-column value="ts">Timestamp</cv-grid-column>
|
|
286
|
+
<cv-grid-column value="msg">Message</cv-grid-column>
|
|
287
|
+
|
|
288
|
+
<cv-grid-row value="r1">
|
|
289
|
+
<cv-grid-cell column="ts">12:00:01</cv-grid-cell>
|
|
290
|
+
<cv-grid-cell column="msg">Server started</cv-grid-cell>
|
|
291
|
+
</cv-grid-row>
|
|
292
|
+
</cv-grid>
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Child Elements
|
|
296
|
+
|
|
297
|
+
### cv-grid-column
|
|
298
|
+
|
|
299
|
+
Column header definition. The parent `cv-grid` assigns `role="columnheader"`, `aria-colindex`, and `aria-disabled` attributes.
|
|
300
|
+
|
|
301
|
+
#### Anatomy
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
<cv-grid-column> (host)
|
|
305
|
+
└── <slot>{label fallback}</slot>
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
#### Attributes
|
|
309
|
+
|
|
310
|
+
| Attribute | Type | Default | Description |
|
|
311
|
+
| ---------- | ------- | ------- | ------------------------------------------------------------------------- |
|
|
312
|
+
| `value` | String | `""` | Unique column identifier. Auto-generated as `column-{n}` if empty. |
|
|
313
|
+
| `label` | String | `""` | Fallback label text rendered when no slotted content is provided |
|
|
314
|
+
| `index` | Number | `0` | Explicit 1-based `aria-colindex`. When `< 1`, positional index is used. |
|
|
315
|
+
| `disabled` | Boolean | `false` | Whether the column is disabled (all cells in this column become disabled) |
|
|
316
|
+
|
|
317
|
+
#### Slots
|
|
318
|
+
|
|
319
|
+
| Slot | Description |
|
|
320
|
+
| ----------- | ------------------------------------------------------------ |
|
|
321
|
+
| `(default)` | Column header content. Falls back to `label` attribute text. |
|
|
322
|
+
|
|
323
|
+
#### CSS Custom Properties (inherited from theme)
|
|
324
|
+
|
|
325
|
+
| Theme Property | Default | Description |
|
|
326
|
+
| -------------------- | --------- | --------------------------------------- |
|
|
327
|
+
| `--cv-space-2` | `8px` | Vertical padding |
|
|
328
|
+
| `--cv-space-3` | `12px` | Horizontal padding |
|
|
329
|
+
| `--cv-color-border` | `#2a3245` | Bottom border color |
|
|
330
|
+
| `--cv-color-text` | `#e8ecf6` | Text color |
|
|
331
|
+
| `--cv-color-surface` | `#141923` | Background color (mixed at 82% opacity) |
|
|
332
|
+
|
|
333
|
+
#### Visual States
|
|
334
|
+
|
|
335
|
+
| Host selector | Description |
|
|
336
|
+
| ------------------- | ----------------------------------------------------------------- |
|
|
337
|
+
| `:host` | Table-cell display, `font-weight: 600`, tinted surface background |
|
|
338
|
+
| `:host([disabled])` | Reduced opacity (`0.55`) |
|
|
339
|
+
|
|
340
|
+
### cv-grid-row
|
|
341
|
+
|
|
342
|
+
Data row container. The parent `cv-grid` assigns `id`, `role="row"`, and `aria-rowindex` from headless `getRowProps`.
|
|
343
|
+
|
|
344
|
+
#### Anatomy
|
|
345
|
+
|
|
346
|
+
```
|
|
347
|
+
<cv-grid-row> (host)
|
|
348
|
+
└── <slot> ← accepts cv-grid-cell children
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
#### Attributes
|
|
352
|
+
|
|
353
|
+
| Attribute | Type | Default | Description |
|
|
354
|
+
| ---------- | ------- | ------- | ----------------------------------------------------------------------- |
|
|
355
|
+
| `value` | String | `""` | Unique row identifier. Auto-generated as `row-{n}` if empty. |
|
|
356
|
+
| `index` | Number | `0` | Explicit 1-based `aria-rowindex`. When `< 1`, positional index is used. |
|
|
357
|
+
| `disabled` | Boolean | `false` | Whether the row is disabled (all cells in this row become disabled) |
|
|
358
|
+
|
|
359
|
+
#### Slots
|
|
360
|
+
|
|
361
|
+
| Slot | Description |
|
|
362
|
+
| ----------- | ------------------------------------- |
|
|
363
|
+
| `(default)` | One or more `<cv-grid-cell>` children |
|
|
364
|
+
|
|
365
|
+
#### Internal Events
|
|
366
|
+
|
|
367
|
+
| Event | Bubbles | Description |
|
|
368
|
+
| ------------------------ | -------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
369
|
+
| `cv-grid-row-slotchange` | Yes (composed) | Dispatched when slotted cell children change. The parent `cv-grid` listens for this to trigger model rebuild. |
|
|
370
|
+
|
|
371
|
+
#### Visual States
|
|
372
|
+
|
|
373
|
+
| Host selector | Description |
|
|
374
|
+
| ------------------- | ------------------------ |
|
|
375
|
+
| `:host` | Table-row display |
|
|
376
|
+
| `:host([disabled])` | Reduced opacity (`0.55`) |
|
|
377
|
+
|
|
378
|
+
### cv-grid-cell
|
|
379
|
+
|
|
380
|
+
Individual data cell within a grid row. The parent `cv-grid` manages all ARIA attributes on this element via headless contracts and direct event wiring.
|
|
381
|
+
|
|
382
|
+
#### Anatomy
|
|
383
|
+
|
|
384
|
+
```
|
|
385
|
+
<cv-grid-cell> (host)
|
|
386
|
+
└── <slot>
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
#### Attributes
|
|
390
|
+
|
|
391
|
+
| Attribute | Type | Default | Description |
|
|
392
|
+
| ---------- | ------- | ------- | -------------------------------------------------------------------------------------- |
|
|
393
|
+
| `column` | String | `""` | Column identifier this cell belongs to. Auto-assigned from positional column if empty. |
|
|
394
|
+
| `disabled` | Boolean | `false` | Whether the cell is individually disabled |
|
|
395
|
+
| `active` | Boolean | `false` | Whether the cell is the active (focused) cell. Managed by parent. |
|
|
396
|
+
| `selected` | Boolean | `false` | Whether the cell is selected. Managed by parent. |
|
|
397
|
+
|
|
398
|
+
#### Slots
|
|
399
|
+
|
|
400
|
+
| Slot | Description |
|
|
401
|
+
| ----------- | ------------ |
|
|
402
|
+
| `(default)` | Cell content |
|
|
403
|
+
|
|
404
|
+
#### CSS Custom Properties (inherited from theme)
|
|
405
|
+
|
|
406
|
+
| Theme Property | Default | Description |
|
|
407
|
+
| -------------------- | --------- | ------------------------------------------------------- |
|
|
408
|
+
| `--cv-space-2` | `8px` | Vertical padding |
|
|
409
|
+
| `--cv-space-3` | `12px` | Horizontal padding |
|
|
410
|
+
| `--cv-color-border` | `#2a3245` | Bottom border color (at 70% mix) |
|
|
411
|
+
| `--cv-color-text` | `#e8ecf6` | Text color |
|
|
412
|
+
| `--cv-color-primary` | `#65d7ff` | Active/selected background tint and focus outline color |
|
|
413
|
+
|
|
414
|
+
#### Visual States
|
|
415
|
+
|
|
416
|
+
| Host selector | Description |
|
|
417
|
+
| ----------------------- | ------------------------------------------------------ |
|
|
418
|
+
| `:host` | Table-cell display, padding, bottom border, no outline |
|
|
419
|
+
| `:host([active])` | Primary-tinted background at 14% opacity |
|
|
420
|
+
| `:host([selected])` | Primary-tinted background at 24% opacity |
|
|
421
|
+
| `:host([disabled])` | Reduced opacity (`0.55`) |
|
|
422
|
+
| `:host(:focus-visible)` | 2px solid primary outline with -2px offset |
|
|
423
|
+
| `:host([hidden])` | Hidden (cell references non-existent column) |
|