@kerfjs/ui 5.0.0-beta.54 → 5.0.0-beta.56

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 (274) hide show
  1. package/README.md +14 -11
  2. package/ai/compile-time-contracts-v1.json +2 -2
  3. package/ai/component-catalog-v2.json +206 -39
  4. package/ai/component-catalog.json +306 -58
  5. package/ai/public-api-signatures-v1.md +671 -92
  6. package/ai/skill.md +43 -38
  7. package/ai/webawesome-jsx-signatures-v1.md +8 -2
  8. package/dist/app-tab.js +1 -1
  9. package/dist/badge.d.ts +35 -7
  10. package/dist/badge.js +1 -1
  11. package/dist/browser/content-item.js +3 -0
  12. package/dist/browser/popup-menu.js +3 -0
  13. package/dist/browser/select.js +1 -0
  14. package/dist/catalog-resources.d.ts +12 -1
  15. package/dist/catalog.d.ts +21 -4
  16. package/dist/catalog.js +260 -308
  17. package/dist/catalog.js.map +1 -1
  18. package/dist/{chunk-7IUSV4XG.js → chunk-24PHKUR3.js} +2 -2
  19. package/dist/chunk-24PHKUR3.js.map +1 -0
  20. package/dist/{chunk-WZ2GLEY4.js → chunk-2G2ZJQYK.js} +2 -14
  21. package/dist/chunk-2G2ZJQYK.js.map +1 -0
  22. package/dist/{chunk-D7GWXIAS.js → chunk-5I7QGQVT.js} +1 -1
  23. package/dist/chunk-5I7QGQVT.js.map +1 -0
  24. package/dist/{chunk-HJKRCKDR.js → chunk-63ZWBGE3.js} +6 -3
  25. package/dist/chunk-63ZWBGE3.js.map +1 -0
  26. package/dist/{chunk-44IG7PFV.js → chunk-6CINWX2E.js} +3 -3
  27. package/dist/{chunk-44IG7PFV.js.map → chunk-6CINWX2E.js.map} +1 -1
  28. package/dist/{chunk-C2FAYFDE.js → chunk-AGXQQFKZ.js} +2 -2
  29. package/dist/{chunk-C2FAYFDE.js.map → chunk-AGXQQFKZ.js.map} +1 -1
  30. package/dist/{chunk-ATHXW6RP.js → chunk-AHEG27M7.js} +2 -2
  31. package/dist/{chunk-ATHXW6RP.js.map → chunk-AHEG27M7.js.map} +1 -1
  32. package/dist/{chunk-7GUQYHKA.js → chunk-AK67F6QN.js} +4 -1
  33. package/dist/chunk-AK67F6QN.js.map +1 -0
  34. package/dist/chunk-BFPBS5Y4.js +250 -0
  35. package/dist/chunk-BFPBS5Y4.js.map +1 -0
  36. package/dist/chunk-DG6G5EPF.js +35 -0
  37. package/dist/chunk-DG6G5EPF.js.map +1 -0
  38. package/dist/{chunk-MTM43SJO.js → chunk-G33XC2NB.js} +1 -1
  39. package/dist/chunk-G33XC2NB.js.map +1 -0
  40. package/dist/{chunk-TZ5IHWB2.js → chunk-K6UNVKDK.js} +3 -3
  41. package/dist/{chunk-TZ5IHWB2.js.map → chunk-K6UNVKDK.js.map} +1 -1
  42. package/dist/chunk-KH6VUGHU.js +399 -0
  43. package/dist/chunk-KH6VUGHU.js.map +1 -0
  44. package/dist/{chunk-HMQ2ZDGG.js → chunk-KROD7KZK.js} +3 -2
  45. package/dist/chunk-KROD7KZK.js.map +1 -0
  46. package/dist/{chunk-5NDQABWK.js → chunk-M7QV3IHH.js} +3 -1
  47. package/dist/chunk-M7QV3IHH.js.map +1 -0
  48. package/dist/{chunk-RNZAKSME.js → chunk-MA6Z6EIM.js} +3 -3
  49. package/dist/{chunk-RNZAKSME.js.map → chunk-MA6Z6EIM.js.map} +1 -1
  50. package/dist/chunk-MA73TA6D.js +42 -0
  51. package/dist/chunk-MA73TA6D.js.map +1 -0
  52. package/dist/{chunk-ZMBU522F.js → chunk-MEY33VWB.js} +2 -2
  53. package/dist/{chunk-ZMBU522F.js.map → chunk-MEY33VWB.js.map} +1 -1
  54. package/dist/{chunk-T22EM4ET.js → chunk-MOBRFZH3.js} +81 -31
  55. package/dist/chunk-MOBRFZH3.js.map +1 -0
  56. package/dist/chunk-MP2GDBQZ.js +50 -0
  57. package/dist/chunk-MP2GDBQZ.js.map +1 -0
  58. package/dist/{chunk-3NBL3OUQ.js → chunk-NAPE7WUS.js} +2 -2
  59. package/dist/chunk-NAPE7WUS.js.map +1 -0
  60. package/dist/{chunk-CMUIXJZM.js → chunk-NBYTIVHS.js} +2 -2
  61. package/dist/{chunk-CMUIXJZM.js.map → chunk-NBYTIVHS.js.map} +1 -1
  62. package/dist/chunk-O3EXIBEZ.js +114 -0
  63. package/dist/chunk-O3EXIBEZ.js.map +1 -0
  64. package/dist/{chunk-KPNTVB7A.js → chunk-PK4N5QTO.js} +8 -7
  65. package/dist/chunk-PK4N5QTO.js.map +1 -0
  66. package/dist/chunk-QQOTQTDD.js +415 -0
  67. package/dist/chunk-QQOTQTDD.js.map +1 -0
  68. package/dist/{chunk-BJJKFVWW.js → chunk-T4UZTNZ4.js} +5 -5
  69. package/dist/chunk-T4UZTNZ4.js.map +1 -0
  70. package/dist/chunk-V65EOWNF.js +83 -0
  71. package/dist/chunk-V65EOWNF.js.map +1 -0
  72. package/dist/{chunk-GUIHMIT2.js → chunk-VNPQDCQZ.js} +3 -3
  73. package/dist/{chunk-GUIHMIT2.js.map → chunk-VNPQDCQZ.js.map} +1 -1
  74. package/dist/{chunk-5I22LMCV.js → chunk-VZOFZPSO.js} +2 -2
  75. package/dist/chunk-VZOFZPSO.js.map +1 -0
  76. package/dist/chunk-WLG6SSNT.js +94 -0
  77. package/dist/chunk-WLG6SSNT.js.map +1 -0
  78. package/dist/{chunk-NE5JAOQS.js → chunk-WOUF7YXM.js} +3 -3
  79. package/dist/{chunk-NE5JAOQS.js.map → chunk-WOUF7YXM.js.map} +1 -1
  80. package/dist/collapsible-panel.d.ts +51 -13
  81. package/dist/collapsible-panel.js +38 -10
  82. package/dist/collapsible-panel.js.map +1 -1
  83. package/dist/content-item.d.ts +46 -0
  84. package/dist/content-item.js +4 -0
  85. package/dist/content-item.js.map +1 -0
  86. package/dist/css-values.d.ts +5 -11
  87. package/dist/css-values.js +1 -1
  88. package/dist/disclosure-arrow.d.ts +4 -1
  89. package/dist/disclosure-arrow.js +1 -1
  90. package/dist/floating-toolbar.d.ts +17 -6
  91. package/dist/floating-toolbar.js +1 -1
  92. package/dist/grid.js +1 -1
  93. package/dist/help-tags-register.d.ts +13 -0
  94. package/dist/help-tags-register.js +9 -0
  95. package/dist/help-tags-register.js.map +1 -0
  96. package/dist/index.d.ts +7 -5
  97. package/dist/index.js +31 -29
  98. package/dist/list-action-row.js +1 -1
  99. package/dist/list-header.d.ts +14 -1
  100. package/dist/list-header.js +1 -1
  101. package/dist/list-inset-control.d.ts +1 -1
  102. package/dist/list-inset-control.js +1 -1
  103. package/dist/list-inset-text.d.ts +9 -10
  104. package/dist/list-inset-text.js +2 -1
  105. package/dist/list-item.js +1 -1
  106. package/dist/list.d.ts +10 -3
  107. package/dist/list.js +1 -1
  108. package/dist/nav-stack.d.ts +38 -5
  109. package/dist/nav-stack.js +4 -1
  110. package/dist/pane.d.ts +20 -2
  111. package/dist/pane.js +1 -1
  112. package/dist/panel-toolbar-C8sF-YZC.d.ts +63 -0
  113. package/dist/popup-menu-register.d.ts +9 -0
  114. package/dist/popup-menu-register.js +31 -0
  115. package/dist/popup-menu-register.js.map +1 -0
  116. package/dist/popup-menu.d.ts +71 -0
  117. package/dist/popup-menu.js +3 -0
  118. package/dist/popup-menu.js.map +1 -0
  119. package/dist/row.d.ts +1 -1
  120. package/dist/row.js +1 -1
  121. package/dist/select-register.js +70 -3
  122. package/dist/select-register.js.map +1 -1
  123. package/dist/select.d.ts +46 -8
  124. package/dist/select.js +2 -1
  125. package/dist/{divider-sides-BzB6rphT.d.ts → sides-BPSWde0A.d.ts} +1 -3
  126. package/dist/skeleton.js +1 -1
  127. package/dist/spacer.js +1 -1
  128. package/dist/split-view.d.ts +31 -3
  129. package/dist/split-view.js +33 -5
  130. package/dist/split-view.js.map +1 -1
  131. package/dist/state-banner.js +1 -1
  132. package/dist/styles/badge.css +13 -0
  133. package/dist/styles/catalog/components/catalog-example.css +1 -17
  134. package/dist/styles/catalog/components/catalog-stage.css +9 -14
  135. package/dist/styles/catalog/components/catalog.css +8 -18
  136. package/dist/styles/catalog.css +0 -5
  137. package/dist/styles/collapsible-panel.css +192 -39
  138. package/dist/styles/content-item.css +30 -0
  139. package/dist/styles/floating-toolbar.css +5 -0
  140. package/dist/styles/foundation.css +28 -19
  141. package/dist/styles/layout.css +1 -21
  142. package/dist/styles/list-action-row.css +13 -1
  143. package/dist/styles/list-inset-text.css +1 -2
  144. package/dist/styles/list-item.css +14 -1
  145. package/dist/styles/list.css +1 -1
  146. package/dist/styles/nav-stack.css +29 -64
  147. package/dist/styles/pane.css +33 -1
  148. package/dist/styles/popup-menu.css +90 -0
  149. package/dist/styles/resizable-region.css +53 -10
  150. package/dist/styles/select.css +39 -12
  151. package/dist/styles/split-view.css +21 -25
  152. package/dist/styles/state-banner.css +13 -4
  153. package/dist/styles/styles.css +1 -0
  154. package/dist/styles/tab-bar.css +31 -3
  155. package/dist/styles/tab-scaffold.css +36 -14
  156. package/dist/styles/text.css +1 -1
  157. package/dist/styles/token-search-field.css +4 -4
  158. package/dist/styles/toolbar-control-group.css +44 -12
  159. package/dist/styles/toolbar.css +57 -7
  160. package/dist/styles/value-table.css +19 -2
  161. package/dist/styles/webawesome.css +3 -1
  162. package/dist/styles/workbench.css +165 -31
  163. package/dist/tab-scaffold.d.ts +35 -1
  164. package/dist/tab-scaffold.js +50 -16
  165. package/dist/tab-scaffold.js.map +1 -1
  166. package/dist/text.js +1 -1
  167. package/dist/token-search-field.d.ts +13 -2
  168. package/dist/token-search-field.js +1 -1
  169. package/dist/toolbar-text.js +1 -1
  170. package/dist/toolbar.d.ts +12 -4
  171. package/dist/toolbar.js +1 -1
  172. package/dist/{types-CEsieBZi.d.ts → types-DXAGogOS.d.ts} +48 -0
  173. package/dist/value-table.js +1 -1
  174. package/dist/webawesome.d.ts +7 -1
  175. package/dist/wire-catalog.d.ts +24 -1
  176. package/dist/wire-catalog.js +26 -3
  177. package/dist/wire-catalog.js.map +1 -1
  178. package/dist/wire-sidebar.js +6 -3
  179. package/dist/wire-sidebar.js.map +1 -1
  180. package/dist/wire-token-search-fields.js +1 -1
  181. package/dist/wire-workbench.js +1 -350
  182. package/dist/wire-workbench.js.map +1 -1
  183. package/dist/workbench.d.ts +144 -6
  184. package/dist/workbench.js +11 -208
  185. package/dist/workbench.js.map +1 -1
  186. package/docs/accessibility.md +126 -6
  187. package/docs/app-layouts.md +9 -7
  188. package/docs/catalog.md +41 -9
  189. package/docs/collapsible-panel.md +79 -5
  190. package/docs/component-contract.md +38 -12
  191. package/docs/component-selection.md +64 -55
  192. package/docs/design/templates/empty-state/with-action-dark.svg +1 -1
  193. package/docs/design/templates/empty-state/with-action.svg +1 -1
  194. package/docs/design/templates/empty-state-dark.svg +1 -1
  195. package/docs/design/templates/empty-state.svg +1 -1
  196. package/docs/design/templates/list/compact-dark.svg +1 -1
  197. package/docs/design/templates/list/compact.svg +1 -1
  198. package/docs/design/templates/list/stack-dark.svg +1 -1
  199. package/docs/design/templates/list/stack.svg +1 -1
  200. package/docs/design/templates/list-action-row/default-dark.svg +1 -1
  201. package/docs/design/templates/list-action-row/default.svg +1 -1
  202. package/docs/design/templates/list-action-row/selected-dark.svg +1 -1
  203. package/docs/design/templates/list-action-row/selected.svg +1 -1
  204. package/docs/design/templates/list-action-row-dark.svg +1 -1
  205. package/docs/design/templates/list-action-row.svg +1 -1
  206. package/docs/design/templates/list-dark.svg +1 -1
  207. package/docs/design/templates/list-item/default-dark.svg +1 -1
  208. package/docs/design/templates/list-item/default.svg +1 -1
  209. package/docs/design/templates/list-item/selected-dark.svg +1 -1
  210. package/docs/design/templates/list-item/selected.svg +1 -1
  211. package/docs/design/templates/list-item/trailing-dark.svg +1 -1
  212. package/docs/design/templates/list-item/trailing.svg +1 -1
  213. package/docs/design/templates/list-item-dark.svg +1 -1
  214. package/docs/design/templates/list-item.svg +1 -1
  215. package/docs/design/templates/list.svg +1 -1
  216. package/docs/design/templates/state-banner/danger-dark.svg +1 -1
  217. package/docs/design/templates/state-banner/danger.svg +1 -1
  218. package/docs/design/templates/state-banner/info-dark.svg +1 -1
  219. package/docs/design/templates/state-banner/info.svg +1 -1
  220. package/docs/design/templates/state-banner/success-dark.svg +1 -1
  221. package/docs/design/templates/state-banner/success.svg +1 -1
  222. package/docs/design/templates/state-banner/warning-dark.svg +1 -1
  223. package/docs/design/templates/state-banner/warning.svg +1 -1
  224. package/docs/design/templates/state-banner-dark.svg +1 -1
  225. package/docs/design/templates/state-banner.svg +1 -1
  226. package/docs/design/templates/value-table/details-dark.svg +1 -1
  227. package/docs/design/templates/value-table/details.svg +1 -1
  228. package/docs/design/templates/value-table-dark.svg +1 -1
  229. package/docs/design/templates/value-table.svg +1 -1
  230. package/docs/design-philosophy.md +1 -1
  231. package/docs/examples/command-palette-adapter.tsx +3 -2
  232. package/docs/layout.md +74 -29
  233. package/docs/nav-stack.md +45 -1
  234. package/docs/recipes.md +48 -40
  235. package/docs/split-view.md +46 -2
  236. package/docs/surface-scaffold.md +1 -1
  237. package/docs/tab-scaffold.md +50 -1
  238. package/docs/type-contracts.md +17 -17
  239. package/docs/ux-demo.md +8 -4
  240. package/docs/webawesome-theme.md +1 -2
  241. package/docs/workbench.md +131 -10
  242. package/llms.txt +38 -9
  243. package/package.json +24 -2
  244. package/ux-demo/recipes/app-shell.tsx +141 -223
  245. package/ux-demo/recipes/collapsible-sidebar.tsx +47 -77
  246. package/ux-demo/recipes/list-detail-dialog.tsx +150 -54
  247. package/ux-demo/recipes/mount-recipe.ts +0 -5
  248. package/ux-demo/recipes/navigation-sidebar.tsx +3 -2
  249. package/ux-demo/recipes/navigation-stack.tsx +3 -2
  250. package/ux-demo/recipes/types.ts +0 -1
  251. package/dist/chunk-3NBL3OUQ.js.map +0 -1
  252. package/dist/chunk-5I22LMCV.js.map +0 -1
  253. package/dist/chunk-5NDQABWK.js.map +0 -1
  254. package/dist/chunk-7GUQYHKA.js.map +0 -1
  255. package/dist/chunk-7IUSV4XG.js.map +0 -1
  256. package/dist/chunk-BJJKFVWW.js.map +0 -1
  257. package/dist/chunk-D7GWXIAS.js.map +0 -1
  258. package/dist/chunk-DQA5FMOX.js +0 -85
  259. package/dist/chunk-DQA5FMOX.js.map +0 -1
  260. package/dist/chunk-HJKRCKDR.js.map +0 -1
  261. package/dist/chunk-HMQ2ZDGG.js.map +0 -1
  262. package/dist/chunk-KPNTVB7A.js.map +0 -1
  263. package/dist/chunk-MTM43SJO.js.map +0 -1
  264. package/dist/chunk-T22EM4ET.js.map +0 -1
  265. package/dist/chunk-WZ2GLEY4.js.map +0 -1
  266. package/dist/chunk-XD7DWYXG.js +0 -34
  267. package/dist/chunk-XD7DWYXG.js.map +0 -1
  268. package/dist/chunk-ZK5EGVFP.js +0 -27
  269. package/dist/chunk-ZK5EGVFP.js.map +0 -1
  270. package/dist/styles/catalog/components/catalog-detail.css +0 -35
  271. package/dist/styles/catalog/components/catalog-resource-footer.css +0 -37
  272. package/dist/styles/catalog/components/catalog-secondary-sections.css +0 -38
  273. package/dist/styles/catalog/components/catalog-section-list.css +0 -22
  274. package/dist/styles/catalog/components/catalog-sidebar.css +0 -82
