@ixfx/components 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/bundle/index.d.ts +1490 -615
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +20477 -15855
  4. package/bundle/index.js.map +1 -1
  5. package/bundle/style.css +1 -1
  6. package/dist/ac-text.js +3 -2
  7. package/dist/ac-text.js.map +1 -1
  8. package/dist/ac-token.js +3 -2
  9. package/dist/ac-token.js.map +1 -1
  10. package/dist/{button-Bn0BuLpO.js → button-Dat2yHAU.js} +4 -3
  11. package/dist/button-Dat2yHAU.js.map +1 -0
  12. package/dist/button.js +1 -1
  13. package/dist/checkbox.js +1 -1
  14. package/dist/{colour-picker-0stHB90J.js → colour-picker-BSDBL-QL.js} +4 -3
  15. package/dist/{colour-picker-0stHB90J.js.map → colour-picker-BSDBL-QL.js.map} +1 -1
  16. package/dist/colour-picker.js +1 -1
  17. package/dist/crumbs.js +4 -2
  18. package/dist/crumbs.js.map +1 -1
  19. package/dist/data-display.js +1 -1
  20. package/dist/data-grid.js +4 -2
  21. package/dist/data-grid.js.map +1 -1
  22. package/dist/{defaults-BCiDhZbF.js → defaults-BkWCPJFL.js} +3 -20
  23. package/dist/defaults-BkWCPJFL.js.map +1 -0
  24. package/dist/editable-label-rzXkjf1H.d.ts +75 -0
  25. package/dist/editable-label-rzXkjf1H.d.ts.map +1 -0
  26. package/dist/editable-label.d.ts +2 -75
  27. package/dist/editable-label.js +2 -1
  28. package/dist/editable-label.js.map +1 -1
  29. package/dist/{fallbacks-DEE1nCTt.js → fallbacks-DNI9xt0F.js} +2 -2
  30. package/dist/{fallbacks-DEE1nCTt.js.map → fallbacks-DNI9xt0F.js.map} +1 -1
  31. package/dist/{grouped-item-lister-_pMDwIo1.d.ts → grouped-item-lister-DnWB-eFT.d.ts} +2 -2
  32. package/dist/{grouped-item-lister-_pMDwIo1.d.ts.map → grouped-item-lister-DnWB-eFT.d.ts.map} +1 -1
  33. package/dist/grouped-item-lister.d.ts +1 -1
  34. package/dist/{icons-Bm2ByQ3s.js → icon-CvnPanhA.js} +3 -6
  35. package/dist/icon-CvnPanhA.js.map +1 -0
  36. package/dist/icons.js +8 -2
  37. package/dist/icons.js.map +1 -0
  38. package/dist/index-CG2vIgPv.d.ts +101 -0
  39. package/dist/index-CG2vIgPv.d.ts.map +1 -0
  40. package/dist/index.d.ts +655 -65
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +3682 -924
  43. package/dist/index.js.map +1 -1
  44. package/dist/interaction-D5XdKzbR.js +2 -0
  45. package/dist/labelled-radial-input.js +2 -1
  46. package/dist/labelled-radial-input.js.map +1 -1
  47. package/dist/labelled-slider-input-DzigjMwP.js +808 -0
  48. package/dist/labelled-slider-input-DzigjMwP.js.map +1 -0
  49. package/dist/labelled-slider-input.d.ts +2 -0
  50. package/dist/labelled-slider-input.js +2 -0
  51. package/dist/{types-DlVNJez_.d.ts → list-selection-types-DSuRNWpx.d.ts} +10 -2
  52. package/dist/list-selection-types-DSuRNWpx.d.ts.map +1 -0
  53. package/dist/menu-BjRvlgPy.js +825 -0
  54. package/dist/menu-BjRvlgPy.js.map +1 -0
  55. package/dist/menu-item-B-zYrTTi.js +797 -0
  56. package/dist/menu-item-B-zYrTTi.js.map +1 -0
  57. package/dist/menu.js +2 -1
  58. package/dist/miller.d.ts +1 -1
  59. package/dist/miller.js +3 -2
  60. package/dist/miller.js.map +1 -1
  61. package/dist/narrowed-text.js +4 -2
  62. package/dist/narrowed-text.js.map +1 -1
  63. package/dist/notifier.js +3 -2
  64. package/dist/notifier.js.map +1 -1
  65. package/dist/panel.d.ts +13 -13
  66. package/dist/panel.d.ts.map +1 -1
  67. package/dist/panel.js +37 -253
  68. package/dist/panel.js.map +1 -1
  69. package/dist/plots.js +1 -1
  70. package/dist/polar-pad.js +1 -1
  71. package/dist/{radial-input-Dk8Wydp5.js → radial-input-Dp5qz0ly.js} +2 -2
  72. package/dist/{radial-input-Dk8Wydp5.js.map → radial-input-Dp5qz0ly.js.map} +1 -1
  73. package/dist/radial-input.js +1 -1
  74. package/dist/range-input.js +1 -1
  75. package/dist/range.js +1 -1
  76. package/dist/registry-CkW7q09Q.js +21 -0
  77. package/dist/registry-CkW7q09Q.js.map +1 -0
  78. package/dist/select-horiz.js +3 -2
  79. package/dist/select-horiz.js.map +1 -1
  80. package/dist/{slider-input-XppH3vcy.d.ts → slider-input-BfRUW2ZB.d.ts} +16 -3
  81. package/dist/slider-input-BfRUW2ZB.d.ts.map +1 -0
  82. package/dist/{slider-input-CT4gcF-o.js → slider-input-De4eoT11.js} +382 -67
  83. package/dist/slider-input-De4eoT11.js.map +1 -0
  84. package/dist/slider-input.d.ts +1 -1
  85. package/dist/slider-input.js +1 -1
  86. package/dist/snackbar.js +1 -1
  87. package/dist/split-layout-BFSbUZQv.js +969 -0
  88. package/dist/split-layout-BFSbUZQv.js.map +1 -0
  89. package/dist/split-layout-JK3xWVX2.d.ts +179 -0
  90. package/dist/split-layout-JK3xWVX2.d.ts.map +1 -0
  91. package/dist/split-layout.d.ts +2 -60
  92. package/dist/split-layout.js +1 -617
  93. package/dist/style.css +1 -1
  94. package/dist/swipe.js +1 -1
  95. package/dist/{tab-list-D_1cYhB_.d.ts → tab-list-MPwq_1Jj.d.ts} +2 -1
  96. package/dist/tab-list-MPwq_1Jj.d.ts.map +1 -0
  97. package/dist/tabs.d.ts +1 -1
  98. package/dist/tabs.js +96 -14
  99. package/dist/tabs.js.map +1 -1
  100. package/dist/{tickled-controller-BtevrRLV.d.ts → tickled-controller-CRzdIEjJ.d.ts} +7 -1
  101. package/dist/tickled-controller-CRzdIEjJ.d.ts.map +1 -0
  102. package/dist/{tickled-controller-DkAU-0qL.js → tickled-controller-h9GmJ_bU.js} +8 -2
  103. package/dist/tickled-controller-h9GmJ_bU.js.map +1 -0
  104. package/dist/titlebar-DaCfpliv.d.ts +50 -0
  105. package/dist/titlebar-DaCfpliv.d.ts.map +1 -0
  106. package/dist/titlebar.d.ts +2 -0
  107. package/dist/titlebar.js +324 -0
  108. package/dist/titlebar.js.map +1 -0
  109. package/dist/{tree-BO6BXI2b.js → tree-Ch4W8qXQ.js} +4 -3
  110. package/dist/{tree-BO6BXI2b.js.map → tree-Ch4W8qXQ.js.map} +1 -1
  111. package/dist/tree.d.ts +1 -1
  112. package/dist/tree.js +1 -1
  113. package/dist/vertical-list-ClnqTkF3.js +814 -0
  114. package/dist/vertical-list-ClnqTkF3.js.map +1 -0
  115. package/dist/vertical-list.d.ts +8 -9
  116. package/dist/vertical-list.d.ts.map +1 -1
  117. package/dist/vertical-list.js +1 -736
  118. package/dist/{xy-axis-B6yMjckW.js → xy-axis-CxHC8z5E.js} +109 -3
  119. package/dist/xy-axis-CxHC8z5E.js.map +1 -0
  120. package/dist/xy-pad.js +1 -1
  121. package/docs-user/README.md +5 -0
  122. package/docs-user/detail-list.md +398 -0
  123. package/docs-user/dock.md +310 -0
  124. package/docs-user/index.json +27 -2
  125. package/docs-user/labelled-slider-input.md +198 -0
  126. package/docs-user/llms.txt +6 -1
  127. package/docs-user/panel.md +20 -1
  128. package/docs-user/slider-input.md +55 -15
  129. package/docs-user/split-layout.md +136 -170
  130. package/docs-user/tabs.md +34 -0
  131. package/docs-user/titlebar.md +102 -0
  132. package/docs-user/toolbar.md +95 -22
  133. package/docs-user/twosplit-layout.md +254 -0
  134. package/docs-user/user-catalog.md +8 -1
  135. package/llms.txt +6 -1
  136. package/package.json +1 -2
  137. package/dist/button-Bn0BuLpO.js.map +0 -1
  138. package/dist/defaults-BCiDhZbF.js.map +0 -1
  139. package/dist/editable-label.d.ts.map +0 -1
  140. package/dist/icons-Bm2ByQ3s.js.map +0 -1
  141. package/dist/menu-DIfU70AA.js +0 -1609
  142. package/dist/menu-DIfU70AA.js.map +0 -1
  143. package/dist/slider-input-CT4gcF-o.js.map +0 -1
  144. package/dist/slider-input-XppH3vcy.d.ts.map +0 -1
  145. package/dist/split-layout.d.ts.map +0 -1
  146. package/dist/split-layout.js.map +0 -1
  147. package/dist/tab-list-D_1cYhB_.d.ts.map +0 -1
  148. package/dist/tickled-controller-BtevrRLV.d.ts.map +0 -1
  149. package/dist/tickled-controller-DkAU-0qL.js.map +0 -1
  150. package/dist/types-DlVNJez_.d.ts.map +0 -1
  151. package/dist/vertical-list.js.map +0 -1
  152. package/dist/xy-axis-B6yMjckW.js.map +0 -1
