@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,263 @@
|
|
|
1
|
+
# cv-treeview
|
|
2
|
+
|
|
3
|
+
Hierarchical tree widget providing APG-aligned keyboard navigation, expansion/collapse, and single- or multiple-select behavior over slotted `cv-treeitem` children.
|
|
4
|
+
|
|
5
|
+
**Headless:** [`createTreeview`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/treeview.md)
|
|
6
|
+
|
|
7
|
+
## Anatomy
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
<cv-treeview> (host)
|
|
11
|
+
└── <div part="base" role="tree">
|
|
12
|
+
└── <slot> ← accepts cv-treeitem children
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Attributes
|
|
16
|
+
|
|
17
|
+
| Attribute | Type | Default | Description |
|
|
18
|
+
| ----------------- | ------ | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
19
|
+
| `value` | String | `""` | Selected item identifier in single-select mode; reflects the first entry of `selectedIds` from headless state; empty string means no selection |
|
|
20
|
+
| `values` | — | `[]` | Property-only (not an HTML attribute). Array of selected item id strings in multiple-select mode; reflects `selectedIds` from headless state |
|
|
21
|
+
| `expanded-values` | — | `[]` | Property-only (not an HTML attribute). Array of expanded item id strings; reflects `expandedIds` from headless state |
|
|
22
|
+
| `selection-mode` | String | `"single"` | Item selection mode: `single` \| `multiple` |
|
|
23
|
+
| `aria-label` | String | `""` | Accessible label applied to the root `[role=tree]` element |
|
|
24
|
+
|
|
25
|
+
## Slots
|
|
26
|
+
|
|
27
|
+
| Slot | Description |
|
|
28
|
+
| ----------- | ------------------------------------------------------------------------------------------- |
|
|
29
|
+
| `(default)` | Accepts `cv-treeitem` elements as root-level tree nodes; slot changes trigger model rebuild |
|
|
30
|
+
|
|
31
|
+
## CSS Parts
|
|
32
|
+
|
|
33
|
+
| Part | Element | Description |
|
|
34
|
+
| ------ | ------- | ---------------------------------------------------------------------------------------------------------- |
|
|
35
|
+
| `base` | `<div>` | Root interactive element with `role="tree"`; receives all ARIA tree attributes and keyboard event handling |
|
|
36
|
+
|
|
37
|
+
## CSS Custom Properties
|
|
38
|
+
|
|
39
|
+
| Property | Default | Description |
|
|
40
|
+
| ---------------------------------- | ------------------------ | ----------------------------------------------------------------------- |
|
|
41
|
+
| `--cv-treeview-indent-size` | `1.5rem` | Per-level indentation of child items |
|
|
42
|
+
| `--cv-treeview-indent-guide-width` | `0px` | Width of the vertical indent guide line; set to `1px` to show the guide |
|
|
43
|
+
| `--cv-treeview-indent-guide-color` | `var(--cv-color-border)` | Color of the vertical indent guide line |
|
|
44
|
+
| `--cv-treeview-indent-guide-style` | `solid` | Border style of the indent guide line (`solid`, `dotted`, `dashed`) |
|
|
45
|
+
|
|
46
|
+
Additionally, component styles depend on theme tokens through fallback values:
|
|
47
|
+
|
|
48
|
+
| Theme Property | Default | Description |
|
|
49
|
+
| -------------------- | --------- | ---------------------------------------------------------- |
|
|
50
|
+
| `--cv-color-border` | `#2a3245` | Border color for the base wrapper |
|
|
51
|
+
| `--cv-color-surface` | `#141923` | Background color for the base wrapper |
|
|
52
|
+
| `--cv-color-primary` | `#65d7ff` | Focus outline color for the base wrapper |
|
|
53
|
+
| `--cv-radius-md` | `10px` | Border radius for the base wrapper |
|
|
54
|
+
| `--cv-space-1` | `4px` | Gap between tree items and padding within the base wrapper |
|
|
55
|
+
|
|
56
|
+
## Visual States
|
|
57
|
+
|
|
58
|
+
| Host selector | Description |
|
|
59
|
+
| ----------------------------- | --------------------------------------------------------------------- |
|
|
60
|
+
| `:host` | `display: block` |
|
|
61
|
+
| `[part="base"]:focus-visible` | `outline: 2px solid var(--cv-color-primary)` at `outline-offset: 1px` |
|
|
62
|
+
|
|
63
|
+
## Events
|
|
64
|
+
|
|
65
|
+
| Event | Detail | Description |
|
|
66
|
+
| ----------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
67
|
+
| `cv-input` | `{ value: string \| null, values: string[], activeId: string \| null, expandedValues: string[] }` | Fires on any user interaction that changes active item, selection, or expansion state |
|
|
68
|
+
| `cv-change` | `{ value: string \| null, values: string[], activeId: string \| null, expandedValues: string[] }` | Fires when selection or expansion state commits (subset of `cv-input` cases; active-item-only changes do not fire `cv-change`) |
|
|
69
|
+
|
|
70
|
+
`value` in the detail is `null` when no item is selected, otherwise the id string of the first selected item. `values` contains all selected ids (single-select mode will have at most one entry).
|
|
71
|
+
|
|
72
|
+
`cv-input` and `cv-change` only fire for user-driven interactions (keyboard, pointer). Programmatic changes via `value`, `values`, or `expandedValues` properties do not re-dispatch these events.
|
|
73
|
+
|
|
74
|
+
## Reactive State Mapping
|
|
75
|
+
|
|
76
|
+
`cv-treeview` is a visual adapter over headless `createTreeview`.
|
|
77
|
+
|
|
78
|
+
### UIKit properties → headless actions
|
|
79
|
+
|
|
80
|
+
| UIKit Property | Direction | Headless Binding |
|
|
81
|
+
| ----------------------- | ------------- | ------------------------------------------------------------------------------------------------ |
|
|
82
|
+
| `value` (attr) | attr → action | `actions.select(value)` in single-select mode; `actions.clearSelected()` when empty |
|
|
83
|
+
| `values` (prop) | prop → action | `actions.clearSelected()` then `actions.toggleSelected(id)` per id (multiple mode; diff-applied) |
|
|
84
|
+
| `expandedValues` (prop) | prop → action | `actions.expand(id)` / `actions.collapse(id)` (diff-applied) |
|
|
85
|
+
| `selection-mode` (attr) | attr → option | passed as `selectionMode` to `createTreeview(options)` on model rebuild |
|
|
86
|
+
| `aria-label` (attr) | attr → option | passed as `ariaLabel` to `createTreeview(options)` on model rebuild |
|
|
87
|
+
|
|
88
|
+
### Headless state → DOM attributes
|
|
89
|
+
|
|
90
|
+
| Headless Signal | Direction | DOM Reflection |
|
|
91
|
+
| --------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
92
|
+
| `state.selectedIds()` | state → prop/attr | `values` property; `value` attribute (first selected id or `""`); `[selected]` on matching `cv-treeitem` elements; `aria-selected="true"` on selected items via `getItemProps` |
|
|
93
|
+
| `state.activeId()` | state → prop | `[active]` on matching `cv-treeitem` element; `tabindex="0"` on active item via `getItemProps`; focus moved to active element |
|
|
94
|
+
| `state.expandedIds()` | state → prop | `expandedValues` property; `[expanded]` on matching `cv-treeitem` elements; child visibility toggled via `[hidden]` attribute on non-expanded branch children |
|
|
95
|
+
|
|
96
|
+
### Contracts spread onto DOM elements
|
|
97
|
+
|
|
98
|
+
| Contract | Spread target |
|
|
99
|
+
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
100
|
+
| `contracts.getTreeProps()` | `[part="base"]` — applies `role`, `tabindex`, `aria-label`, `aria-multiselectable` |
|
|
101
|
+
| `contracts.getItemProps(id)` | Each `cv-treeitem` element — applies `id`, `role`, `tabindex`, `aria-level`, `aria-posinset`, `aria-setsize`, `aria-selected`, `aria-expanded` (branch only), `aria-disabled` (disabled only), `data-active`, `data-expanded`; UIKit also writes `active`, `selected`, `expanded`, `branch`, `level`, `disabled`, and `hidden` as element properties |
|
|
102
|
+
|
|
103
|
+
### Pointer and keyboard action triggers
|
|
104
|
+
|
|
105
|
+
| User Trigger | Action Called |
|
|
106
|
+
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
107
|
+
| `click` on a `cv-treeitem` | `actions.setActive(id)`; then `actions.toggleSelected(id)` (multiple mode) or `actions.select(id)` (single mode) |
|
|
108
|
+
| `cv-treeitem-toggle` event from a `cv-treeitem` | `actions.toggleExpanded(id)` |
|
|
109
|
+
| `focus` on a `cv-treeitem` | `actions.setActive(id)` |
|
|
110
|
+
| `keydown` on `[part="base"]` | `actions.handleKeyDown(event)` |
|
|
111
|
+
| slot content change | Model rebuilt from DOM (`rebuildModelFromSlot(preserveState: true)`) |
|
|
112
|
+
| `selection-mode` / `aria-label` change | Model rebuilt from DOM (`rebuildModelFromSlot(preserveState: true)`) |
|
|
113
|
+
|
|
114
|
+
## Keyboard Interaction
|
|
115
|
+
|
|
116
|
+
Derived from headless `handleKeyDown` contract:
|
|
117
|
+
|
|
118
|
+
| Key | Behavior |
|
|
119
|
+
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
120
|
+
| `ArrowUp` | Move focus to previous visible enabled item; in single-select mode also moves selection to that item (selection-follows-focus) |
|
|
121
|
+
| `ArrowDown` | Move focus to next visible enabled item; in single-select mode also moves selection to that item (selection-follows-focus) |
|
|
122
|
+
| `ArrowRight` | If focused item is a collapsed branch: expand it (focus stays). If focused item is an expanded branch: move focus to first child item |
|
|
123
|
+
| `ArrowLeft` | If focused item is an expanded branch: collapse it (focus stays). If focused item is collapsed or a leaf: move focus to parent item |
|
|
124
|
+
| `Home` | Move focus to first visible enabled item; in single-select mode also moves selection to that item (selection-follows-focus) |
|
|
125
|
+
| `End` | Move focus to last visible enabled item; in single-select mode also moves selection to that item (selection-follows-focus) |
|
|
126
|
+
| `Enter` | Select the currently focused item (both modes; replaces selection) |
|
|
127
|
+
| `Space` | Toggle selection on the currently focused item (both modes; in multiple mode, focus and selection are independent) |
|
|
128
|
+
| `Ctrl+A` / `Meta+A` | Select all enabled items (multiple mode only) |
|
|
129
|
+
|
|
130
|
+
**Selection-follows-focus** applies in single-select mode only. When `ArrowUp`, `ArrowDown`, `Home`, or `End` moves focus to a new enabled visible node, `selectedIds` is simultaneously updated to contain only that node's id. In multiple-select mode, focus and selection remain independent.
|
|
131
|
+
|
|
132
|
+
Keys `ArrowUp`, `ArrowDown`, `ArrowLeft`, `ArrowRight`, `Home`, `End`, `Enter`, and `Space` are always `preventDefault()`-ed when handled.
|
|
133
|
+
|
|
134
|
+
## ARIA
|
|
135
|
+
|
|
136
|
+
| Element | Role | Required Attributes |
|
|
137
|
+
| --------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
138
|
+
| `[part="base"]` | `tree` | `aria-label` (recommended); `aria-multiselectable="true"` when `selection-mode="multiple"` |
|
|
139
|
+
| `cv-treeitem` | `treeitem` | `aria-level`, `aria-posinset`, `aria-setsize`, `aria-selected`; `aria-expanded` (branch items only); `aria-disabled` (disabled items only) |
|
|
140
|
+
|
|
141
|
+
`aria-level` starts at `1` for root items. `aria-multiselectable` is `"true"` when `selection-mode="multiple"`, omitted otherwise.
|
|
142
|
+
|
|
143
|
+
## Usage
|
|
144
|
+
|
|
145
|
+
```html
|
|
146
|
+
<!-- Single-select tree -->
|
|
147
|
+
<cv-treeview aria-label="File tree" selection-mode="single">
|
|
148
|
+
<cv-treeitem value="src" label="src/">
|
|
149
|
+
<cv-treeitem value="index" label="index.ts" slot="children"></cv-treeitem>
|
|
150
|
+
<cv-treeitem value="app" label="app.ts" slot="children"></cv-treeitem>
|
|
151
|
+
</cv-treeitem>
|
|
152
|
+
<cv-treeitem value="readme" label="README.md"></cv-treeitem>
|
|
153
|
+
</cv-treeview>
|
|
154
|
+
|
|
155
|
+
<!-- Multiple-select tree with pre-expanded branch -->
|
|
156
|
+
<cv-treeview aria-label="Settings" selection-mode="multiple">
|
|
157
|
+
<cv-treeitem value="general" label="General">
|
|
158
|
+
<cv-treeitem value="theme" label="Theme" slot="children"></cv-treeitem>
|
|
159
|
+
<cv-treeitem value="language" label="Language" slot="children"></cv-treeitem>
|
|
160
|
+
</cv-treeitem>
|
|
161
|
+
<cv-treeitem value="advanced" label="Advanced" disabled></cv-treeitem>
|
|
162
|
+
</cv-treeview>
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Child Elements
|
|
166
|
+
|
|
167
|
+
### cv-treeitem
|
|
168
|
+
|
|
169
|
+
Represents a single tree node. Slotted directly into `cv-treeview` (root items) or into `slot="children"` of a parent `cv-treeitem` (child items). State attributes (`active`, `selected`, `expanded`, `branch`, `level`, `hidden`) are managed by the parent `cv-treeview`; authors should not set them manually.
|
|
170
|
+
|
|
171
|
+
#### Anatomy
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
<cv-treeitem> (host)
|
|
175
|
+
├── <div part="row">
|
|
176
|
+
│ ├── <button part="toggle" aria-hidden?=${!branch} hidden?=${!branch}>
|
|
177
|
+
│ │ └── ▸ or ▾ (expand/collapse icon)
|
|
178
|
+
│ └── <span part="label">
|
|
179
|
+
│ └── <slot name="label"> ← falls back to [label] attribute text
|
|
180
|
+
└── <div part="children" role="group" hidden?=${!expanded}>
|
|
181
|
+
└── <slot name="children"> ← accepts nested cv-treeitem children
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### Attributes
|
|
185
|
+
|
|
186
|
+
| Attribute | Type | Default | Description |
|
|
187
|
+
| ---------- | ------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
188
|
+
| `value` | String | `""` | Item identifier. Auto-assigned as `"item-N"` by parent if empty |
|
|
189
|
+
| `label` | String | `""` | Fallback text displayed in the `label` slot when no slotted content is provided |
|
|
190
|
+
| `disabled` | Boolean | `false` | Marks item as disabled; excluded from navigation and selection |
|
|
191
|
+
| `active` | Boolean | `false` | Set by parent `cv-treeview` when this item is the currently focused item; drives row-level active highlight |
|
|
192
|
+
| `selected` | Boolean | `false` | Set by parent `cv-treeview` when this item is selected; drives row-level selection highlight |
|
|
193
|
+
| `expanded` | Boolean | `false` | Set by parent `cv-treeview`; controls visibility of `[part="children"]` and reflects `aria-expanded` |
|
|
194
|
+
| `branch` | Boolean | `false` | Set by parent `cv-treeview` when this item has child items; shows the toggle button |
|
|
195
|
+
| `level` | Number | `1` | Nesting depth written by parent `cv-treeview` from `getItemProps()['aria-level']`; drives indentation via `--cv-tree-level` inline property; root items get `1`, child items get `2`, grandchild items get `3`, etc. |
|
|
196
|
+
| `hidden` | Boolean | `false` | Set by parent `cv-treeview` when item is not visible due to an ancestor being collapsed; maps to `display: none` |
|
|
197
|
+
|
|
198
|
+
#### Slots
|
|
199
|
+
|
|
200
|
+
| Slot | Description |
|
|
201
|
+
| ---------- | -------------------------------------------------------------------------------------------------------------- |
|
|
202
|
+
| `label` | Item label content; falls back to the `label` attribute value |
|
|
203
|
+
| `children` | Accepts nested `cv-treeitem` elements; rendered inside `[part="children"]`, hidden when `[expanded]` is absent |
|
|
204
|
+
|
|
205
|
+
#### CSS Parts
|
|
206
|
+
|
|
207
|
+
| Part | Element | Description |
|
|
208
|
+
| ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
209
|
+
| `row` | `<div>` | Row layout element; uses CSS grid with toggle button and label; left-padding computed from `--cv-treeview-indent-size` and `--cv-tree-level` (inline custom property set by parent `cv-treeview`) |
|
|
210
|
+
| `toggle` | `<button>` | Expand/collapse trigger button; hidden (visibility-hidden) when `[branch]` is absent |
|
|
211
|
+
| `label` | `<span>` | Wrapper around the `label` slot |
|
|
212
|
+
| `children` | `<div>` | Container for nested child items; `display: none` when `[expanded]` is absent |
|
|
213
|
+
|
|
214
|
+
#### CSS Custom Properties
|
|
215
|
+
|
|
216
|
+
`cv-treeitem` defines the following custom properties with defaults on `:host`, which can be overridden by the parent `cv-treeview` or any ancestor:
|
|
217
|
+
|
|
218
|
+
| Property | Default | Description |
|
|
219
|
+
| ---------------------------------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------- |
|
|
220
|
+
| `--cv-treeview-indent-size` | `1.5rem` | Per-level indentation; used directly in `[part="row"]` padding calculation via `--cv-tree-level` inline property |
|
|
221
|
+
| `--cv-treeview-indent-guide-width` | `0px` | Width of the vertical indent guide line on `[part="children"]`; set to `1px` to show the guide |
|
|
222
|
+
| `--cv-treeview-indent-guide-color` | `var(--cv-color-border)` | Color of the vertical indent guide line |
|
|
223
|
+
| `--cv-treeview-indent-guide-style` | `solid` | Border style of the vertical indent guide line |
|
|
224
|
+
|
|
225
|
+
Additionally, component styles depend on theme tokens:
|
|
226
|
+
|
|
227
|
+
| Theme Property | Default | Description |
|
|
228
|
+
| -------------------- | --------- | ----------------------------------------------------------- |
|
|
229
|
+
| `--cv-color-primary` | `#65d7ff` | Active/selected row background tint and focus outline color |
|
|
230
|
+
| `--cv-color-border` | `#2a3245` | Toggle button border color |
|
|
231
|
+
| `--cv-color-surface` | `#141923` | Toggle button background color |
|
|
232
|
+
| `--cv-color-text` | `#e8ecf6` | Toggle button icon color |
|
|
233
|
+
| `--cv-radius-sm` | `6px` | Border radius of `[part="row"]` and `[part="toggle"]` |
|
|
234
|
+
| `--cv-radius-xs` | `4px` | Border radius of `[part="toggle"]` |
|
|
235
|
+
| `--cv-space-1` | `4px` | Gap between toggle button and label in `[part="row"]` |
|
|
236
|
+
| `--cv-space-2` | `8px` | Inline-end padding of `[part="row"]` |
|
|
237
|
+
|
|
238
|
+
#### Visual States
|
|
239
|
+
|
|
240
|
+
| Host selector | Description |
|
|
241
|
+
| ------------------------------------- | --------------------------------------------------------------------------- |
|
|
242
|
+
| `:host` | `display: block`, `outline: none` |
|
|
243
|
+
| `:host([hidden])` | `display: none` |
|
|
244
|
+
| `:host([active]) [part="row"]` | `background: color-mix(in oklab, var(--cv-color-primary) 22%, transparent)` |
|
|
245
|
+
| `:host([selected]) [part="row"]` | `background: color-mix(in oklab, var(--cv-color-primary) 30%, transparent)` |
|
|
246
|
+
| `:host([disabled]) [part="row"]` | `opacity: 0.55` |
|
|
247
|
+
| `:host([expanded]) [part="children"]` | Visible (default); children hidden only when `[expanded]` is absent |
|
|
248
|
+
| `:host([branch]) [part="toggle"]` | Toggle button rendered and visible |
|
|
249
|
+
| `:host(:focus-visible) [part="row"]` | `outline: 2px solid var(--cv-color-primary)` at `outline-offset: 1px` |
|
|
250
|
+
|
|
251
|
+
#### Events
|
|
252
|
+
|
|
253
|
+
| Event | Detail | Description |
|
|
254
|
+
| -------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
255
|
+
| `cv-treeitem-toggle` | `{ id: string }` | Fired (bubbling, composed) when the user clicks the `[part="toggle"]` button; intercepted by the parent `cv-treeview` to call `actions.toggleExpanded(id)`; `stopPropagation()` is called by the parent so the event does not escape the tree |
|
|
256
|
+
|
|
257
|
+
## Out of Scope
|
|
258
|
+
|
|
259
|
+
- Async/lazy loading of child nodes (pagination or lazy fetch)
|
|
260
|
+
- Drag-and-drop node reordering
|
|
261
|
+
- Checkbox or radio variant tree items
|
|
262
|
+
- Typeahead (jump to node by typed character)
|
|
263
|
+
- Leaf-only selection mode (preventing branch-node selection)
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# cv-window-splitter
|
|
2
|
+
|
|
3
|
+
A resizable pane separator that lets users drag or keyboard-navigate to redistribute space between two adjacent panels.
|
|
4
|
+
|
|
5
|
+
**Headless:** [`createWindowSplitter`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/window-splitter.md)
|
|
6
|
+
|
|
7
|
+
## Anatomy
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
<cv-window-splitter> (host)
|
|
11
|
+
└── <div part="base" data-orientation="vertical|horizontal">
|
|
12
|
+
├── <div part="pane" data-pane="primary" data-orientation="vertical|horizontal">
|
|
13
|
+
│ └── <slot name="primary">
|
|
14
|
+
├── <div part="separator" role="separator" tabindex="0"
|
|
15
|
+
│ aria-valuenow aria-valuemin aria-valuemax
|
|
16
|
+
│ aria-orientation aria-controls
|
|
17
|
+
│ data-orientation="vertical|horizontal">
|
|
18
|
+
│ └── <span part="separator-handle">
|
|
19
|
+
│ └── <slot name="separator"> ← custom handle content
|
|
20
|
+
└── <div part="pane" data-pane="secondary" data-orientation="vertical|horizontal">
|
|
21
|
+
└── <slot name="secondary">
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
> The `separator-handle` span renders a default glyph (`⋮` for vertical, `⋯` for horizontal) when the `separator` slot is empty.
|
|
25
|
+
|
|
26
|
+
## Attributes
|
|
27
|
+
|
|
28
|
+
| Attribute | Type | Default | Description |
|
|
29
|
+
| ----------------- | ------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
30
|
+
| `position` | Number | `50` | Current splitter position within `[min, max]`. Reflected as an attribute. |
|
|
31
|
+
| `min` | Number | `0` | Minimum allowed position (inclusive). |
|
|
32
|
+
| `max` | Number | `100` | Maximum allowed position (inclusive). |
|
|
33
|
+
| `step` | Number | `1` | Step size applied per arrow-key press. |
|
|
34
|
+
| `orientation` | String | `"horizontal"` | Axis of the separator bar: `"vertical"` (vertical bar, left/right split) \| `"horizontal"` (horizontal bar, top/bottom split). Matches `aria-orientation`. |
|
|
35
|
+
| `fixed` | Boolean | `false` | Enables fixed (toggle) mode. Arrow keys are disabled; `Enter` toggles position between `min` and `max`. |
|
|
36
|
+
| `snap` | String | — | Space-separated snap positions, e.g. `"25 50 75"` or `"25% 50% 75%"`. Values ending in `%` are resolved as percentages of the `[min, max]` range. Snap logic runs inside headless `setPosition`. |
|
|
37
|
+
| `snap-threshold` | Number | `12` | Maximum distance from a snap point within which `setPosition` snaps instead of using the raw value. Expressed in the same unit as `position`. |
|
|
38
|
+
| `aria-label` | String | `""` | Accessible label applied to the separator element. |
|
|
39
|
+
| `aria-labelledby` | String | `""` | ID(s) of element(s) that label the separator. |
|
|
40
|
+
|
|
41
|
+
## Variants
|
|
42
|
+
|
|
43
|
+
| Variant | Attribute | Description |
|
|
44
|
+
| ------- | --------- | ----------------------------------------------------------------- |
|
|
45
|
+
| Default | _(none)_ | Continuous slider; arrow keys adjust position by `step`. |
|
|
46
|
+
| Fixed | `fixed` | Toggle mode; `Enter` collapses/restores. Arrow keys are disabled. |
|
|
47
|
+
|
|
48
|
+
> `orientation` is a configuration attribute, not a visual variant — both orientations share the same variant rows above.
|
|
49
|
+
|
|
50
|
+
## Slots
|
|
51
|
+
|
|
52
|
+
| Slot | Description |
|
|
53
|
+
| ----------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
54
|
+
| `primary` | Content of the primary (first) pane. |
|
|
55
|
+
| `secondary` | Content of the secondary (second) pane. |
|
|
56
|
+
| `separator` | Custom handle content rendered inside `[part="separator-handle"]`. Replaces the default orientation glyph when provided. |
|
|
57
|
+
|
|
58
|
+
## CSS Parts
|
|
59
|
+
|
|
60
|
+
| Part | Element | Description |
|
|
61
|
+
| ------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
62
|
+
| `base` | `<div>` | Root grid container. Receives `data-orientation` and the inline `--cv-window-splitter-primary-size` variable. |
|
|
63
|
+
| `pane` | `<div>` | Either pane. Carries `data-pane="primary"` or `data-pane="secondary"`, and `data-orientation`. |
|
|
64
|
+
| `separator` | `<div>` | The focusable, interactive separator element with `role="separator"`. Receives all ARIA and `data-orientation` attributes. |
|
|
65
|
+
| `separator-handle` | `<span>` | Visual drag handle inside the separator. Renders the default glyph or the `separator` slot content. |
|
|
66
|
+
|
|
67
|
+
### Data attributes on `[part="pane"]`
|
|
68
|
+
|
|
69
|
+
| Data attribute | Values | Description |
|
|
70
|
+
| ------------------ | ------------------------------ | ----------------------------------------------------------- |
|
|
71
|
+
| `data-pane` | `"primary"` \| `"secondary"` | Identifies which pane this element is. |
|
|
72
|
+
| `data-orientation` | `"vertical"` \| `"horizontal"` | Mirrors the host `orientation` attribute for CSS targeting. |
|
|
73
|
+
|
|
74
|
+
### Data attributes on `[part="separator"]`
|
|
75
|
+
|
|
76
|
+
| Data attribute | Values | Description |
|
|
77
|
+
| ------------------ | ------------------------------ | -------------------------------------------------------------------------------------------------- |
|
|
78
|
+
| `data-orientation` | `"vertical"` \| `"horizontal"` | Mirrors the host `orientation` attribute for cursor and layout CSS. |
|
|
79
|
+
| `data-dragging` | Present when dragging | Set while a pointer drag is active (set on `pointerdown`, removed on `pointerup`/`pointercancel`). |
|
|
80
|
+
|
|
81
|
+
## CSS Custom Properties
|
|
82
|
+
|
|
83
|
+
### Component properties
|
|
84
|
+
|
|
85
|
+
| Property | Default | Description |
|
|
86
|
+
| ----------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
87
|
+
| `--cv-window-splitter-primary-size` | `50%` | Computed percentage size of the primary pane, set inline on `[part="base"]` as the grid track size. Updated continuously during drag and keyboard interaction. |
|
|
88
|
+
| `--cv-window-splitter-divider-size` | `8px` | Width (vertical orientation) or height (horizontal orientation) of the separator track in the grid layout. |
|
|
89
|
+
|
|
90
|
+
### Theme tokens consumed (via fallback values)
|
|
91
|
+
|
|
92
|
+
| Theme property | Default | Description |
|
|
93
|
+
| ----------------------- | --------- | ----------------------------------------------------- |
|
|
94
|
+
| `--cv-color-surface` | `#141923` | Used (mixed with black) for the separator background. |
|
|
95
|
+
| `--cv-color-border` | `#2a3245` | Separator border color. |
|
|
96
|
+
| `--cv-color-text-muted` | `#9aa6bf` | Separator handle icon color. |
|
|
97
|
+
| `--cv-color-primary` | `#65d7ff` | Focus ring color on the separator. |
|
|
98
|
+
|
|
99
|
+
## Visual States
|
|
100
|
+
|
|
101
|
+
| Selector | Description |
|
|
102
|
+
| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
103
|
+
| `[part="base"][data-orientation="vertical"]` | Vertical separator bar; `[part="base"]` uses `grid-template-columns` with three tracks (primary size, divider size, `1fr`); cursor on separator is `col-resize`. |
|
|
104
|
+
| `[part="base"][data-orientation="horizontal"]` | Horizontal separator bar; `[part="base"]` uses `grid-template-rows` with three tracks (primary size, divider size, `1fr`); cursor on separator is `row-resize`. |
|
|
105
|
+
| `:host([fixed])` | Fixed/toggle mode. No visual difference by default; host styles may suppress drag-cursor or reduce separator opacity as desired. (`fixed` is a reflected boolean property, so `:host([fixed])` is a valid CSS hook for consumers.) |
|
|
106
|
+
| `[part="separator"]:focus-visible` | `outline: 2px solid var(--cv-color-primary, #65d7ff)` with `outline-offset: 1px`. |
|
|
107
|
+
| `[part="separator"][data-dragging]` | Applied while a pointer drag is in progress. Can be targeted in CSS for active drag styles (e.g. highlight, elevated `z-index`). |
|
|
108
|
+
|
|
109
|
+
## Reactive State Mapping
|
|
110
|
+
|
|
111
|
+
`cv-window-splitter` is a visual adapter over headless `createWindowSplitter`.
|
|
112
|
+
|
|
113
|
+
### UIKit attributes → headless options / actions
|
|
114
|
+
|
|
115
|
+
| UIKit Attribute | Direction | Headless Binding |
|
|
116
|
+
| ----------------- | ------------- | ------------------------------------------------------------ |
|
|
117
|
+
| `position` | attr → action | `actions.setPosition(value)` on change |
|
|
118
|
+
| `min` | attr → option | `createWindowSplitter({ min })` (model recreated) |
|
|
119
|
+
| `max` | attr → option | `createWindowSplitter({ max })` (model recreated) |
|
|
120
|
+
| `step` | attr → option | `createWindowSplitter({ step })` (model recreated) |
|
|
121
|
+
| `orientation` | attr → option | `createWindowSplitter({ orientation })` (model recreated) |
|
|
122
|
+
| `fixed` | attr → option | `createWindowSplitter({ isFixed })` (model recreated) |
|
|
123
|
+
| `snap` | attr → option | `createWindowSplitter({ snap })` (model recreated) |
|
|
124
|
+
| `snap-threshold` | attr → option | `createWindowSplitter({ snapThreshold })` (model recreated) |
|
|
125
|
+
| `aria-label` | attr → option | `createWindowSplitter({ ariaLabel })` (model recreated) |
|
|
126
|
+
| `aria-labelledby` | attr → option | `createWindowSplitter({ ariaLabelledBy })` (model recreated) |
|
|
127
|
+
|
|
128
|
+
> Model recreation: when any option-only attribute changes, the entire headless model is recreated via `createWindowSplitter(...)` with the latest values.
|
|
129
|
+
|
|
130
|
+
### Headless state → DOM reflection
|
|
131
|
+
|
|
132
|
+
| Headless Signal | Direction | DOM / CSS Reflection |
|
|
133
|
+
| --------------------- | ------------ | ------------------------------------------------------------------------------------------------ |
|
|
134
|
+
| `state.position()` | state → CSS | `--cv-window-splitter-primary-size` inline on `[part="base"]`; `position` host attribute updated |
|
|
135
|
+
| `state.isDragging()` | state → attr | `[data-dragging]` on `[part="separator"]` |
|
|
136
|
+
| `state.orientation()` | state → attr | `data-orientation` on `[part="base"]`, `[part="separator"]`, and both `[part="pane"]` elements |
|
|
137
|
+
|
|
138
|
+
### Contract spreading
|
|
139
|
+
|
|
140
|
+
`contracts.getSplitterProps()` is spread onto `[part="separator"]` to apply:
|
|
141
|
+
|
|
142
|
+
- `role="separator"`
|
|
143
|
+
- `tabindex="0"`
|
|
144
|
+
- `aria-valuenow`, `aria-valuemin`, `aria-valuemax`, `aria-valuetext` (if `formatValueText` provided)
|
|
145
|
+
- `aria-orientation`
|
|
146
|
+
- `aria-controls` (space-separated IDs of both pane elements)
|
|
147
|
+
- `aria-label` / `aria-labelledby` (when set)
|
|
148
|
+
- `onKeyDown` handler bound to the `keydown` event
|
|
149
|
+
|
|
150
|
+
`contracts.getPrimaryPaneProps()` is spread onto the primary `[part="pane"]` to apply `id`, `data-pane="primary"`, and `data-orientation`.
|
|
151
|
+
|
|
152
|
+
`contracts.getSecondaryPaneProps()` is spread onto the secondary `[part="pane"]` to apply `id`, `data-pane="secondary"`, and `data-orientation`.
|
|
153
|
+
|
|
154
|
+
### Pointer event drag contract (target state)
|
|
155
|
+
|
|
156
|
+
The implementation MUST use pointer events with capture for reliable cross-boundary dragging:
|
|
157
|
+
|
|
158
|
+
1. **`pointerdown`** on `[part="separator"]` (primary button only):
|
|
159
|
+
- Call `event.preventDefault()` and focus the separator.
|
|
160
|
+
- Call `actions.startDragging()`.
|
|
161
|
+
- Call `separatorEl.setPointerCapture(event.pointerId)` so subsequent `pointermove`/`pointerup` events are routed to the separator regardless of cursor position.
|
|
162
|
+
- Set `[data-dragging]` on the separator.
|
|
163
|
+
- Compute and apply the initial position via `actions.setPosition(...)`.
|
|
164
|
+
|
|
165
|
+
2. **`pointermove`** on `[part="separator"]` (while captured):
|
|
166
|
+
- Convert `event.clientX` / `event.clientY` to a position value relative to `[part="base"]` bounding rect, clamped to `[0, 1]` ratio.
|
|
167
|
+
- Call `actions.setPosition(computedValue)` (snap logic applied inside headless).
|
|
168
|
+
- Dispatch `cv-input` event with `{ position }`.
|
|
169
|
+
|
|
170
|
+
3. **`pointerup`** / **`pointercancel`** on `[part="separator"]`:
|
|
171
|
+
- Compute final position.
|
|
172
|
+
- Call `actions.stopDragging()`.
|
|
173
|
+
- Call `separatorEl.releasePointerCapture(event.pointerId)`.
|
|
174
|
+
- Remove `[data-dragging]` from the separator.
|
|
175
|
+
- If position changed during the drag, dispatch `cv-change` event with `{ position }`.
|
|
176
|
+
|
|
177
|
+
> Note: The current implementation uses `mousedown`/`mousemove`/`mouseup` on `document`. The target state described above replaces this with pointer capture on the separator element. `IMPL_UIKIT` will perform this migration.
|
|
178
|
+
|
|
179
|
+
## Events
|
|
180
|
+
|
|
181
|
+
| Event | Detail | Description |
|
|
182
|
+
| ----------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
183
|
+
| `cv-input` | `{ position: number }` | Fires on every position change during drag (`pointermove`) or keyboard interaction. Bubbles and is composed. |
|
|
184
|
+
| `cv-change` | `{ position: number }` | Fires on committed changes: keyboard key release that caused a position change, or `pointerup` when position changed during the drag. Bubbles and is composed. |
|
|
185
|
+
|
|
186
|
+
Both events are dispatched as `CustomEvent` with `bubbles: true` and `composed: true`. The `cv-input` event fires continuously during interaction; `cv-change` fires once per committed gesture.
|
|
187
|
+
|
|
188
|
+
## Usage
|
|
189
|
+
|
|
190
|
+
```html
|
|
191
|
+
<!-- Default: horizontal separator (top/bottom split) -->
|
|
192
|
+
<cv-window-splitter position="40" min="20" max="80">
|
|
193
|
+
<div slot="primary">
|
|
194
|
+
<p>Primary pane content</p>
|
|
195
|
+
</div>
|
|
196
|
+
<div slot="secondary">
|
|
197
|
+
<p>Secondary pane content</p>
|
|
198
|
+
</div>
|
|
199
|
+
</cv-window-splitter>
|
|
200
|
+
|
|
201
|
+
<!-- Vertical separator (left/right split) with snap points -->
|
|
202
|
+
<cv-window-splitter
|
|
203
|
+
orientation="vertical"
|
|
204
|
+
position="50"
|
|
205
|
+
snap="25% 50% 75%"
|
|
206
|
+
snap-threshold="10"
|
|
207
|
+
aria-label="Resize panels"
|
|
208
|
+
>
|
|
209
|
+
<nav slot="primary">Navigation</nav>
|
|
210
|
+
<main slot="secondary">Content</main>
|
|
211
|
+
</cv-window-splitter>
|
|
212
|
+
|
|
213
|
+
<!-- Fixed (toggle) mode -->
|
|
214
|
+
<cv-window-splitter orientation="vertical" fixed position="30">
|
|
215
|
+
<aside slot="primary">Sidebar</aside>
|
|
216
|
+
<section slot="secondary">Main content</section>
|
|
217
|
+
</cv-window-splitter>
|
|
218
|
+
|
|
219
|
+
<!-- Custom separator handle -->
|
|
220
|
+
<cv-window-splitter orientation="vertical">
|
|
221
|
+
<div slot="primary">Left</div>
|
|
222
|
+
<div slot="secondary">Right</div>
|
|
223
|
+
<span slot="separator">⠿</span>
|
|
224
|
+
</cv-window-splitter>
|
|
225
|
+
```
|