@kerfjs/ui 5.0.0-beta.16 → 5.0.0-beta.18
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/README.md +111 -81
- package/ai/component-catalog-extension.schema.json +79 -0
- package/ai/component-catalog.json +499 -0
- package/ai/component-catalog.schema.json +15 -0
- package/ai/public-api-signatures-v1.md +86 -32
- package/ai/skill.md +60 -51
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/dist/app-tab.d.ts +1 -1
- package/dist/app-tab.js +1 -1
- package/dist/catalog.d.ts +12 -3
- package/dist/catalog.js +283 -90
- package/dist/catalog.js.map +1 -1
- package/dist/chunk-43FUQH35.js +83 -0
- package/dist/chunk-43FUQH35.js.map +1 -0
- package/dist/{chunk-YGN7OMLT.js → chunk-4TRDV6RL.js} +2 -2
- package/dist/chunk-4TRDV6RL.js.map +1 -0
- package/dist/chunk-6ETZKKAE.js +38 -0
- package/dist/chunk-6ETZKKAE.js.map +1 -0
- package/dist/chunk-6WTETUZ6.js +21 -0
- package/dist/chunk-6WTETUZ6.js.map +1 -0
- package/dist/chunk-7L6EHUMV.js +31 -0
- package/dist/chunk-7L6EHUMV.js.map +1 -0
- package/dist/chunk-BBRXX6PF.js +108 -0
- package/dist/chunk-BBRXX6PF.js.map +1 -0
- package/dist/chunk-DPDBVKNA.js +113 -0
- package/dist/chunk-DPDBVKNA.js.map +1 -0
- package/dist/chunk-ELSJX4W7.js +68 -0
- package/dist/chunk-ELSJX4W7.js.map +1 -0
- package/dist/{chunk-LCOBDOCC.js → chunk-ENVTNLYC.js} +153 -67
- package/dist/chunk-ENVTNLYC.js.map +1 -0
- package/dist/{chunk-AHQI52QO.js → chunk-FEY65TBV.js} +6 -2
- package/dist/chunk-FEY65TBV.js.map +1 -0
- package/dist/chunk-JTHUXDN2.js +58 -0
- package/dist/chunk-JTHUXDN2.js.map +1 -0
- package/dist/chunk-LPOMRC4C.js +25 -0
- package/dist/chunk-LPOMRC4C.js.map +1 -0
- package/dist/chunk-NENBUYX5.js +79 -0
- package/dist/chunk-NENBUYX5.js.map +1 -0
- package/dist/{chunk-XXMOARTH.js → chunk-NVWXKU37.js} +57 -16
- package/dist/chunk-NVWXKU37.js.map +1 -0
- package/dist/chunk-PIDTEQ3U.js +105 -0
- package/dist/chunk-PIDTEQ3U.js.map +1 -0
- package/dist/chunk-PX3YSS5C.js +32 -0
- package/dist/chunk-PX3YSS5C.js.map +1 -0
- package/dist/chunk-PZQVSDLH.js +61 -0
- package/dist/chunk-PZQVSDLH.js.map +1 -0
- package/dist/chunk-QIRXYKCE.js +113 -0
- package/dist/chunk-QIRXYKCE.js.map +1 -0
- package/dist/chunk-QO55FN2Y.js +20 -0
- package/dist/chunk-QO55FN2Y.js.map +1 -0
- package/dist/chunk-QQFJMRIB.js +45 -0
- package/dist/chunk-QQFJMRIB.js.map +1 -0
- package/dist/chunk-S3HKPI7I.js +218 -0
- package/dist/chunk-S3HKPI7I.js.map +1 -0
- package/dist/chunk-S3PCZ7NV.js +38 -0
- package/dist/chunk-S3PCZ7NV.js.map +1 -0
- package/dist/{chunk-7BR3DO2A.js → chunk-SRSJO5QE.js} +13 -4
- package/dist/chunk-SRSJO5QE.js.map +1 -0
- package/dist/chunk-U5IFGQCD.js +117 -0
- package/dist/chunk-U5IFGQCD.js.map +1 -0
- package/dist/{chunk-56RJXPHG.js → chunk-Y6LYKFXY.js} +1 -1
- package/dist/{chunk-56RJXPHG.js.map → chunk-Y6LYKFXY.js.map} +1 -1
- package/dist/chunk-ZK2G3VR5.js +35 -0
- package/dist/chunk-ZK2G3VR5.js.map +1 -0
- package/dist/chunk-ZVI5YST2.js +41 -0
- package/dist/chunk-ZVI5YST2.js.map +1 -0
- package/dist/collapsible-panel.d.ts +2 -2
- package/dist/collapsible-panel.js +47 -6
- package/dist/collapsible-panel.js.map +1 -1
- package/dist/device-class.js +33 -5
- package/dist/device-class.js.map +1 -1
- package/dist/disclosure-arrow.js +1 -1
- package/dist/empty-state.d.ts +1 -1
- package/dist/empty-state.js +1 -1
- package/dist/floating-toolbar.d.ts +1 -1
- package/dist/floating-toolbar.js +1 -1
- package/dist/index.js +25 -25
- package/dist/list-action-row.d.ts +1 -1
- package/dist/list-action-row.js +1 -1
- package/dist/list-header.d.ts +1 -1
- package/dist/list-header.js +1 -1
- package/dist/list-inset-control.d.ts +1 -1
- package/dist/list-inset-control.js +1 -1
- package/dist/list-inset-text.d.ts +1 -1
- package/dist/list-inset-text.js +1 -1
- package/dist/list-item.d.ts +1 -1
- package/dist/list-item.js +1 -1
- package/dist/loading-spinner.js +1 -1
- package/dist/nav-stack.d.ts +1 -1
- package/dist/nav-stack.js +1 -1
- package/dist/panel-header.d.ts +1 -1
- package/dist/panel-header.js +1 -1
- package/dist/resizable-region.d.ts +1 -1
- package/dist/resizable-region.js +1 -1
- package/dist/segmented-control.js +1 -1
- package/dist/select.d.ts +1 -1
- package/dist/select.js +1 -1
- package/dist/skeleton.d.ts +1 -1
- package/dist/skeleton.js +1 -1
- package/dist/split-view.d.ts +1 -1
- package/dist/split-view.js +70 -10
- package/dist/split-view.js.map +1 -1
- package/dist/state-banner.d.ts +1 -1
- package/dist/state-banner.js +1 -1
- package/dist/styles/catalog.css +48 -1
- package/dist/styles/toolbar-text.css +54 -2
- package/dist/styles/webawesome.css +131 -0
- package/dist/tab-bar.d.ts +1 -1
- package/dist/tab-bar.js +1 -1
- package/dist/tab-scaffold.d.ts +1 -1
- package/dist/tab-scaffold.js +43 -8
- package/dist/tab-scaffold.js.map +1 -1
- package/dist/token-search-field.js +1 -1
- package/dist/toolbar-control-group.d.ts +1 -1
- package/dist/toolbar-control-group.js +1 -1
- package/dist/toolbar-text.d.ts +17 -1
- package/dist/toolbar-text.js +1 -1
- package/dist/toolbar.d.ts +1 -1
- package/dist/toolbar.js +1 -1
- package/dist/value-table.d.ts +2 -2
- package/dist/value-table.js +1 -1
- package/dist/wire-catalog.d.ts +31 -2
- package/dist/wire-catalog.js +203 -8
- package/dist/wire-catalog.js.map +1 -1
- package/dist/wire-nav-stack.js +22 -7
- package/dist/wire-nav-stack.js.map +1 -1
- package/dist/wire-resizable-regions.d.ts +1 -1
- package/dist/wire-resizable-regions.js +1 -1
- package/dist/wire-sidebar.js +52 -41
- package/dist/wire-sidebar.js.map +1 -1
- package/dist/wire-tab-bars.js +1 -1
- package/dist/wire-tab-scaffold.js +9 -4
- package/dist/wire-tab-scaffold.js.map +1 -1
- package/dist/wire-token-search-fields.d.ts +1 -1
- package/dist/wire-token-search-fields.js +1 -1
- package/dist/workbench.d.ts +1 -1
- package/dist/workbench.js +56 -9
- package/dist/workbench.js.map +1 -1
- package/docs/app-layouts.md +7 -7
- package/docs/catalog.md +110 -25
- package/docs/collapsible-panel.md +38 -11
- package/docs/component-contract.md +30 -2
- package/docs/component-selection.md +76 -65
- package/docs/design/templates.md +1 -1
- package/docs/design-philosophy.md +5 -5
- package/docs/device-class.md +8 -8
- package/docs/examples/command-palette-adapter.tsx +67 -17
- package/docs/examples/component-catalog-extension.json +45 -0
- package/docs/layout.md +19 -19
- package/docs/nav-stack.md +15 -6
- package/docs/recipes.md +4 -4
- package/docs/split-view.md +3 -3
- package/docs/tab-scaffold.md +16 -7
- package/docs/ux-demo.md +2 -2
- package/docs/webawesome-theme.md +73 -8
- package/docs/workbench.md +18 -5
- package/llms.txt +36 -15
- package/package.json +6 -4
- package/ux-demo/recipes/app-shell.tsx +206 -10
- package/ux-demo/recipes/collapsible-sidebar.tsx +167 -44
- package/ux-demo/recipes/compact-toolbar.tsx +114 -12
- package/ux-demo/recipes/composer-form.tsx +106 -18
- package/ux-demo/recipes/list-detail-dialog.tsx +144 -8
- package/ux-demo/recipes/list-workspace-states.tsx +165 -8
- package/ux-demo/recipes/loaders.ts +5 -2
- package/ux-demo/recipes/loading-inspector.tsx +124 -41
- package/ux-demo/recipes/mount-recipe.ts +27 -6
- package/ux-demo/recipes/navigation-sidebar.tsx +120 -9
- package/ux-demo/recipes/navigation-stack.tsx +61 -19
- package/ux-demo/recipes/types.ts +3 -1
- package/ux-demo/recipes/workspace-header.tsx +57 -2
- package/dist/chunk-2Z5TWHKQ.js +0 -34
- package/dist/chunk-2Z5TWHKQ.js.map +0 -1
- package/dist/chunk-37T3VNIZ.js +0 -19
- package/dist/chunk-37T3VNIZ.js.map +0 -1
- package/dist/chunk-55DATV3A.js +0 -10
- package/dist/chunk-55DATV3A.js.map +0 -1
- package/dist/chunk-5ATCDSQS.js +0 -42
- package/dist/chunk-5ATCDSQS.js.map +0 -1
- package/dist/chunk-5E2HVJ33.js +0 -10
- package/dist/chunk-5E2HVJ33.js.map +0 -1
- package/dist/chunk-7BR3DO2A.js.map +0 -1
- package/dist/chunk-7JB7VPRI.js +0 -34
- package/dist/chunk-7JB7VPRI.js.map +0 -1
- package/dist/chunk-7LDCTU7D.js +0 -134
- package/dist/chunk-7LDCTU7D.js.map +0 -1
- package/dist/chunk-AHQI52QO.js.map +0 -1
- package/dist/chunk-AQNKJRFH.js +0 -79
- package/dist/chunk-AQNKJRFH.js.map +0 -1
- package/dist/chunk-AVZHJN6C.js +0 -14
- package/dist/chunk-AVZHJN6C.js.map +0 -1
- package/dist/chunk-B24C4A6T.js +0 -22
- package/dist/chunk-B24C4A6T.js.map +0 -1
- package/dist/chunk-BLK3UEEI.js +0 -23
- package/dist/chunk-BLK3UEEI.js.map +0 -1
- package/dist/chunk-CH4SQ6H5.js +0 -39
- package/dist/chunk-CH4SQ6H5.js.map +0 -1
- package/dist/chunk-DAFFDO33.js +0 -14
- package/dist/chunk-DAFFDO33.js.map +0 -1
- package/dist/chunk-I7INRJO4.js +0 -17
- package/dist/chunk-I7INRJO4.js.map +0 -1
- package/dist/chunk-L4OKFDJY.js +0 -24
- package/dist/chunk-L4OKFDJY.js.map +0 -1
- package/dist/chunk-LCOBDOCC.js.map +0 -1
- package/dist/chunk-M5JRI6MN.js +0 -27
- package/dist/chunk-M5JRI6MN.js.map +0 -1
- package/dist/chunk-RNPGFQ33.js +0 -10
- package/dist/chunk-RNPGFQ33.js.map +0 -1
- package/dist/chunk-RUNWEPR4.js +0 -10
- package/dist/chunk-RUNWEPR4.js.map +0 -1
- package/dist/chunk-VPCTCUED.js +0 -10
- package/dist/chunk-VPCTCUED.js.map +0 -1
- package/dist/chunk-WVPYVDXY.js +0 -15
- package/dist/chunk-WVPYVDXY.js.map +0 -1
- package/dist/chunk-XE5ZLPCC.js +0 -20
- package/dist/chunk-XE5ZLPCC.js.map +0 -1
- package/dist/chunk-XXMOARTH.js.map +0 -1
- package/dist/chunk-YGN7OMLT.js.map +0 -1
|
@@ -19,14 +19,210 @@ export const createRecipe: RecipeFactory = (announce) => {
|
|
|
19
19
|
const selected = signal('inbox');
|
|
20
20
|
const navigationSize = signal(224);
|
|
21
21
|
const inspectorSize = signal(240);
|
|
22
|
-
const responsivePane = signal<'content' | 'navigation' | 'inspector'>(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
const responsivePane = signal<'content' | 'navigation' | 'inspector'>(
|
|
23
|
+
'content',
|
|
24
|
+
);
|
|
25
|
+
const render = () => (
|
|
26
|
+
<section
|
|
27
|
+
class="kui-recipe recipe-shell kui-recipe__surface kui-layout"
|
|
28
|
+
data-recipe="recipe-app-shell"
|
|
29
|
+
data-responsive-pane={responsivePane.value}
|
|
30
|
+
>
|
|
31
|
+
<Toolbar
|
|
32
|
+
label="Atlas workspace"
|
|
33
|
+
leading={
|
|
34
|
+
<ToolbarControlGroup appearance="borderless" single>
|
|
35
|
+
<ToolbarText text="Atlas" />
|
|
36
|
+
</ToolbarControlGroup>
|
|
37
|
+
}
|
|
38
|
+
trailing={
|
|
39
|
+
<div class="recipe-shell__toolbar-actions">
|
|
40
|
+
<ToolbarControlGroup
|
|
41
|
+
className="recipe-shell__responsive-controls"
|
|
42
|
+
appearance="borderless"
|
|
43
|
+
label="Workspace panes"
|
|
44
|
+
>
|
|
45
|
+
<button
|
|
46
|
+
type="button"
|
|
47
|
+
aria-label="Show navigation"
|
|
48
|
+
aria-controls="recipe-shell-navigation"
|
|
49
|
+
aria-pressed={String(responsivePane.value === 'navigation')}
|
|
50
|
+
data-action="recipe-action"
|
|
51
|
+
data-recipe-command="show-navigation"
|
|
52
|
+
>
|
|
53
|
+
<LucideIcon icon={Folder} name="folder" />
|
|
54
|
+
</button>
|
|
55
|
+
<button
|
|
56
|
+
type="button"
|
|
57
|
+
aria-label="Show content"
|
|
58
|
+
aria-controls="recipe-shell-content"
|
|
59
|
+
aria-pressed={String(responsivePane.value === 'content')}
|
|
60
|
+
data-action="recipe-action"
|
|
61
|
+
data-recipe-command="show-content"
|
|
62
|
+
>
|
|
63
|
+
<LucideIcon icon={Inbox} name="inbox" />
|
|
64
|
+
</button>
|
|
65
|
+
<button
|
|
66
|
+
type="button"
|
|
67
|
+
aria-label="Show inspector"
|
|
68
|
+
aria-controls="recipe-shell-inspector"
|
|
69
|
+
aria-pressed={String(responsivePane.value === 'inspector')}
|
|
70
|
+
data-action="recipe-action"
|
|
71
|
+
data-recipe-command="show-inspector"
|
|
72
|
+
>
|
|
73
|
+
<LucideIcon icon={Settings} name="settings" />
|
|
74
|
+
</button>
|
|
75
|
+
</ToolbarControlGroup>
|
|
76
|
+
<ToolbarControlGroup
|
|
77
|
+
appearance="borderless"
|
|
78
|
+
label="Workspace controls"
|
|
79
|
+
>
|
|
80
|
+
<button
|
|
81
|
+
type="button"
|
|
82
|
+
aria-label="Notifications"
|
|
83
|
+
data-action="recipe-action"
|
|
84
|
+
data-recipe-command="notify"
|
|
85
|
+
>
|
|
86
|
+
<LucideIcon icon={Bell} name="bell" />
|
|
87
|
+
</button>
|
|
88
|
+
<button
|
|
89
|
+
type="button"
|
|
90
|
+
aria-label="Settings"
|
|
91
|
+
data-action="recipe-action"
|
|
92
|
+
data-recipe-command="settings"
|
|
93
|
+
>
|
|
94
|
+
<LucideIcon icon={Settings} name="settings" />
|
|
95
|
+
</button>
|
|
96
|
+
</ToolbarControlGroup>
|
|
97
|
+
</div>
|
|
98
|
+
}
|
|
99
|
+
/>
|
|
100
|
+
<div class="recipe-shell__body">
|
|
101
|
+
<div id="recipe-shell-navigation" class="recipe-shell__nav-region">
|
|
102
|
+
<ResizableRegion
|
|
103
|
+
id="recipe-navigation"
|
|
104
|
+
label="Navigation"
|
|
105
|
+
size={navigationSize.value}
|
|
106
|
+
min={180}
|
|
107
|
+
max={320}
|
|
108
|
+
>
|
|
109
|
+
<aside class="recipe-shell__nav kui-pane">
|
|
110
|
+
<ListHeader label="Workspace" />
|
|
111
|
+
<nav
|
|
112
|
+
class="recipe-shell__nav-list kui-pane__content kui-content"
|
|
113
|
+
aria-label="Workspace"
|
|
114
|
+
>
|
|
115
|
+
<section>
|
|
116
|
+
<ListItem
|
|
117
|
+
action="recipe-action"
|
|
118
|
+
itemId="inbox"
|
|
119
|
+
label="Inbox"
|
|
120
|
+
icon={<LucideIcon icon={Inbox} name="inbox" />}
|
|
121
|
+
selected={selected.value === 'inbox'}
|
|
122
|
+
/>
|
|
123
|
+
<ListItem
|
|
124
|
+
action="recipe-action"
|
|
125
|
+
itemId="projects"
|
|
126
|
+
label="Projects with a deliberately wrapping title"
|
|
127
|
+
icon={<LucideIcon icon={Folder} name="folder" />}
|
|
128
|
+
selected={selected.value === 'projects'}
|
|
129
|
+
multiline
|
|
130
|
+
/>
|
|
131
|
+
</section>
|
|
132
|
+
</nav>
|
|
133
|
+
</aside>
|
|
134
|
+
</ResizableRegion>
|
|
135
|
+
</div>
|
|
136
|
+
<main id="recipe-shell-content" class="recipe-shell__main kui-pane">
|
|
137
|
+
<PanelHeader
|
|
138
|
+
title={
|
|
139
|
+
selected.value === 'inbox' ? 'Inbox triage' : 'Active projects'
|
|
140
|
+
}
|
|
141
|
+
titleId="recipe-shell-main-title"
|
|
142
|
+
actions={
|
|
143
|
+
<button
|
|
144
|
+
class="kui-recipe__button"
|
|
145
|
+
data-primary="true"
|
|
146
|
+
type="button"
|
|
147
|
+
data-action="recipe-action"
|
|
148
|
+
data-recipe-command="new"
|
|
149
|
+
>
|
|
150
|
+
New task
|
|
151
|
+
</button>
|
|
152
|
+
}
|
|
153
|
+
/>
|
|
154
|
+
<div class="recipe-shell__main-body kui-pane__content kui-content">
|
|
155
|
+
<p class="kui-recipe__ownership kui-content-item">
|
|
156
|
+
Recipe owns pane geometry and one scroll owner per pane. The app
|
|
157
|
+
owns routing, data, pane visibility, sizes, and persistence.
|
|
158
|
+
</p>
|
|
159
|
+
<div class="recipe-shell__cards">
|
|
160
|
+
{[
|
|
161
|
+
'Release accessibility audit',
|
|
162
|
+
'Prepare tablet navigation',
|
|
163
|
+
'Review stale-data states',
|
|
164
|
+
'Confirm package boundaries',
|
|
165
|
+
].map((title) => (
|
|
166
|
+
<article class="recipe-shell__card kui-content-item">
|
|
167
|
+
<strong>{title}</strong>
|
|
168
|
+
<p class="kui-recipe__muted">
|
|
169
|
+
Assigned to the interface systems team · due this week
|
|
170
|
+
</p>
|
|
171
|
+
</article>
|
|
172
|
+
))}
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
</main>
|
|
176
|
+
<div id="recipe-shell-inspector" class="recipe-shell__inspector-region">
|
|
177
|
+
<ResizableRegion
|
|
178
|
+
id="recipe-inspector"
|
|
179
|
+
label="Inspector"
|
|
180
|
+
size={inspectorSize.value}
|
|
181
|
+
min={200}
|
|
182
|
+
max={360}
|
|
183
|
+
edge="start"
|
|
184
|
+
>
|
|
185
|
+
<aside class="recipe-shell__inspector kui-pane">
|
|
186
|
+
<PanelHeader
|
|
187
|
+
title="Inspector"
|
|
188
|
+
titleId="recipe-shell-inspector-title"
|
|
189
|
+
/>
|
|
190
|
+
<div class="recipe-shell__inspector-body kui-pane__content kui-content">
|
|
191
|
+
<ValueTable label="Selected task">
|
|
192
|
+
<ValueTableRow label="Status" value="In review" />
|
|
193
|
+
<ValueTableRow label="Owner" value="Mara Chen" />
|
|
194
|
+
<ValueTableRow label="Priority" value="High" />
|
|
195
|
+
</ValueTable>
|
|
196
|
+
</div>
|
|
197
|
+
</aside>
|
|
198
|
+
</ResizableRegion>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
</section>
|
|
202
|
+
);
|
|
203
|
+
return {
|
|
204
|
+
render,
|
|
205
|
+
action(command, element) {
|
|
206
|
+
const pane = command.match(
|
|
207
|
+
/^show-(navigation|content|inspector)$/,
|
|
208
|
+
)?.[1] as typeof responsivePane.value | undefined;
|
|
209
|
+
if (pane) responsivePane.value = pane;
|
|
210
|
+
const id = element.dataset.itemId;
|
|
211
|
+
if (id) selected.value = id;
|
|
212
|
+
announce(
|
|
213
|
+
id
|
|
214
|
+
? `Opened ${id}`
|
|
215
|
+
: pane
|
|
216
|
+
? `Showing ${pane}`
|
|
217
|
+
: command === 'new'
|
|
218
|
+
? 'New task requested'
|
|
219
|
+
: `${command} requested`,
|
|
220
|
+
);
|
|
221
|
+
},
|
|
222
|
+
resize(id, size) {
|
|
223
|
+
if (id === 'recipe-navigation') navigationSize.value = size;
|
|
224
|
+
if (id === 'recipe-inspector') inspectorSize.value = size;
|
|
225
|
+
announce(`${id} resized to ${size}px`);
|
|
226
|
+
},
|
|
227
|
+
};
|
|
32
228
|
};
|
|
@@ -2,7 +2,10 @@ import '@kerfjs/ui/layout.css';
|
|
|
2
2
|
import '@kerfjs/ui/collapsible-panel.css';
|
|
3
3
|
import './recipes.css';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
CollapsiblePanel,
|
|
7
|
+
CollapsiblePanelToggle,
|
|
8
|
+
} from '@kerfjs/ui/collapsible-panel';
|
|
6
9
|
import { deviceClass } from '@kerfjs/ui/device-class';
|
|
7
10
|
import { ListHeader } from '@kerfjs/ui/list-header';
|
|
8
11
|
import { ListItem } from '@kerfjs/ui/list-item';
|
|
@@ -26,7 +29,12 @@ const DRAWER_ACTION = 'recipe-sidebar-drawer';
|
|
|
26
29
|
*/
|
|
27
30
|
function memoryStorage(): SidebarStorage {
|
|
28
31
|
const map = new Map<string, string>();
|
|
29
|
-
return {
|
|
32
|
+
return {
|
|
33
|
+
getItem: (key) => map.get(key) ?? null,
|
|
34
|
+
setItem: (key, value) => {
|
|
35
|
+
map.set(key, value);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
export const createRecipe: RecipeFactory = (announce) => {
|
|
@@ -38,52 +46,157 @@ export const createRecipe: RecipeFactory = (announce) => {
|
|
|
38
46
|
// switches the rail/drawer to a dismissable overlay presentation.
|
|
39
47
|
const device = deviceClass();
|
|
40
48
|
|
|
41
|
-
const railToggle = (inPanel: boolean) =>
|
|
42
|
-
|
|
49
|
+
const railToggle = (inPanel: boolean) => (
|
|
50
|
+
<CollapsiblePanelToggle
|
|
51
|
+
side="left"
|
|
52
|
+
collapsed={railCollapsed.value}
|
|
53
|
+
action={RAIL_ACTION}
|
|
54
|
+
panelId="sidebar-rail"
|
|
55
|
+
label={railCollapsed.value ? 'Show navigation' : 'Hide navigation'}
|
|
56
|
+
className={inPanel ? '' : 'recipe-collapsible-sidebar__reveal'}
|
|
57
|
+
/>
|
|
58
|
+
);
|
|
59
|
+
const drawerToggle = () => (
|
|
60
|
+
<CollapsiblePanelToggle
|
|
61
|
+
side="bottom"
|
|
62
|
+
collapsed={drawerCollapsed.value}
|
|
63
|
+
action={DRAWER_ACTION}
|
|
64
|
+
panelId="sidebar-console"
|
|
65
|
+
label={drawerCollapsed.value ? 'Show activity' : 'Hide activity'}
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
43
68
|
|
|
44
|
-
const render = () =>
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
69
|
+
const render = () => (
|
|
70
|
+
<section
|
|
71
|
+
class="kui-recipe recipe-collapsible-sidebar kui-recipe__surface"
|
|
72
|
+
data-recipe="recipe-collapsible-sidebar"
|
|
73
|
+
data-rail-collapsed={String(railCollapsed.value)}
|
|
74
|
+
data-drawer-collapsed={String(drawerCollapsed.value)}
|
|
75
|
+
>
|
|
76
|
+
<CollapsiblePanel
|
|
77
|
+
id="sidebar-rail"
|
|
78
|
+
side="left"
|
|
79
|
+
size={232}
|
|
80
|
+
collapsed={railCollapsed.value}
|
|
81
|
+
label="Workspace navigation"
|
|
82
|
+
className="recipe-collapsible-sidebar__rail kui-pane"
|
|
83
|
+
>
|
|
84
|
+
<div class="recipe-collapsible-sidebar__rail-head">
|
|
85
|
+
<ToolbarText text="Atlas" />
|
|
86
|
+
<ToolbarControlGroup appearance="borderless" single>
|
|
87
|
+
{railToggle(true)}
|
|
88
|
+
</ToolbarControlGroup>
|
|
89
|
+
</div>
|
|
90
|
+
<nav
|
|
91
|
+
class="recipe-collapsible-sidebar__nav kui-pane__content kui-content"
|
|
92
|
+
aria-label="Workspace"
|
|
93
|
+
>
|
|
94
|
+
<section>
|
|
95
|
+
<ListHeader label="Workspace" />
|
|
96
|
+
<ListItem
|
|
97
|
+
action="recipe-action"
|
|
98
|
+
itemId="inbox"
|
|
99
|
+
label="Inbox"
|
|
100
|
+
icon={<LucideIcon icon={Inbox} name="inbox" />}
|
|
101
|
+
trailing={<span>12</span>}
|
|
102
|
+
selected={selected.value === 'inbox'}
|
|
103
|
+
/>
|
|
104
|
+
<ListItem
|
|
105
|
+
action="recipe-action"
|
|
106
|
+
itemId="projects"
|
|
107
|
+
label="Projects"
|
|
108
|
+
icon={<LucideIcon icon={Folder} name="folder" />}
|
|
109
|
+
selected={selected.value === 'projects'}
|
|
110
|
+
/>
|
|
111
|
+
<ListItem
|
|
112
|
+
action="recipe-action"
|
|
113
|
+
itemId="shared"
|
|
114
|
+
label="Shared with me"
|
|
115
|
+
icon={<LucideIcon icon={Users} name="users" />}
|
|
116
|
+
selected={selected.value === 'shared'}
|
|
117
|
+
/>
|
|
118
|
+
</section>
|
|
119
|
+
</nav>
|
|
120
|
+
</CollapsiblePanel>
|
|
121
|
+
<main class="recipe-collapsible-sidebar__main kui-pane">
|
|
122
|
+
<PanelHeader
|
|
123
|
+
title={
|
|
124
|
+
selected.value === 'projects'
|
|
125
|
+
? 'Projects'
|
|
126
|
+
: selected.value === 'shared'
|
|
127
|
+
? 'Shared with me'
|
|
128
|
+
: 'Inbox'
|
|
129
|
+
}
|
|
130
|
+
titleId="recipe-collapsible-main-title"
|
|
131
|
+
icon={railToggle(false)}
|
|
132
|
+
actions={
|
|
133
|
+
<ToolbarControlGroup appearance="borderless" single>
|
|
134
|
+
{drawerToggle()}
|
|
135
|
+
</ToolbarControlGroup>
|
|
136
|
+
}
|
|
137
|
+
/>
|
|
138
|
+
<div class="recipe-collapsible-sidebar__body kui-pane__content kui-content">
|
|
139
|
+
<p class="kui-recipe__ownership kui-content-item">
|
|
140
|
+
The app owns each panel's <code>collapsed</code> signal, sizes, and
|
|
141
|
+
content; <code>wireSidebar</code> owns the toggle, focus
|
|
142
|
+
move/restore, the compact overlay, and persistence.
|
|
143
|
+
</p>
|
|
144
|
+
<article class="recipe-collapsible-sidebar__card kui-content-item">
|
|
145
|
+
<strong>Narrow the window to a compact width</strong>
|
|
146
|
+
<p class="kui-recipe__muted">
|
|
147
|
+
The rail and drawer become a dismissable overlay: a backdrop,
|
|
148
|
+
Escape, and a trapped Tab ring, all managed by the wire.
|
|
149
|
+
</p>
|
|
150
|
+
</article>
|
|
151
|
+
</div>
|
|
152
|
+
</main>
|
|
153
|
+
<CollapsiblePanel
|
|
154
|
+
id="sidebar-console"
|
|
155
|
+
side="bottom"
|
|
156
|
+
size={168}
|
|
157
|
+
collapsed={drawerCollapsed.value}
|
|
158
|
+
label="Activity"
|
|
159
|
+
className="recipe-collapsible-sidebar__drawer kui-pane"
|
|
160
|
+
>
|
|
161
|
+
<Toolbar
|
|
162
|
+
label="Activity"
|
|
163
|
+
divider
|
|
164
|
+
leading={
|
|
165
|
+
<ToolbarControlGroup appearance="borderless" single>
|
|
166
|
+
<ToolbarText text="Activity" size="small" />
|
|
167
|
+
</ToolbarControlGroup>
|
|
168
|
+
}
|
|
169
|
+
trailing={
|
|
170
|
+
<ToolbarControlGroup appearance="borderless" single>
|
|
171
|
+
{drawerToggle()}
|
|
172
|
+
</ToolbarControlGroup>
|
|
173
|
+
}
|
|
174
|
+
/>
|
|
175
|
+
<div class="recipe-collapsible-sidebar__drawer-body kui-pane__content kui-content">
|
|
176
|
+
<ul class="recipe-collapsible-sidebar__log">
|
|
177
|
+
<li>
|
|
178
|
+
<LucideIcon icon={Bell} name="bell" />
|
|
179
|
+
Deploy finished · 2m ago
|
|
180
|
+
</li>
|
|
181
|
+
<li>
|
|
182
|
+
<LucideIcon icon={Bell} name="bell" />
|
|
183
|
+
Review requested · 9m ago
|
|
184
|
+
</li>
|
|
185
|
+
</ul>
|
|
186
|
+
</div>
|
|
187
|
+
</CollapsiblePanel>
|
|
188
|
+
</section>
|
|
189
|
+
);
|
|
81
190
|
|
|
82
191
|
return {
|
|
83
192
|
render,
|
|
84
193
|
action(command, element) {
|
|
85
194
|
const id = element.dataset.itemId;
|
|
86
|
-
if (id) {
|
|
195
|
+
if (id) {
|
|
196
|
+
selected.value = id;
|
|
197
|
+
announce(`Opened ${id}`);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
87
200
|
announce(`${command} requested`);
|
|
88
201
|
},
|
|
89
202
|
wire(root) {
|
|
@@ -91,8 +204,18 @@ export const createRecipe: RecipeFactory = (announce) => {
|
|
|
91
204
|
deviceClass: device,
|
|
92
205
|
storage,
|
|
93
206
|
panels: [
|
|
94
|
-
{
|
|
95
|
-
|
|
207
|
+
{
|
|
208
|
+
id: 'sidebar-rail',
|
|
209
|
+
collapsed: railCollapsed,
|
|
210
|
+
toggleAction: RAIL_ACTION,
|
|
211
|
+
storageKey: 'recipe-sidebar-rail',
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
id: 'sidebar-console',
|
|
215
|
+
collapsed: drawerCollapsed,
|
|
216
|
+
toggleAction: DRAWER_ACTION,
|
|
217
|
+
storageKey: 'recipe-sidebar-console',
|
|
218
|
+
},
|
|
96
219
|
],
|
|
97
220
|
});
|
|
98
221
|
},
|
|
@@ -17,27 +17,129 @@ export const createRecipe: RecipeFactory = (announce) => {
|
|
|
17
17
|
const view = signal('list');
|
|
18
18
|
const sort = signal('updated');
|
|
19
19
|
const filtered = signal(false);
|
|
20
|
-
const render = () =>
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
const render = () => (
|
|
21
|
+
<section
|
|
22
|
+
class="kui-recipe recipe-compact-toolbar kui-recipe__surface kui-layout"
|
|
23
|
+
data-recipe="recipe-compact-toolbar"
|
|
24
|
+
>
|
|
25
|
+
<Toolbar
|
|
26
|
+
label="Task workspace controls"
|
|
27
|
+
leading={
|
|
28
|
+
<ToolbarControlGroup appearance="borderless" single>
|
|
29
|
+
<ToolbarText text="Tasks" size="small" />
|
|
30
|
+
</ToolbarControlGroup>
|
|
31
|
+
}
|
|
32
|
+
center={
|
|
33
|
+
<ToolbarControlGroup>
|
|
34
|
+
<SegmentedControl
|
|
35
|
+
id="recipe-view"
|
|
36
|
+
label="View"
|
|
37
|
+
value={view.value}
|
|
38
|
+
action="recipe-action"
|
|
39
|
+
appearance="toolbar"
|
|
40
|
+
shape="pill"
|
|
41
|
+
size="small"
|
|
42
|
+
choices={[
|
|
43
|
+
{ value: 'list', label: 'List' },
|
|
44
|
+
{ value: 'board', label: 'Board' },
|
|
45
|
+
{ value: 'timeline', label: 'Timeline' },
|
|
46
|
+
]}
|
|
47
|
+
/>
|
|
48
|
+
</ToolbarControlGroup>
|
|
49
|
+
}
|
|
50
|
+
trailing={
|
|
51
|
+
<div class="kui-control-cluster">
|
|
52
|
+
<Select
|
|
53
|
+
name="recipe-sort"
|
|
54
|
+
value={sort.value}
|
|
55
|
+
ariaLabel="Sort tasks"
|
|
56
|
+
fitMenu
|
|
57
|
+
choices={[
|
|
58
|
+
{ value: 'updated', label: 'Recently updated' },
|
|
59
|
+
{ value: 'priority', label: 'Priority' },
|
|
60
|
+
{ value: 'owner', label: 'Owner' },
|
|
61
|
+
{ value: 'created', label: 'Created date' },
|
|
62
|
+
]}
|
|
63
|
+
/>
|
|
64
|
+
<ToolbarControlGroup label="Task actions" buttonAppearance="push">
|
|
65
|
+
<button
|
|
66
|
+
type="button"
|
|
67
|
+
aria-label="Toggle filters"
|
|
68
|
+
aria-pressed={String(filtered.value)}
|
|
69
|
+
data-action="recipe-action"
|
|
70
|
+
data-recipe-command="filter"
|
|
71
|
+
>
|
|
72
|
+
<LucideIcon icon={Filter} name="filter" />
|
|
73
|
+
</button>
|
|
74
|
+
<button
|
|
75
|
+
type="button"
|
|
76
|
+
aria-label="Refresh tasks"
|
|
77
|
+
data-action="recipe-action"
|
|
78
|
+
data-recipe-command="refresh"
|
|
79
|
+
>
|
|
80
|
+
<LucideIcon icon={RefreshCw} name="refresh-cw" />
|
|
81
|
+
</button>
|
|
82
|
+
</ToolbarControlGroup>
|
|
83
|
+
<button
|
|
84
|
+
class="kui-recipe__button"
|
|
85
|
+
type="button"
|
|
86
|
+
aria-label="More task actions"
|
|
87
|
+
data-action="recipe-action"
|
|
88
|
+
data-recipe-command="more"
|
|
89
|
+
>
|
|
90
|
+
<LucideIcon icon={MoreHorizontal} name="more-horizontal" />
|
|
91
|
+
</button>
|
|
92
|
+
</div>
|
|
93
|
+
}
|
|
94
|
+
/>
|
|
95
|
+
<div class="recipe-compact-toolbar__guide kui-content-item">
|
|
96
|
+
<div>
|
|
97
|
+
<strong>ToolbarControlGroup</strong>
|
|
98
|
+
<span class="kui-recipe__muted">
|
|
99
|
+
Related commands or pressed tools.
|
|
100
|
+
</span>
|
|
101
|
+
</div>
|
|
102
|
+
<div>
|
|
103
|
+
<strong>SegmentedControl</strong>
|
|
104
|
+
<span class="kui-recipe__muted">
|
|
105
|
+
A few visible exclusive choices.
|
|
106
|
+
</span>
|
|
107
|
+
</div>
|
|
108
|
+
<div>
|
|
109
|
+
<strong>Select</strong>
|
|
110
|
+
<span class="kui-recipe__muted">A longer controlled value list.</span>
|
|
111
|
+
</div>
|
|
112
|
+
<div>
|
|
113
|
+
<strong>Ordinary button</strong>
|
|
114
|
+
<span class="kui-recipe__muted">One independent command.</span>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
<p class="kui-recipe__ownership kui-content-item">
|
|
118
|
+
The recipe owns control semantics and wrapping. The app owns values,
|
|
119
|
+
actions, persistence, and responsive priority.
|
|
120
|
+
</p>
|
|
121
|
+
</section>
|
|
122
|
+
);
|
|
30
123
|
return {
|
|
31
124
|
render,
|
|
32
125
|
action(command, element) {
|
|
33
126
|
const value = element.dataset.segmentValue;
|
|
34
127
|
if (value) view.value = value;
|
|
35
128
|
if (command === 'filter') filtered.value = !filtered.value;
|
|
36
|
-
announce(
|
|
129
|
+
announce(
|
|
130
|
+
value
|
|
131
|
+
? `View: ${value}`
|
|
132
|
+
: command === 'filter'
|
|
133
|
+
? `Filters ${filtered.value ? 'on' : 'off'}`
|
|
134
|
+
: `${command} requested`,
|
|
135
|
+
);
|
|
37
136
|
},
|
|
38
137
|
change(element) {
|
|
39
138
|
const value = (element as HTMLElement & { value?: string }).value;
|
|
40
|
-
if (element.getAttribute('name') === 'recipe-sort' && value) {
|
|
139
|
+
if (element.getAttribute('name') === 'recipe-sort' && value) {
|
|
140
|
+
sort.value = value;
|
|
141
|
+
announce(`Sort: ${value}`);
|
|
142
|
+
}
|
|
41
143
|
},
|
|
42
144
|
};
|
|
43
145
|
};
|