@@ -1,6 +1,6 @@
1
- # Split Layout
1
+ # N-Way Split Layout
2
2
 
3
- A Lit web component that lays out two elements within a container with a split bar to adjust their proportions.
3
+ A Lit web component that lays out any number of panels along one axis with draggable gutters between them.
4
4
 
5
5
  ---
6
6
 
@@ -10,245 +10,211 @@ A Lit web component that lays out two elements within a container with a split b
10
10
 
11
11
  | Attribute | Type | Default | Description |
12
12
  |-----------|------|---------|-------------|
13
- | `layout` | `vertical` \| `horizontal` | `vertical` | Split orientation. `vertical` creates left/right panels, `horizontal` creates top/bottom panels. |
14
- | `sizeA` | `String` | `""` | CSS size for panel A (slot `a`). Can use relative (`50%`) or absolute (`100px`) values. Empty string defaults to `auto`. |
15
- | `sizeB` | `String` | `""` | CSS size for panel B (slot `b`). Same format as `sizeA`. |
16
- | `fixedA` | `Boolean` | `false` | If `true`, `sizeA` is calculated in pixels rather than percentage of whole. |
17
- | `fixedB` | `Boolean` | `false` | If `true`, `sizeB` is calculated in pixels rather than percentage of whole. |
18
- | `collapsible` | `Boolean` | `false` | If `true`, panels can be collapsed by dragging the handle to the edge. The other panel expands to fill the space. |
19
- | `minSizeA` | `String` | `""` | Minimum size for panel A (e.g., `"100px"` or `"20%"`). Empty means no minimum. |
20
- | `maxSizeA` | `String` | `""` | Maximum size for panel A. Empty means no maximum. |
21
- | `minSizeB` | `String` | `""` | Minimum size for panel B. |
22
- | `maxSizeB` | `String` | `""` | Maximum size for panel B. |
23
-
24
- ### Properties
25
-
26
- | Property | Type | Description |
27
- |----------|------|-------------|
28
- | `collapsedA` | `Boolean` (reflects attribute) | `true` if panel A is currently collapsed. |
29
- | `collapsedB` | `Boolean` (reflects attribute) | `true` if panel B is currently collapsed. |
30
-
31
- ### CSS Variables
13
+ | `direction` | `row` \| `column` | `row` | Split direction. `row` creates side-by-side panels, `column` creates stacked panels. |
14
+ | `sizes` | string | `''` | Positional weight shorthand, e.g. `"1 2 1"`, applied to children lacking an explicit `size`. |
15
+ | `storageKey` | string | `''` | Enables `localStorage` persistence. |
16
+ | `gutterSize` | string | | Convenience mirror of `--split-gutter-size`. |
17
+ | `snap` | string | | Convenience mirror of `--split-snap`. |
32
18
 