@@ -1,32 +1,22 @@
1
1
  import '@kerfjs/ui/layout.css';
2
+ import '@kerfjs/ui/workbench.css';
2
3
 
3
- import { deviceClass } from '@kerfjs/ui/device-class';
4
- import { Grid } from '@kerfjs/ui/grid';
5
4
  import { List } from '@kerfjs/ui/list';
6
- import { ListHeader } from '@kerfjs/ui/list-header';
7
5
  import { ListItem } from '@kerfjs/ui/list-item';
8
6
  import { LucideIcon } from '@kerfjs/ui/lucide-icon';
9
- import { Pane, type PaneSeparatorSide } from '@kerfjs/ui/pane';
10
- import { ResizableRegion } from '@kerfjs/ui/resizable-region';
11
- import { Row } from '@kerfjs/ui/row';
12
7
  import { Toolbar } from '@kerfjs/ui/toolbar';
13
8
  import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
14
9
  import { ToolbarText } from '@kerfjs/ui/toolbar-text';
15
10
  import { ValueTable, ValueTableRow } from '@kerfjs/ui/value-table';
16
- import { signal } from 'kerfjs';
11
+ import { wireWorkbench } from '@kerfjs/ui/wire-workbench';
12
+ import { Workbench } from '@kerfjs/ui/workbench';
13
+ import { delegate, signal } from 'kerfjs';
17
14
  import { Bell, Folder, Inbox, Settings } from 'lucide';