33
- | Variable | Default | Description |
34
- |----------|---------|-------------|
35
- | `--handle-size` | `2px` | Width/height of the split bar |
36
- | `--handle-colour` | `transparent` | Colour of the split bar |
37
- | `--handle-hover-colour` | `var(--surface-6)` | Colour of handle when hovering |
38
- | `--handle-active-colour` | `var(--accent)` | Colour of handle when being dragged |
39
- | `--handle-bg` | `var(--handle-colour)` | Background colour of the grab handle rectangle |
40
- | `--handle-grab-colour` | `var(--surface-muted-text)` | Colour of the grab handle dots |
41
- | `--handle-grab-dot-size` | `3px` | Diameter of each grab handle dot |
42
- | `--handle-grab-size` | `16px` | Length of the grab handle |
43
- | `--handle-grab-thickness` | `8px` | Thickness of the grab handle |
44
-
45
- ### Events
46
-
47
- | Event | Detail | Description |
48
- |-------|--------|-------------|
49
- | `ixfx-split-collapsed` | `{ panel: 'A' \| 'B', collapsed: boolean }` | Fired when a panel is collapsed or uncollapsed via drag. |
19
+ ---
20
+
21
+ ## `ixfx-split-panel`
22
+
23
+ The `ixfx-split-panel` element wraps panel content and provides sizing constraints.
24
+
25
+ ### Attributes
26
+
27
+ | Attribute | Type | Default | Description |
28
+ |-----------|------|---------|-------------|
29
+ | `id` | string | generated | Identity for persistence + events. |
30
+ | `size` / `weight` | number | `1` | Flex weight. |
31
+ | `fixed` | boolean \| length | `false` | `fixed` or `fixed="200px"` → non-flexible px track. |
32
+ | `min` | length (`px`/`%`) | `0` | Resolved to px against the group's container. |
33
+ | `max` | length | | |
34
+ | `when-small` | `prevent` \| `collapse` \| `close` | `prevent` | Behaviour past `min`. |
35
+ | `collapsed` | boolean | `false` | Reflected; toggled by the controller. |
36
+ | `snap` | length | `--split-snap` | Per-panel hysteresis override. |
50
37
 
51
38
  ---
52
39
 
53
40
  ## Basic Usage
54
41
 
55
- ### Vertical Split (Left/Right)
42
+ ### Three-Panel Row
56
43
 
57
44
  ```html
58
- <ixfx-split-layout layout="vertical" style="width: 100%; height: 400px;">
59
- <div slot="a" style="background: var(--surface-3);">Left Panel</div>
60
- <div slot="b" style="background: var(--surface-5);">Right Panel</div>
45
+ <ixfx-split-layout direction="row" sizes="1 2 1" style="width: 100%; height: 400px;">
46
+ <ixfx-split-panel>Left Panel</ixfx-split-panel>
47
+ <ixfx-split-panel>Center Panel</ixfx-split-panel>
48
+ <ixfx-split-panel>Right Panel</ixfx-split-panel>
61
49
  </ixfx-split-layout>
62
50
  ```
63
51
 
64
- ### Horizontal Split (Top/Bottom)
52
+ ### Column Layout
65
53
 
66
54
  ```html
67
- <ixfx-split-layout layout="horizontal" style="width: 100%; height: 400px;">
68
- <div slot="a" style="background: var(--surface-3);">Top Panel</div>
69
- <div slot="b" style="background: var(--surface-5);">Bottom Panel</div>
55
+ <ixfx-split-layout direction="column" style="width: 100%; height: 400px;">
56
+ <ixfx-split-panel>Top Panel</ixfx-split-panel>
57
+ <ixfx-split-panel>Bottom Panel</ixfx-split-panel>
70
58
  </ixfx-split-layout>
71
59
  ```
72
60
 
73
61
  ### With Fixed Panel
74
62
 
75
63
  ```html
76
- <!-- Left panel fixed at 200px, right panel takes remaining space -->
77
- <ixfx-split-layout fixedA layout="vertical" style="width: 100%; height: 400px;">
78
- <div slot="a" style="background: var(--surface-3);">Left Panel (200px)</div>
79
- <div slot="b" style="background: var(--surface-5);">Right Panel (flexible)</div>
64
+ <ixfx-split-layout direction="row" style="width: 100%; height: 400px;">
65
+ <ixfx-split-panel fixed="200px">Left (200px)</ixfx-split-panel>
66
+ <ixfx-split-panel>Right (flexible)</ixfx-split-panel>
80
67
  </ixfx-split-layout>
81
68
  ```
82
69
 
83
- ---
84
-
85
- ## Collapsible Panels
86
-
87
- When `collapsible` is set, dragging a split bar to the edge collapses that panel. The other panel expands to fill the space. Dragging away from the edge uncollapses the panel.
70
+ ### With Min/Max Constraints
88
71
 
89
72
  ```html
90
- <ixfx-split-layout collapsible layout="horizontal" style="width: 100%; height: 400px;">
91
- <div slot="a" style="background: var(--surface-3);">Top Panel</div>
92
- <div slot="b" style="background: var(--surface-5);">Bottom Panel</div>
73
+ <ixfx-split-layout direction="row" style="width: 100%; height: 400px;">
74
+ <ixfx-split-panel min="100px" max="70%">Left</ixfx-split-panel>
75
+ <ixfx-split-panel min="50px">Right</ixfx-split-panel>
93
76
  </ixfx-split-layout>
94
77
  ```
95
78
 
96
- ### Collapse Events
97
-
98
- ```javascript
99
- const split = document.querySelector('ixfx-split-layout[collapsible]');
100
-
101
- split.addEventListener('ixfx-split-collapsed', ({ detail }) => {
102
- console.log(`Panel ${detail.panel} is now ${detail.collapsed ? 'collapsed' : 'uncollapsed'}`);
103
- });
104
- ```
105
-
106
79
  ---
107
80
 