18
15
 
19
16
  import type { RecipeFactory, RecipePresentation } from './types.js';
20
17
 
21
- type ShellPane = 'content' | 'navigation' | 'inspector';
22
- type ScreenEdges = readonly PaneSeparatorSide[];
23
-
24
- // Safe areas: the app bar claims the top edge and both sides, so it clears the
25
- // status area. Each pane below compensates only for the screen edges it
26
- // actually reaches: the bottom, plus its outer side in the three-pane layout or
27
- // both sides when it is the only pane shown.
28
- const appBarEdges: ScreenEdges = ['block-start', 'inline-start', 'inline-end'];
29
- const soleEdges: ScreenEdges = ['block-end', 'inline-start', 'inline-end'];
18
+ const NAVIGATION_ACTION = 'recipe-shell-toggle-navigation';
19
+ const INSPECTOR_ACTION = 'recipe-shell-toggle-inspector';
30
20
 
31
21
  const tasks = [
32
22
  ['Release accessibility audit', 'Interface systems · due this week'],
@@ -44,46 +34,19 @@ export const presentation: RecipePresentation = {
44
34
  overflow: 'hidden',
45
35
  shadow: true,
46
36
  },
47
- note: 'The recipe owns pane geometry and one scroll owner per pane. The app owns routing, data, pane visibility, sizes, and persistence.',
37
+ note: 'The recipe is a Workbench under an app bar: resizable navigation and inspector rails beside the task list, each rail toggled by its standard control, which moves into the work-area toolbar while the rail is hidden. On a small screen the rails overlay the task list one at a time. The app owns routing, data, rail visibility, sizes, and persistence.',
48
38
  };