108
- ## Min/Max Size Constraints
81
+ ## When-Small Behaviour
82
+
83
+ The `when-small` attribute controls what happens when a panel is dragged past its minimum size:
109
84
 
110
- Use `minSizeA`, `maxSizeA`, `minSizeB`, and `maxSizeB` to constrain panel sizes:
85
+ - `prevent` (default) Stop at `min`. Cascade continues to next panel.
86
+ - `collapse` — If pointer travels past threshold, collapse the panel. Drag back to restore.
87
+ - `close` — If pointer travels past threshold, show closing affordance. On release, emit cancelable `ixfx-split-close`.
111
88
 
112
89
  ```html
113
- <ixfx-split-layout
114
- minSizeA="100px"
115
- maxSizeA="70%"
116
- minSizeB="50px"
117
- layout="horizontal"
118
- style="width: 100%; height: 400px;">
119
- <div slot="a" style="background: var(--surface-3);">Top Panel</div>
120
- <div slot="b" style="background: var(--surface-5);">Bottom Panel</div>
121
- </ixfx-split-layout>
90
+ <ixfx-split-panel when-small="collapse">Collapsible Panel</ixfx-split-panel>
122
91
  ```
123
92
 
124
- **Constraint rules:**
125
- - **Min constraints take precedence**: If both panels' minimums exceed 100% combined, they're scaled proportionally
126
- - **Max constraints are secondary**: If a max constraint conflicts with a min constraint, the max is ignored to satisfy the min
127
- - **The constrained panel gets clamped**: The other panel receives the remaining space
128
-
129
93
  ---
130
94
 
131
- ## Nested Splits with Two-Way Corner Resize
132
-
133
- When a `ixfx-split-layout` is nested inside slot A of an outer split, a corner zone appears at the intersection of the two split bars. This enables simultaneous resize of both splits.
134
-
135
- ```
136
- ┌─────────────────┬────────────┐
137
- │ │ │
138
- │ ┌─────────────┤ │
139
- │ │ Editor │ │
140
- │ │ │ │
141
- │ ├─────────────┤ Sidebar │
142
- │ │ Terminal │ │
143
- │ │ │ │
144
- │ └─────────────┘ │
145
- └─────────────────┴────────────┘
146
-
147
- Corner Zone
148
- (drag here for two-way resize)
149
- ```
95
+ ## Nested Splits
150
96
 
151
- ### Structure
97
+ Nested `<ixfx-split-layout>` elements create sub-groups:
152
98
 
153
99
  ```html
154
- <ixfx-split-layout layout="vertical" style="width: 100%; height: 400px;">
155
- <!-- Nested split is a direct child of slot A -->
156
- <ixfx-split-layout slot="a" layout="horizontal" style="width: 100%; height: 100%;">
157
- <div slot="a" style="background: var(--surface);">Editor</div>
158
- <div slot="b" style="background: var(--surface-3);">Terminal</div>
100
+ <ixfx-split-layout direction="row" style="width: 100%; height: 400px;">
101
+ <ixfx-split-layout direction="column" style="width: 100%; height: 100%;">
102
+ <ixfx-split-panel>Editor</ixfx-split-panel>
103
+ <ixfx-split-panel>Terminal</ixfx-split-panel>
159
104
  </ixfx-split-layout>
160
- <div slot="b" style="background: var(--surface-5);">Sidebar</div>
105
+ <ixfx-split-panel>Sidebar</ixfx-split-panel>
161
106
  </ixfx-split-layout>
162
107
  ```
163
108
 
164
- **Note**: For the corner zone to appear, the nested split must be a direct child of slot A (no wrapper elements between the split and slot A).
109
+ ---
165
110
 
166
- ### Corner Zone Behavior
111
+ ## Events
167
112
 
168
- - **Visual indicator**: A small magenta circle (12px) appears at the intersection point
169
- - **Cursor change**: Shows `nwse-resize` cursor when hovering over the corner zone (for vertical-outer/horizontal-inner splits)
170
- - **Two-way resize**: Dragging the corner zone adjusts both the outer vertical split and the inner horizontal split simultaneously
171
- - **Position tracking**: The corner zone follows the split bars as they move, staying at their intersection
113
+ | Name | Detail | Cancelable | When |
114
+ |---|---|---|---|
115
+ | `ixfx-split-resize` | `{ sizes: Record<string, number>; groupId: string }` | no | Drag end, `setSizes`, `collapse`/`expand`. |
116
+ | `ixfx-split-collapse` | `{ id: string; index: number }` | no | Panel collapsed (drag or API). |
117
+ | `ixfx-split-expand` | `{ id: string; index: number }` | no | Panel expanded. |
118
+ | `ixfx-split-close` | `{ id: string; index: number }` | **yes** | Before a panel is removed; `preventDefault()` vetoes. |
119
+ | `ixfx-split-change` | `{ layout: LayoutSnapshot }` | no | After any size/structure change (persistence hook). |
172
120
 
173
121
  ---
174
122
 
175
- ## CSS Customisation
123
+ ## CSS Custom Properties
176
124
 
177
- ### Handle Styling
125
+ | Variable | Default | Description |
126
+ |----------|---------|-------------|
127
+ | `--split-gutter-size` | `6px` | Width/height of gutters |
128
+ | `--split-gutter-color` | `transparent` | Gutter background |
129
+ | `--split-gutter-hover-color` | `var(--surface-6)` | Gutter on hover |
130
+ | `--split-gutter-active-color` | `var(--accent)` | Gutter while dragging |
131
+ | `--split-collapsed-size` | `0px` | Size of collapsed panels |
132
+ | `--split-snap` | `24px` | Snap/hysteresis distance |
133
+ | `--split-grab-color` | `var(--border-subtle-color)` | Grab handle dot colour |
134
+ | `--split-grab-dot-size` | `3px` | Grab handle dot size |
135
+ | `--split-grab-size` | `16px` | Grab handle length |
136
+ | `--split-grab-thickness` | `8px` | Grab handle thickness |
178
137
 
179
- ```css
180
- ixfx-split-layout {
181
- --handle-size: 4px;
182
- --handle-colour: var(--surface-4);
183
- --handle-hover-colour: var(--surface-6);
184
- --handle-active-colour: var(--accent);
185
- }
186
- ```
138
+ ---
187
139
 
188
- ### Grab Handle
140
+ ## Programmatic Control
189
141
 
190
- When `--handle-size` is greater than `5px`, a grab handle appears in the center of the split bar. The grab handle consists of a rectangle with three dots arranged perpendicular to the split direction:
142
+ ### Getting/Setting Sizes
191
143
 
192
- - **Vertical split**: Rectangle with 3 vertical dots (⋮)
193
- - **Horizontal split**: Rectangle with 3 horizontal dots ()
144
+ ```javascript
145
+ const split = document.querySelector('ixfx-split-layout');
194
146
 
195
- ```css
196
- ixfx-split-layout {
197
- --handle-bg: var(--handle-colour); /* Rectangle background */
198
- --handle-grab-colour: var(--surface-muted-text); /* Dot colour */
199
- --handle-grab-dot-size: 3px; /* Dot diameter */
200
- --handle-grab-size: 16px; /* Rectangle length */
201
- --handle-grab-thickness: 8px; /* Rectangle thickness */
202
- }
203
- ```
147
+ // Get all panel sizes
148
+ console.log(split.getSizes());
149
+ // { "p-1": 0.25, "p-2": 0.5, "p-3": 0.25 }
204
150
 
205
- ### Hide Collapsed Panels Completely
151
+ // Set sizes by array (positional)
152
+ split.setSizes([1, 2, 1]);
206
153
 
207
- ```css
208
- ixfx-split-layout[data-collapsed-a] slot[name="a"] {
209
- display: none;
210
- }
154
+ // Set sizes by object (id -> weight)
155
+ split.setSizes({ "my-panel": 0.3 });
211
156
  ```
212
157
 
213
- ---
158
+ ### Collapse/Expand
214
159
 
215
- ## Programmatic Control
160
+ ```javascript
161
+ // Collapse a panel
162
+ split.collapse('panel-id');
163
+
164
+ // Expand a panel
165
+ split.expand('panel-id');
216
166
 
217
- ### Setting Sizes
167
+ // Toggle
168
+ split.toggle('panel-id');
169
+ ```
170
+
171
+ ### Close Panel (Cancelable)
218
172
 
219
173
  ```javascript
220
- const split = document.querySelector('ixfx-split-layout');
174
+ split.addEventListener('ixfx-split-close', (e) => {
175
+ if (someCondition) {
176
+ e.preventDefault(); // Veto the close
177
+ }
178
+ });
221
179
 
222
- // Set to 30% / 70%
223
- split.sizeA = '30%';
224
- split.sizeB = '70%';
180
+ split.closePanel('panel-id');
181
+ ```
182
+
183
+ ### Manual Gutter Drags
225
184
 
226
- // Set to fixed pixel widths
227
- split.fixedA = true;
228
- split.sizeA = '200px'; // Panel A is 200px
229
- // Panel B automatically gets remaining space
185
+ ```javascript
186
+ // Bounding rects of the gutter elements, in gutter index order
187
+ split.getGutterRects();
230
188
 