49
39
 
50
40
  export const createRecipe: RecipeFactory = (announce) => {
51
41
  const selected = signal('inbox');
52
42
  const navigationSize = signal(224);
53
43
  const inspectorSize = signal(240);
54
- const responsivePane = signal<ShellPane>('content');
55
- // Desktop-class viewports show all three panes; smaller ones show one pane
56
- // at a time and let the toolbar switch between them.
57
- const device = deviceClass();
58
-
59
- const paneButton = (
60
- pane: ShellPane,
61
- label: string,
62
- icon: typeof Folder,
63
- iconName: string,
64
- ) => (
65
- <button
66
- type="button"
67
- aria-label={label}
68
- aria-controls={`recipe-shell-${pane}`}
69
- aria-pressed={String(responsivePane.value === pane)}
70
- data-action="recipe-action"
71
- data-recipe-command={`show-${pane}`}
72
- >
73
- <LucideIcon icon={icon} name={iconName} />
74
- </button>
75
- );
44
+ const navigationCollapsed = signal(false);
45
+ const inspectorCollapsed = signal(false);
76
46
 
77
- const navigation = (edges: ScreenEdges) => (
78
- <Pane
79
- element="aside"
80
- safeAreaEdges={edges}
81
- id="recipe-shell-navigation"
82
- contentElement="nav"
83
- contentLabel="Workspace"
84
- >
85
- <section>
86
- <ListHeader label="Workspace" />
47
+ const navigation = () => (
48
+ <nav aria-label="Workspace">
49
+ <List gap="2xs">
87
50
  <ListItem
88
51
  action="recipe-action"
89
52
  itemId="inbox"
@@ -99,204 +62,159 @@ export const createRecipe: RecipeFactory = (announce) => {
99
62
  selected={selected.value === 'projects'}
100
63
  multiline
101
64
  />
102
- </section>
103
- </Pane>
104
- );
105
-
106
- const content = (edges: ScreenEdges) => (
107
- <Pane
108
- element="main"
109
- safeAreaEdges={edges}
110
- id="recipe-shell-content"
111
- header={
112
- <Toolbar
113
- label="Current workspace view"
114
- dividerSides=""
115
- leading={
116
- <ToolbarText
117
- text={
118
- selected.value === 'inbox' ? 'Inbox triage' : 'Active projects'
119
- }
120
- size="xlarge"
121
- id="recipe-shell-main-title"
122
- />
123
- }
124
- trailing={
125
- <ToolbarControlGroup appearance="borderless" content="text" single>
126
- <button
127
- type="button"
128
- data-action="recipe-action"
129
- data-recipe-command="new"
130
- >
131
- New task
132
- </button>
133
- </ToolbarControlGroup>
134
- }
135
- />
136
- }
137
- >
138
- <section aria-label="Tasks">
139
- {tasks.map(([title, detail]) => (
140
- <ListItem
141
- action="recipe-action"
142
- label={title}
143
- description={detail}
144
- multiline
145
- rootAttributes={{ 'data-recipe-command': 'open-task' }}
146
- />
147
- ))}
148
- </section>
149
- </Pane>
150
- );
151
-
152
- const inspector = (edges: ScreenEdges) => (
153
- <Pane
154
- element="aside"
155
- safeAreaEdges={edges}
156
- id="recipe-shell-inspector"
157
- header={
158
- <Toolbar
159
- label="Inspector"
160
- dividerSides=""
161
- leading={
162
- <ToolbarText
163
- text="Inspector"
164
- size="xlarge"
165
- id="recipe-shell-inspector-title"
166
- />
167
- }
168
- />
169
- }
170
- >
171
- <ValueTable label="Selected task">
172
- <ValueTableRow label="Status" value="In review" />
173
- <ValueTableRow label="Owner" value="Mara Chen" />
174
- <ValueTableRow label="Priority" value="High" />
175
- </ValueTable>
176
- </Pane>
65
+ </List>
66
+ </nav>
177
67
  );
178
68
 
179
- const render = () => {
180
- const onePane = !device.value.atLeast('desktop');
181
- return (
182
- <List
183
- fill
184
- rootAttributes={{
185
- 'data-recipe': 'recipe-app-shell',
186
- 'data-responsive-pane': onePane ? responsivePane.value : 'all',
187
- }}
188
- >
189
- <Toolbar
190
- label="Atlas workspace"
191
- safeAreaEdges={appBarEdges}
192
- leading={<ToolbarText text="Atlas" />}
193
- trailing={
194
- <>
195
- {onePane ? (
196
- <ToolbarControlGroup
197
- appearance="borderless"
198
- buttonAppearance="push"
199
- label="Workspace panes"
200
- >
201
- {paneButton(
202
- 'navigation',
203
- 'Show navigation',
204
- Folder,
205
- 'folder',
206
- )}
207
- {paneButton('content', 'Show content', Inbox, 'inbox')}
208
- {paneButton(
209
- 'inspector',
210
- 'Show inspector',
211
- Settings,
212
- 'settings',
213
- )}
214
- </ToolbarControlGroup>
215
- ) : null}
69
+ const render = () => (
70
+ <List fill rootAttributes={{ 'data-recipe': 'recipe-app-shell' }}>
71
+ <Toolbar
72
+ label="Atlas workspace"
73
+ safeAreaEdges={['block-start', 'inline-start', 'inline-end']}
74
+ dividerSides="b"
75
+ leading={<ToolbarText text="Atlas" />}
76
+ trailing={
77
+ <ToolbarControlGroup
78
+ appearance="borderless"
79
+ label="Workspace controls"
80
+ >
81
+ <button
82
+ type="button"
83
+ aria-label="Notifications"
84
+ data-action="recipe-action"
85
+ data-recipe-command="notify"
86
+ >
87
+ <LucideIcon icon={Bell} name="bell" />
88
+ </button>
89
+ <button
90
+ type="button"
91
+ aria-label="Settings"
92
+ data-action="recipe-action"
93
+ data-recipe-command="settings"
94
+ >
95
+ <LucideIcon icon={Settings} name="settings" />
96
+ </button>
97
+ </ToolbarControlGroup>
98
+ }
99
+ />
100
+ <List flex>
101
+ <Workbench
102
+ id="recipe-shell"
103
+ label="Atlas workspace panes"
104
+ leftRail={{
105
+ label: 'Navigation',
106
+ size: navigationSize.value,
107
+ resizable: { min: 180, max: 320 },
108
+ collapsed: navigationCollapsed.value,
109
+ toolbar: {
110
+ label: 'Navigation',
111
+ title: <ToolbarText text="Workspace" />,
112
+ toggle: { action: NAVIGATION_ACTION, name: 'navigation' },
113
+ },
114
+ content: navigation(),
115
+ }}
116
+ mainToolbar={{
117
+ label: 'Current workspace view',
118
+ title: (
119
+ <ToolbarText
120
+ text={
121
+ selected.value === 'inbox'
122
+ ? 'Inbox triage'
123
+ : 'Active projects'
124
+ }
125
+ size="xlarge"
126
+ id="recipe-shell-main-title"
127
+ />
128
+ ),
129
+ responsive: 'wrap',
130
+ trailing: (
216
131
  <ToolbarControlGroup
217
132
  appearance="borderless"
218
- label="Workspace controls"
133
+ content="text"
134
+ single
219
135
  >
220
136
  <button
221
137
  type="button"
222
- aria-label="Notifications"
223
138
  data-action="recipe-action"
224
- data-recipe-command="notify"
139
+ data-recipe-command="new"
225
140
  >
226
- <LucideIcon icon={Bell} name="bell" />
227
- </button>
228
- <button
229
- type="button"
230
- aria-label="Settings"
231
- data-action="recipe-action"
232
- data-recipe-command="settings"
233
- >
234
- <LucideIcon icon={Settings} name="settings" />
141
+ New task
235
142
  </button>
236
143
  </ToolbarControlGroup>
237
- </>
144
+ ),
145
+ }}
146
+ main={
147
+ <section aria-label="Tasks">
148
+ {tasks.map(([title, detail]) => (
149
+ <ListItem
150
+ action="recipe-action"
151
+ label={title}
152
+ description={detail}
153
+ multiline
154
+ rootAttributes={{ 'data-recipe-command': 'open-task' }}
155
+ />
156
+ ))}
157
+ </section>
238
158
  }
159
+ rightRail={{
160
+ label: 'Inspector',
161
+ size: inspectorSize.value,
162
+ resizable: { min: 200, max: 360 },
163
+ collapsed: inspectorCollapsed.value,
164
+ toolbar: {
165
+ label: 'Inspector',
166
+ title: <ToolbarText text="Inspector" />,
167
+ toggle: { action: INSPECTOR_ACTION, name: 'inspector' },
168
+ },
169
+ content: (
170
+ <ValueTable label="Selected task">
171
+ <ValueTableRow label="Status" value="In review" />
172
+ <ValueTableRow label="Owner" value="Mara Chen" />
173
+ <ValueTableRow label="Priority" value="High" />
174
+ </ValueTable>
175
+ ),
176
+ }}
239
177
  />
240
- {onePane ? (
241
- <Grid columns={1} gap="none" flex>
242
- {responsivePane.value === 'navigation'
243
- ? navigation(soleEdges)
244
- : responsivePane.value === 'inspector'
245
- ? inspector(soleEdges)
246
- : content(soleEdges)}
247
- </Grid>
248
- ) : (
249
- <Row gap="none" flex>
250
- <ResizableRegion
251
- id="recipe-navigation"
252
- label="Navigation"
253
- size={navigationSize.value}
254
- min={180}
255
- max={320}
256
- >
257
- {navigation(['block-end', 'inline-start'])}
258
- </ResizableRegion>
259
- <Grid columns={1} gap="none" flex>
260
- {content(['block-end'])}
261
- </Grid>
262
- <ResizableRegion
263
- id="recipe-inspector"
264
- label="Inspector"
265
- size={inspectorSize.value}
266
- min={200}
267
- max={360}
268
- edge="start"
269
- >
270
- {inspector(['block-end', 'inline-end'])}
271
- </ResizableRegion>
272
- </Row>
273
- )}
274
178
  </List>
275
- );
276
- };
179
+ </List>
180
+ );
181
+
277
182
  return {
278
183
  render,
279
184
  action(command, element) {
280
- const pane = command.match(
281
- /^show-(navigation|content|inspector)$/,
282
- )?.[1] as ShellPane | undefined;
283
- if (pane) responsivePane.value = pane;
284
185
  const id = element.dataset.itemId;
285
186
  if (id) selected.value = id;
286
187
  announce(
287
188
  id
288
189
  ? `Opened ${id}`
289
- : pane
290
- ? `Showing ${pane}`
291
- : command === 'new'
292
- ? 'New task requested'
293
- : `${command} requested`,
190
+ : command === 'new'
191
+ ? 'New task requested'
192
+ : `${command} requested`,
294
193
  );
295
194
  },
296
- resize(id, size) {
297
- if (id === 'recipe-navigation') navigationSize.value = size;
298
- if (id === 'recipe-inspector') inspectorSize.value = size;
299
- announce(`${id} resized to ${size}px`);
195
+ wire(root) {
196
+ const toggle = (action: string, collapsed: typeof navigationCollapsed) =>
197
+ delegate(root, 'click', `[data-action="${action}"]`, () => {
198
+ collapsed.value = !collapsed.value;
199
+ });
200
+ const stops = [
201
+ toggle(NAVIGATION_ACTION, navigationCollapsed),
202
+ toggle(INSPECTOR_ACTION, inspectorCollapsed),
203
+ wireWorkbench(root, {
204
+ id: 'recipe-shell',
205
+ panels: {
206
+ leftRail: { size: navigationSize, collapsed: navigationCollapsed },
207
+ rightRail: { size: inspectorSize, collapsed: inspectorCollapsed },
208
+ },
209
+ onResize: ({ panel, size }) =>
210
+ announce(
211
+ `${panel === 'leftRail' ? 'Navigation' : 'Inspector'} resized to ${size}px`,
212
+ ),
213
+ }),
214
+ ];
215
+ return () => {
216
+ for (const stop of stops) stop();
217
+ };
300
218
  },
301
219
  };
302
220
  };
@@ -4,8 +4,10 @@ import '@kerfjs/ui/floating-toolbar.css';
4
4
 
5
5
  import {
6
6
  CollapsiblePanel,
7
- CollapsiblePanelToggle,
7
+ CollapsiblePanelRelocated,
8
+ type CollapsiblePanelToolbar,
8
9
  } from '@kerfjs/ui/collapsible-panel';
10
+ import { ContentItem } from '@kerfjs/ui/content-item';
9
11
  import { deviceClass } from '@kerfjs/ui/device-class';
10
12
  import { FloatingToolbar } from '@kerfjs/ui/floating-toolbar';
11
13
  import { Grid } from '@kerfjs/ui/grid';
@@ -17,7 +19,6 @@ import { Pane } from '@kerfjs/ui/pane';
17
19
  import { Row } from '@kerfjs/ui/row';
18
20
  import { Text } from '@kerfjs/ui/text';
19
21
  import { Toolbar } from '@kerfjs/ui/toolbar';
20
- import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
21
22
  import { ToolbarText } from '@kerfjs/ui/toolbar-text';
22
23
  import { ValueTable, ValueTableRow } from '@kerfjs/ui/value-table';
23
24
  import { type SidebarStorage, wireSidebar } from '@kerfjs/ui/wire-sidebar';
@@ -73,24 +74,20 @@ export const createRecipe: RecipeFactory = (announce) => {
73
74
  const drawerCollapsed = signal(true);
74
75
  const storage = memoryStorage();
75
76
 
76
- const railToggle = () => (
77
- <CollapsiblePanelToggle
78
- side="left"
79
- collapsed={railCollapsed.value}
80
- action={RAIL_ACTION}
81
- panelId="sidebar-rail"
82
- label={railCollapsed.value ? 'Show navigation' : 'Hide navigation'}
83
- />
84
- );
85
- const drawerToggle = () => (
86
- <CollapsiblePanelToggle
87
- side="bottom"
88
- collapsed={drawerCollapsed.value}
89
- action={DRAWER_ACTION}
90
- panelId="sidebar-console"
91
- label={drawerCollapsed.value ? 'Show activity' : 'Hide activity'}
92
- />
93
- );
77
+ // Each panel composes its own toolbar with the standard toggle last. While a
78
+ // panel is collapsed, CollapsiblePanelRelocated carries that toggle: the
79
+ // rail's leads the main toolbar, the drawer's floats in its container's
80
+ // bottom-end corner. wireSidebar hands focus between the two positions.
81
+ const railToolbar: CollapsiblePanelToolbar = {
82
+ label: 'Workspace navigation',
83
+ title: <ToolbarText text="Atlas" />,
84
+ toggle: { action: RAIL_ACTION, name: 'navigation' },
85
+ };
86
+ const drawerToolbar: CollapsiblePanelToolbar = {
87
+ label: 'Activity',
88
+ title: <ToolbarText text="Activity" size="small" />,
89
+ toggle: { action: DRAWER_ACTION, name: 'activity' },
90
+ };
94
91
 
95
92
  const rail = () => (
96
93
  <CollapsiblePanel
@@ -99,23 +96,9 @@ export const createRecipe: RecipeFactory = (announce) => {
99
96
  size={232}
100
97
  collapsed={railCollapsed.value}
101
98
  label="Workspace navigation"
99
+ toolbar={railToolbar}
102
100
  >
103
- <Pane
104
- contentElement="nav"
105
- contentLabel="Workspace"
106
- header={
107
- <Toolbar
108
- label="Workspace navigation"
109
- dividerSides=""
110
- leading={<ToolbarText text="Atlas" />}
111
- trailing={
112
- <ToolbarControlGroup appearance="borderless" single>
113
- {railToggle()}
114
- </ToolbarControlGroup>
115
- }
116
- />
117
- }
118
- >
101
+ <nav aria-label="Workspace">
119
102
  <section>
120
103
  <ListHeader label="Workspace" />
121
104
  <ListItem
@@ -141,15 +124,10 @@ export const createRecipe: RecipeFactory = (announce) => {
141
124
  selected={selected.value === 'shared'}
142
125
  />
143
126
  </section>
144
- </Pane>
127
+ </nav>
145
128
  </CollapsiblePanel>
146
129
  );
147
130
 
148
- // Each panel's own header holds its collapse toggle; its expand toggle shows
149
- // only while it is collapsed, so exactly one control owns each action. The
150
- // rail's sits in the main header; the drawer's is the drawer's own
151
- // `restoreControl`, which the panel floats in its container's bottom-end
152
- // corner. wireSidebar hands focus between them.
153
131
  const main = () => (
154
132
  <Pane
155
133
  element="main"
@@ -159,11 +137,12 @@ export const createRecipe: RecipeFactory = (announce) => {
159
137
  dividerSides=""
160
138
  leading={
161
139
  <>
162
- {railCollapsed.value ? (
163
- <ToolbarControlGroup appearance="borderless" single>
164
- {railToggle()}
165
- </ToolbarControlGroup>
166
- ) : null}
140
+ <CollapsiblePanelRelocated
141
+ panelId="sidebar-rail"
142
+ side="left"
143
+ collapsed={railCollapsed.value}
144
+ toolbar={railToolbar}
145
+ />
167
146
  <ToolbarText
168
147
  text={labels[selected.value] ?? 'Inbox'}
169
148
  size="xlarge"
@@ -174,7 +153,7 @@ export const createRecipe: RecipeFactory = (announce) => {
174
153
  />
175
154
  }
176
155
  >
177
- <div class="kui-content-item">
156
+ <ContentItem>
178
157
  <List gap="2xs">
179
158
  <Text variant="span">
180
159
  <strong>Narrow the window to a compact width</strong>
@@ -184,7 +163,7 @@ export const createRecipe: RecipeFactory = (announce) => {
184
163
  a backdrop, Escape, and a trapped Tab ring, all managed by the wire.
185
164
  </Text>
186
165
  </List>
187
- </div>
166
+ </ContentItem>
188
167
  </Pane>
189
168
  );
190
169
 
@@ -195,39 +174,30 @@ export const createRecipe: RecipeFactory = (announce) => {
195
174
  size={168}
196
175
  collapsed={drawerCollapsed.value}
197
176
  label="Activity"
177
+ toolbar={drawerToolbar}
198
178
  restoreControl={
199
179
  <FloatingToolbar label="Activity drawer">
200
- <ToolbarControlGroup single>{drawerToggle()}</ToolbarControlGroup>
180
+ <CollapsiblePanelRelocated
181
+ panelId="sidebar-console"
182
+ side="bottom"
183
+ collapsed={drawerCollapsed.value}
184
+ toolbar={drawerToolbar}
185
+ />
201
186
  </FloatingToolbar>
202
187
  }
203
188
  >
204
- <Pane
205
- header={
206
- <Toolbar
207
- label="Activity"
208
- dividerSides=""
209
- leading={<ToolbarText text="Activity" size="small" />}
210
- trailing={
211
- <ToolbarControlGroup appearance="borderless" single>
212
- {drawerToggle()}
213
- </ToolbarControlGroup>
214
- }
215
- />
216
- }
217
- >
218
- <ValueTable label="Recent activity">
219
- <ValueTableRow
220
- label="Deploy finished"
221
- value="2m ago"
222
- icon={<LucideIcon icon={Bell} name="bell" />}
223
- />
224
- <ValueTableRow
225
- label="Review requested"
226
- value="9m ago"
227
- icon={<LucideIcon icon={Bell} name="bell" />}
228
- />
229
- </ValueTable>
230
- </Pane>
189
+ <ValueTable label="Recent activity">
190
+ <ValueTableRow
191
+ label="Deploy finished"
192
+ value="2m ago"
193
+ icon={<LucideIcon icon={Bell} name="bell" />}
194
+ />
195
+ <ValueTableRow
196
+ label="Review requested"
197
+ value="9m ago"
198
+ icon={<LucideIcon icon={Bell} name="bell" />}
199
+ />
200
+ </ValueTable>
231
201
  </CollapsiblePanel>
232
202
  );
233
203