231
- // Collapse a panel programmatically
232
- split.collapsedA = true;
233
- split.collapsedB = false;
189
+ // Start dragging gutter 0 as if the user pressed at (x, y); the drag then
190
+ // follows pointer `pointerId` until pointerup. Returns false when the index
191
+ // is out of range or the layout isn't ready.
192
+ split.beginGutterDrag(0, event.pointerId, event.clientX, event.clientY);
234
193
  ```
235
194
 
236
- ### Reading State
195
+ `beginGutterDrag` is meant for composite resize affordances — e.g. a corner
196
+ handle that starts this drag alongside perpendicular drags on other nested
197
+ layouts, resizing several splits with one gesture.
198
+
199
+ ### Layout Serialization
237
200
 
238
201
  ```javascript
239
- console.log('Panel A size:', split.sizeA);
240
- console.log('Panel B size:', split.sizeB);
241
- console.log('Is A collapsed?', split.collapsedA);
242
- console.log('Is B collapsed?', split.collapsedB);
202
+ // Get current layout
203
+ const layout = split.getLayout();
204
+ // { version: 1, root: { kind: 'group', id: 'g-0', children: [...] } }
205
+
206
+ // Restore a layout
207
+ split.setLayout(layout);
243
208
  ```
244
209
 
245
210
  ---
246
211
 
247
- ## Implementation Notes
212
+ ## Persistence
213
+
214
+ Set `storage-key` to enable automatic localStorage persistence:
215
+
216
+ ```html
217
+ <ixfx-split-layout storage-key="my-app-layout" ...>
218
+ ```
248
219
 
249
- - Uses Shadow DOM for style encapsulation
250
- - Slots use `display: block` and `overflow: hidden` by default
251
- - When no size is specified for a panel, it defaults to `auto` (fill remaining space)
252
- - When both sizes are empty, both default to `50%`
253
- - The split bar is `flex-shrink: 0` to prevent it from collapsing
254
- - Nested splits detect their parent through slot assignment and automatically enable corner resize
220
+ The layout is saved automatically after each change and restored on page load.
package/docs-user/tabs.md CHANGED
@@ -52,8 +52,42 @@ Manages tab selection and keyboard navigation.
52
52
  | `orientation` | `'horizontal' \| 'vertical-stack' \| 'vertical-side'` | `'horizontal'` | Layout direction |
53
53
  | `flip` | `boolean` | `false` | Flip indicator to opposite side |
54
54
  | `scrollable` | `boolean` | `false` | Enable scroll overflow. When set, vertical mousewheel on horizontal lists scrolls horizontally (and vice-versa for vertical lists). |
55
+ | `shrink` | `boolean` | `false` | When set, tabs size to content (intrinsic width for horizontal, intrinsic height for vertical) instead of stretching to fill the container |
55
56
  | `drag-mode` | `'off' \| 'reorder'` | `'off'` | Pointer-driven reordering within the list (sets `grab` cursor on items) |
56
57
 
58
+ ### Slots
59
+
60
+ | Slot | Description |
61
+ |-------------|-----------------------------------------------------------------------------|
62
+ | *(default)* | `ixfx-tab-list-item` elements. |
63
+ | `toolbar` | Toolbar items shown after the tabs. For `orientation="horizontal"` this is right-justified after the tabs and does not scroll with `scrollable`. Put an `ixfx-toolbar` in this slot to get automatic overflow handling. Plain buttons/elements work as well. |
64
+
65
+ Toolbar example (with overflow):
66
+
67
+ ```html
68
+ <ixfx-tab-list>
69
+ <ixfx-tab-list-item selected for="p1">Tab A</ixfx-tab-list-item>
70
+ <ixfx-tab-list-item for="p2">Tab B</ixfx-tab-list-item>
71
+
72
+ <ixfx-toolbar slot="toolbar" invisible-groups style="--toolbar-bg:transparent; --toolbar-border:none; --toolbar-padding:0;">
73
+ <ixfx-button>New</ixfx-button>
74
+ <ixfx-button>Save</ixfx-button>
75
+ <ixfx-button>Export</ixfx-button>
76
+ </ixfx-toolbar>
77
+ </ixfx-tab-list>
78
+ ```
79
+
80
+ For a simple toolbar without overflow, use plain buttons:
81
+
82
+ ```html
83
+ <ixfx-tab-list>
84
+ <ixfx-tab-list-item>Tab A</ixfx-tab-list-item>
85
+ <button slot="toolbar" type="button" class="tab-toolbar-btn">Action</button>
86
+ </ixfx-tab-list>
87
+ ```
88
+
89
+ When `orientation="vertical-stack"` / `vertical-side`, the toolbar slot appears after the tabs in the column direction (below the tabs). It is still outside the scrollable tab area.
90
+
57
91
  ### Events
58
92
 
59
93
  | Event | Detail | Description |
@@ -0,0 +1,102 @@
1
+ # Titlebar
2
+
3
+ Standalone titlebar chrome extracted from `ixfx-panel`: title text, icon, toolbar slot, close button and an optional collapse/expand caret. Use it when you need a panel-style header without the collapsible content wrapper (custom layouts, dock headers, section bars).
4
+
5
+ ```html
6
+ <ixfx-titlebar title="Settings" icon-name="settings" collapsible>
7
+ <div slot="toolbar">
8
+ <button type="button"><ixfx-icon name="printer"></ixfx-icon></button>
9
+ </div>
10
+ </ixfx-titlebar>
11
+ ```
12
+
13
+ `ixfx-panel` composes this component internally, so the two stay visually in sync.
14
+
15
+ ---
16
+
17
+ ## `ixfx-titlebar`
18
+
19
+ ### Properties / attributes
20
+
21
+ | Attribute / Property | Type | Default | Description |
22
+ |--------------------------|---------------------------------------------------|----------------|-----------------------------------------------------------------|
23
+ | `title` | string | `''` | Title text |
24
+ | `icon-name` | string | `''` | Icon name from the registry |
25
+ | `orientation` | `'horizontal'` \| `'vertical'` | `'horizontal'` | Layout direction of the bar |
26
+ | `variant` | `'titlebar'` \| `'midline'` | `'titlebar'` | Visual style (same variants as `ixfx-panel`) |
27
+ | `closable` | boolean | `false` | Shows a close button that fires `close` |
28
+ | `collapsible` | boolean | `false` | Shows the caret and enables toggle behaviour (opt-in) |
29
+ | `collapsed` | boolean | `false` | Collapsed state (controls caret rotation / label) |
30
+ | `toggle-interaction` | `'implicit'` \| `'explicit'` \| `'disabled'` | `'implicit'` | How users can toggle (only when `collapsible` is present) |
31
+
32
+ ### Collapsible vs toggle interaction
33
+
34
+ The caret is shown only when `collapsible` is present **and** `toggle-interaction` is not `disabled`. When `collapsible` is absent, `toggle-interaction` is inert and no `toggle` events fire.
35
+
36
+ | `collapsible` | `toggle-interaction` | Behaviour |
37
+ |---------------|----------------------|--------------------------------------------------------|
38
+ | absent | any | No caret, no user toggle |
39
+ | present | `implicit` | Clicking the caret or anywhere in the bar toggles |
40
+ | present | `explicit` | Only clicking the caret toggles |
41
+ | present | `disabled` | Caret hidden, no user toggle (programmatic only) |
42
+
43
+ Unlike `ixfx-panel`, toggling only flips the `collapsed` property and fires `toggle` — there is no content to hide. And unlike `ixfx-panel`, `close` does **not** remove the element; the host decides what closing means.
44
+
45
+ ### Slots
46
+
47
+ | Slot | Description |
48
+ |-------------|--------------------------------------------------|
49
+ | `toolbar` | Buttons or other elements right-aligned in the bar |
50
+
51
+ Clicking toolbar content never toggles, even with `toggle-interaction="implicit"`.
52
+
53
+ ### Events
54
+
55
+ | Event | Detail | Description |
56
+ |----------|------------------------------------|------------------------------------------|
57
+ | `toggle` | `{ collapsed: boolean }` | Fired when collapsed state changes |
58
+ | `close` | — | Fired when the close button is clicked |
59
+
60
+ ```js
61
+ const bar = document.querySelector('ixfx-titlebar');
62
+
63
+ bar.addEventListener('toggle', e => {
64
+ console.log('Collapsed:', e.detail.collapsed);
65
+ });
66
+
67
+ bar.addEventListener('close', () => {
68
+ console.log('Close requested');
69
+ });
70
+ ```
71
+
72
+ ### Icons
73
+
74
+ Icon names resolve against the icon registry. Register custom icons with `registerIcon(name, svgString)` from `src/icons/registry.ts`.
75
+
76
+ Built-in icon names: `check`, `chevron-down`, `chevron-right`, `close`.
77
+
78
+ ---
79
+
80
+ ## Visual variants
81
+
82
+ Same `titlebar` (default) and `midline` styles as `ixfx-panel`, including vertical orientation support:
83
+
84
+ ```html
85
+ <ixfx-titlebar title="Settings" variant="midline" icon-name="settings" collapsible></ixfx-titlebar>
86
+ <ixfx-titlebar title="Device" orientation="vertical" icon-name="server" collapsible></ixfx-titlebar>
87
+ ```
88
+
89
+ ---
90
+
91
+ ## TypeScript types
92
+
93
+ ```ts
94
+ import type { TitlebarOrientation, TitlebarVariant, TitlebarToggleInteraction, TitlebarToggleDetail } from 'ixfxfun-components';
95
+ ```
96
+
97
+ ---
98
+
99
+ ## See also
100
+
101
+ - [`ixfx-panel`](../panel/README.md) — Collapsible panel that composes this titlebar
102
+ - [`ixfx-icon`](../icons/README.md) — Icon component used by the titlebar