@kerfjs/ui 5.0.0-beta.4 → 5.0.0-beta.6
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 +8 -8
- package/ai/component-catalog.json +303 -167
- package/ai/public-api-signatures-v1.md +333 -26
- package/ai/skill.md +14 -7
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/dist/browser/{dialog-header.js → panel-header.js} +3 -2
- package/dist/{chunk-GY5WH7TO.js → chunk-24Z2XE6C.js} +3 -3
- package/dist/chunk-24Z2XE6C.js.map +1 -0
- package/dist/chunk-2RJBFNB6.js +172 -0
- package/dist/chunk-2RJBFNB6.js.map +1 -0
- package/dist/chunk-KZKSBUKC.js +19 -0
- package/dist/chunk-KZKSBUKC.js.map +1 -0
- package/dist/chunk-RBTVBGRD.js +23 -0
- package/dist/chunk-RBTVBGRD.js.map +1 -0
- package/dist/device-class.d.ts +62 -0
- package/dist/device-class.js +75 -0
- package/dist/device-class.js.map +1 -0
- package/dist/index.d.ts +2 -3
- package/dist/index.js +8 -9
- package/dist/nav-stack.d.ts +38 -0
- package/dist/nav-stack.js +4 -0
- package/dist/nav-stack.js.map +1 -0
- package/dist/panel-header.d.ts +26 -0
- package/dist/panel-header.js +6 -0
- package/dist/panel-header.js.map +1 -0
- package/dist/split-view.d.ts +42 -0
- package/dist/split-view.js +20 -0
- package/dist/split-view.js.map +1 -0
- package/dist/styles/foundation.css +21 -0
- package/dist/styles/menu-header.css +19 -2
- package/dist/styles/nav-stack.css +112 -0
- package/dist/styles/panel-header.css +59 -0
- package/dist/styles/split-view.css +35 -0
- package/dist/styles/styles.css +1 -2
- package/dist/styles/tab-scaffold.css +84 -0
- package/dist/styles/toolbar-control-group.css +52 -2
- package/dist/styles/toolbar-text.css +11 -0
- package/dist/styles/toolbar.css +9 -0
- package/dist/styles/workbench.css +94 -0
- package/dist/tab-scaffold.d.ts +30 -0
- package/dist/tab-scaffold.js +16 -0
- package/dist/tab-scaffold.js.map +1 -0
- package/dist/toolbar-text.d.ts +4 -2
- package/dist/toolbar-text.js +1 -1
- package/dist/wire-nav-stack.d.ts +15 -0
- package/dist/wire-nav-stack.js +88 -0
- package/dist/wire-nav-stack.js.map +1 -0
- package/dist/wire-tab-scaffold.d.ts +11 -0
- package/dist/wire-tab-scaffold.js +16 -0
- package/dist/wire-tab-scaffold.js.map +1 -0
- package/dist/wire-token-search-fields.d.ts +46 -4
- package/dist/wire-token-search-fields.js +1 -1
- package/dist/workbench.d.ts +33 -0
- package/dist/workbench.js +17 -0
- package/dist/workbench.js.map +1 -0
- package/docs/accessibility.md +10 -10
- package/docs/app-layouts.md +57 -0
- package/docs/component-contract.md +34 -9
- package/docs/component-selection.md +56 -7
- package/docs/design-philosophy.md +23 -1
- package/docs/device-class.md +54 -0
- package/docs/layout.md +33 -8
- package/docs/nav-stack.md +47 -0
- package/docs/recipes.md +17 -5
- package/docs/split-view.md +49 -0
- package/docs/tab-scaffold.md +41 -0
- package/docs/ux-demo.md +3 -3
- package/docs/workbench.md +47 -0
- package/llms.txt +107 -43
- package/package.json +37 -11
- package/ux-demo/recipes/app-shell.tsx +3 -3
- package/ux-demo/recipes/composer-form.tsx +2 -2
- package/ux-demo/recipes/list-workspace-states.tsx +2 -2
- package/ux-demo/recipes/loaders.ts +2 -0
- package/ux-demo/recipes/master-detail-dialog.tsx +2 -2
- package/ux-demo/recipes/mount-recipe.ts +3 -0
- package/ux-demo/recipes/navigation-stack.tsx +76 -0
- package/ux-demo/recipes/recipes.css +19 -8
- package/ux-demo/recipes/workspace-header.tsx +2 -2
- package/dist/browser/page-header.js +0 -3
- package/dist/chunk-2PES33HS.js +0 -13
- package/dist/chunk-2PES33HS.js.map +0 -1
- package/dist/chunk-GY5WH7TO.js.map +0 -1
- package/dist/chunk-H5AGGVU5.js +0 -75
- package/dist/chunk-H5AGGVU5.js.map +0 -1
- package/dist/chunk-K3G72I6D.js +0 -24
- package/dist/chunk-K3G72I6D.js.map +0 -1
- package/dist/dialog-header.d.ts +0 -15
- package/dist/dialog-header.js +0 -5
- package/dist/dialog-header.js.map +0 -1
- package/dist/page-header.d.ts +0 -9
- package/dist/page-header.js +0 -3
- package/dist/page-header.js.map +0 -1
- package/dist/styles/dialog-header.css +0 -87
- package/dist/styles/page-header.css +0 -33
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kerfjs/ui",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.6",
|
|
4
4
|
"description": "Accessible, composable UI primitives for kerf applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -88,6 +88,10 @@
|
|
|
88
88
|
"types": "./dist/wire-resizable-regions.d.ts",
|
|
89
89
|
"import": "./dist/wire-resizable-regions.js"
|
|
90
90
|
},
|
|
91
|
+
"./device-class": {
|
|
92
|
+
"types": "./dist/device-class.d.ts",
|
|
93
|
+
"import": "./dist/device-class.js"
|
|
94
|
+
},
|
|
91
95
|
"./app-tab": {
|
|
92
96
|
"types": "./dist/app-tab.d.ts",
|
|
93
97
|
"browser": "./dist/browser/app-tab.js",
|
|
@@ -102,10 +106,29 @@
|
|
|
102
106
|
"types": "./dist/wire-tab-bars.d.ts",
|
|
103
107
|
"import": "./dist/wire-tab-bars.js"
|
|
104
108
|
},
|
|
105
|
-
"./
|
|
106
|
-
"types": "./dist/
|
|
107
|
-
"
|
|
108
|
-
|
|
109
|
+
"./nav-stack": {
|
|
110
|
+
"types": "./dist/nav-stack.d.ts",
|
|
111
|
+
"import": "./dist/nav-stack.js"
|
|
112
|
+
},
|
|
113
|
+
"./wire-nav-stack": {
|
|
114
|
+
"types": "./dist/wire-nav-stack.d.ts",
|
|
115
|
+
"import": "./dist/wire-nav-stack.js"
|
|
116
|
+
},
|
|
117
|
+
"./split-view": {
|
|
118
|
+
"types": "./dist/split-view.d.ts",
|
|
119
|
+
"import": "./dist/split-view.js"
|
|
120
|
+
},
|
|
121
|
+
"./workbench": {
|
|
122
|
+
"types": "./dist/workbench.d.ts",
|
|
123
|
+
"import": "./dist/workbench.js"
|
|
124
|
+
},
|
|
125
|
+
"./tab-scaffold": {
|
|
126
|
+
"types": "./dist/tab-scaffold.d.ts",
|
|
127
|
+
"import": "./dist/tab-scaffold.js"
|
|
128
|
+
},
|
|
129
|
+
"./wire-tab-scaffold": {
|
|
130
|
+
"types": "./dist/wire-tab-scaffold.d.ts",
|
|
131
|
+
"import": "./dist/wire-tab-scaffold.js"
|
|
109
132
|
},
|
|
110
133
|
"./loading-spinner": {
|
|
111
134
|
"types": "./dist/loading-spinner.d.ts",
|
|
@@ -149,10 +172,10 @@
|
|
|
149
172
|
"browser": "./dist/browser/empty-state.js",
|
|
150
173
|
"import": "./dist/empty-state.js"
|
|
151
174
|
},
|
|
152
|
-
"./
|
|
153
|
-
"types": "./dist/
|
|
154
|
-
"browser": "./dist/browser/
|
|
155
|
-
"import": "./dist/
|
|
175
|
+
"./panel-header": {
|
|
176
|
+
"types": "./dist/panel-header.d.ts",
|
|
177
|
+
"browser": "./dist/browser/panel-header.js",
|
|
178
|
+
"import": "./dist/panel-header.js"
|
|
156
179
|
},
|
|
157
180
|
"./value-table": {
|
|
158
181
|
"types": "./dist/value-table.d.ts",
|
|
@@ -178,14 +201,17 @@
|
|
|
178
201
|
"./resizable-region.css": "./dist/styles/resizable-region.css",
|
|
179
202
|
"./app-tab.css": "./dist/styles/app-tab.css",
|
|
180
203
|
"./tab-bar.css": "./dist/styles/tab-bar.css",
|
|
181
|
-
"./
|
|
204
|
+
"./nav-stack.css": "./dist/styles/nav-stack.css",
|
|
205
|
+
"./split-view.css": "./dist/styles/split-view.css",
|
|
206
|
+
"./workbench.css": "./dist/styles/workbench.css",
|
|
207
|
+
"./tab-scaffold.css": "./dist/styles/tab-scaffold.css",
|
|
182
208
|
"./loading-spinner.css": "./dist/styles/loading-spinner.css",
|
|
183
209
|
"./select.css": "./dist/styles/select.css",
|
|
184
210
|
"./segmented-control.css": "./dist/styles/segmented-control.css",
|
|
185
211
|
"./token-search-field.css": "./dist/styles/token-search-field.css",
|
|
186
212
|
"./state-banner.css": "./dist/styles/state-banner.css",
|
|
187
213
|
"./empty-state.css": "./dist/styles/empty-state.css",
|
|
188
|
-
"./
|
|
214
|
+
"./panel-header.css": "./dist/styles/panel-header.css",
|
|
189
215
|
"./value-table.css": "./dist/styles/value-table.css",
|
|
190
216
|
"./ai/*": "./ai/*",
|
|
191
217
|
"./docs/*": "./docs/*"
|
|
@@ -4,7 +4,7 @@ import './recipes.css';
|
|
|
4
4
|
import { LucideIcon } from '@kerfjs/ui/lucide-icon';
|
|
5
5
|
import { MenuHeader } from '@kerfjs/ui/menu-header';
|
|
6
6
|
import { MenuItem } from '@kerfjs/ui/menu-item';
|
|
7
|
-
import {
|
|
7
|
+
import { PanelHeader } from '@kerfjs/ui/panel-header';
|
|
8
8
|
import { ResizableRegion } from '@kerfjs/ui/resizable-region';
|
|
9
9
|
import { Toolbar } from '@kerfjs/ui/toolbar';
|
|
10
10
|
import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
|
|
@@ -24,8 +24,8 @@ export const createRecipe: RecipeFactory = (announce) => {
|
|
|
24
24
|
<Toolbar label="Atlas workspace" leading={<ToolbarControlGroup appearance="borderless" single><ToolbarText text="Atlas" /></ToolbarControlGroup>} trailing={<div class="recipe-shell__toolbar-actions"><ToolbarControlGroup className="recipe-shell__responsive-controls" appearance="borderless" label="Workspace panes"><button type="button" aria-label="Show navigation" aria-controls="recipe-shell-navigation" aria-pressed={String(responsivePane.value === 'navigation')} data-action="recipe-action" data-recipe-command="show-navigation"><LucideIcon icon={Folder} name="folder" /></button><button type="button" aria-label="Show content" aria-controls="recipe-shell-content" aria-pressed={String(responsivePane.value === 'content')} data-action="recipe-action" data-recipe-command="show-content"><LucideIcon icon={Inbox} name="inbox" /></button><button type="button" aria-label="Show inspector" aria-controls="recipe-shell-inspector" aria-pressed={String(responsivePane.value === 'inspector')} data-action="recipe-action" data-recipe-command="show-inspector"><LucideIcon icon={Settings} name="settings" /></button></ToolbarControlGroup><ToolbarControlGroup appearance="borderless" label="Workspace controls"><button type="button" aria-label="Notifications" data-action="recipe-action" data-recipe-command="notify"><LucideIcon icon={Bell} name="bell" /></button><button type="button" aria-label="Settings" data-action="recipe-action" data-recipe-command="settings"><LucideIcon icon={Settings} name="settings" /></button></ToolbarControlGroup></div>} />
|
|
25
25
|
<div class="recipe-shell__body">
|
|
26
26
|
<div id="recipe-shell-navigation" class="recipe-shell__nav-region"><ResizableRegion id="recipe-navigation" label="Navigation" size={navigationSize.value} min={180} max={320}><aside class="recipe-shell__nav kui-pane"><MenuHeader label="Workspace" /><nav class="recipe-shell__nav-list kui-pane__content kui-content" aria-label="Workspace"><section><MenuItem action="recipe-action" itemId="inbox" label="Inbox" icon={<LucideIcon icon={Inbox} name="inbox" />} selected={selected.value === 'inbox'} /><MenuItem action="recipe-action" itemId="projects" label="Projects with a deliberately wrapping title" icon={<LucideIcon icon={Folder} name="folder" />} selected={selected.value === 'projects'} multiline /></section></nav></aside></ResizableRegion></div>
|
|
27
|
-
<main id="recipe-shell-content" class="recipe-shell__main kui-pane"><
|
|
28
|
-
<div id="recipe-shell-inspector" class="recipe-shell__inspector-region"><ResizableRegion id="recipe-inspector" label="Inspector" size={inspectorSize.value} min={200} max={360} edge="start"><aside class="recipe-shell__inspector kui-pane"><
|
|
27
|
+
<main id="recipe-shell-content" class="recipe-shell__main kui-pane"><PanelHeader title={selected.value === 'inbox' ? 'Inbox triage' : 'Active projects'} titleId="recipe-shell-main-title" actions={<button class="kui-recipe__button" data-primary="true" type="button" data-action="recipe-action" data-recipe-command="new">New task</button>} /><div class="recipe-shell__main-body kui-pane__content kui-content"><p class="kui-recipe__ownership kui-content-item">Recipe owns pane geometry and one scroll owner per pane. The app owns routing, data, pane visibility, sizes, and persistence.</p><div class="recipe-shell__cards">{['Release accessibility audit', 'Prepare tablet navigation', 'Review stale-data states', 'Confirm package boundaries'].map((title) => <article class="recipe-shell__card kui-content-item"><strong>{title}</strong><p class="kui-recipe__muted">Assigned to the interface systems team · due this week</p></article>)}</div></div></main>
|
|
28
|
+
<div id="recipe-shell-inspector" class="recipe-shell__inspector-region"><ResizableRegion id="recipe-inspector" label="Inspector" size={inspectorSize.value} min={200} max={360} edge="start"><aside class="recipe-shell__inspector kui-pane"><PanelHeader title="Inspector" titleId="recipe-shell-inspector-title" /><div class="recipe-shell__inspector-body kui-pane__content kui-content"><ValueTable label="Selected task"><ValueTableRow label="Status" value="In review" /><ValueTableRow label="Owner" value="Mara Chen" /><ValueTableRow label="Priority" value="High" /></ValueTable></div></aside></ResizableRegion></div>
|
|
29
29
|
</div>
|
|
30
30
|
</section>;
|
|
31
31
|
return { render, action(command, element) { const pane = command.match(/^show-(navigation|content|inspector)$/)?.[1] as typeof responsivePane.value | undefined; if (pane) responsivePane.value = pane; const id = element.dataset.itemId; if (id) selected.value = id; announce(id ? `Opened ${id}` : pane ? `Showing ${pane}` : command === 'new' ? 'New task requested' : `${command} requested`); }, resize(id, size) { if (id === 'recipe-navigation') navigationSize.value = size; if (id === 'recipe-inspector') inspectorSize.value = size; announce(`${id} resized to ${size}px`); } };
|
|
@@ -6,7 +6,7 @@ import '@awesome.me/webawesome/dist/components/input/input.js';
|
|
|
6
6
|
import '@awesome.me/webawesome/dist/components/textarea/textarea.js';
|
|
7
7
|
import './recipes.css';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { PanelHeader } from '@kerfjs/ui/panel-header';
|
|
10
10
|
import { Select } from '@kerfjs/ui/select';
|
|
11
11
|
import { StateBanner } from '@kerfjs/ui/state-banner';
|
|
12
12
|
import { signal } from 'kerfjs';
|
|
@@ -30,7 +30,7 @@ export const createRecipe: RecipeFactory = (announce) => {
|
|
|
30
30
|
const audience = signal('team');
|
|
31
31
|
const status = signal<'idle' | 'error' | 'saved'>('idle');
|
|
32
32
|
const render = () => <form class="kui-recipe recipe-form kui-recipe__surface kui-content" data-recipe="recipe-composer-form" aria-labelledby="recipe-composer-title" aria-describedby="recipe-composer-summary" noValidate>
|
|
33
|
-
<
|
|
33
|
+
<PanelHeader title="Publish workspace update" titleId="recipe-composer-title" summary="Share a concise, actionable update with collaborators." summaryId="recipe-composer-summary" />
|
|
34
34
|
{status.value === 'error' && <StateBanner title="Add a title before publishing" detail="The update body and audience are preserved." tone="danger" urgency="alert" />}
|
|
35
35
|
{status.value === 'saved' && <StateBanner title="Update published" detail="The team audience can now read it." tone="success" />}
|
|
36
36
|
<div class="recipe-form__section recipe-form__fields">
|
|
@@ -3,7 +3,7 @@ import './recipes.css';
|
|
|
3
3
|
|
|
4
4
|
import { EmptyState } from '@kerfjs/ui/empty-state';
|
|
5
5
|
import { LoadingSpinner } from '@kerfjs/ui/loading-spinner';
|
|
6
|
-
import {
|
|
6
|
+
import { PanelHeader } from '@kerfjs/ui/panel-header';
|
|
7
7
|
import { StateBanner } from '@kerfjs/ui/state-banner';
|
|
8
8
|
import { signal } from 'kerfjs';
|
|
9
9
|
|
|
@@ -21,6 +21,6 @@ export const createRecipe: RecipeFactory = (announce) => {
|
|
|
21
21
|
if (state.value === 'error') return <StateBanner title="Release tasks could not be refreshed" detail="Existing filters are preserved. Try again when the connection recovers." tone="danger" urgency="alert" action={<button class="kui-recipe__button" type="button" data-action="recipe-action" data-recipe-command="retry">Retry</button>} />;
|
|
22
22
|
return <>{state.value === 'stale' && <StateBanner title="Showing saved results" detail="Refreshing in the background." tone="warning" action={<span class="kui-inline-metadata"><LoadingSpinner label="Refreshing release tasks" /><button class="kui-recipe__button" type="button" data-action="recipe-action" data-recipe-command="finish">Finish refresh</button></span>} />}{populated()}</>;
|
|
23
23
|
};
|
|
24
|
-
const render = () => <section class="kui-recipe recipe-list kui-recipe__surface kui-pane" data-recipe="recipe-list-workspace-states" data-list-state={state.value}><
|
|
24
|
+
const render = () => <section class="kui-recipe recipe-list kui-recipe__surface kui-pane" data-recipe="recipe-list-workspace-states" data-list-state={state.value}><PanelHeader title="Release tasks" titleId="recipe-list-title" actions={<div class="kui-control-cluster"><button class="kui-recipe__button" type="button" data-action="recipe-action" data-recipe-command="empty">Clear</button><button class="kui-recipe__button" type="button" data-action="recipe-action" data-recipe-command="fail">Simulate failure</button><button class="kui-recipe__button" data-primary="true" type="button" data-action="recipe-action" data-recipe-command="refresh">Refresh</button></div>} /><div class="recipe-list__body kui-pane__content kui-content">{renderBody()}<p class="kui-recipe__ownership kui-content-item">The recipe owns feedback placement and stable content. The app owns fetching, cache age, retry policy, and domain rows.</p></div></section>;
|
|
25
25
|
return { render, action(command) { const next: Record<string, ListState> = { load: 'populated', empty: 'empty', create: 'populated', fail: 'error', retry: 'populated', refresh: 'stale', finish: 'populated' }; if (command in next) state.value = next[command]!; announce(`List state: ${state.value}`); } };
|
|
26
26
|
};
|
|
@@ -8,6 +8,7 @@ export const recipeIds = [
|
|
|
8
8
|
'recipe-composer-form',
|
|
9
9
|
'recipe-list-workspace-states',
|
|
10
10
|
'recipe-compact-toolbar',
|
|
11
|
+
'recipe-navigation-stack',
|
|
11
12
|
] as const;
|
|
12
13
|
|
|
13
14
|
export type RecipeId = typeof recipeIds[number];
|
|
@@ -20,6 +21,7 @@ export const recipeLoaders: Record<RecipeId, () => Promise<{ createRecipe: Recip
|
|
|
20
21
|
'recipe-composer-form': () => import('./composer-form.js'),
|
|
21
22
|
'recipe-list-workspace-states': () => import('./list-workspace-states.js'),
|
|
22
23
|
'recipe-compact-toolbar': () => import('./compact-toolbar.js'),
|
|
24
|
+
'recipe-navigation-stack': () => import('./navigation-stack.js'),
|
|
23
25
|
};
|
|
24
26
|
|
|
25
27
|
export function isRecipeId(value: string): value is RecipeId {
|
|
@@ -4,10 +4,10 @@ import '@awesome.me/webawesome/dist/components/button/button.js';
|
|
|
4
4
|
import '@awesome.me/webawesome/dist/components/dialog/dialog.js';
|
|
5
5
|
import './recipes.css';
|
|
6
6
|
|
|
7
|
-
import { DialogHeader } from '@kerfjs/ui/dialog-header';
|
|
8
7
|
import { LucideIcon } from '@kerfjs/ui/lucide-icon';
|
|
9
8
|
import { MenuHeader } from '@kerfjs/ui/menu-header';
|
|
10
9
|
import { MenuItem } from '@kerfjs/ui/menu-item';
|
|
10
|
+
import { PanelHeader } from '@kerfjs/ui/panel-header';
|
|
11
11
|
import { ValueTable, ValueTableRow } from '@kerfjs/ui/value-table';
|
|
12
12
|
import { signal } from 'kerfjs';
|
|
13
13
|
import { FileText } from 'lucide';
|
|
@@ -27,7 +27,7 @@ interface RecipeDialogElement extends HTMLElement {
|
|
|
27
27
|
export const createRecipe: RecipeFactory = (announce) => {
|
|
28
28
|
const selected = signal<keyof typeof records>('alpha');
|
|
29
29
|
const open = { value: false };
|
|
30
|
-
const render = () => <section class="kui-recipe kui-content" data-recipe="recipe-master-detail-dialog"><button class="kui-recipe__button kui-content-item" data-primary="true" type="button" data-action="recipe-action" data-recipe-command="open">Open project details</button><p class="kui-recipe__ownership kui-content-item">Web Awesome owns modal focus and dismissal. The recipe owns header/body/master-detail anatomy; the app owns open state, selection, and policy.</p><wa-dialog class="recipe-dialog" label="Project details" without-header open={open.value}><div class="recipe-dialog__pane kui-pane"><
|
|
30
|
+
const render = () => <section class="kui-recipe kui-content" data-recipe="recipe-master-detail-dialog"><button class="kui-recipe__button kui-content-item" data-primary="true" type="button" data-action="recipe-action" data-recipe-command="open">Open project details</button><p class="kui-recipe__ownership kui-content-item">Web Awesome owns modal focus and dismissal. The recipe owns header/body/master-detail anatomy; the app owns open state, selection, and policy.</p><wa-dialog class="recipe-dialog" label="Project details" without-header open={open.value}><div class="recipe-dialog__pane kui-pane"><PanelHeader title="Project details" titleId="recipe-dialog-title" summary="Compare delivery state without leaving the workspace." summaryId="recipe-dialog-summary" icon={<LucideIcon icon={FileText} name="file-text" />} actions={<wa-button appearance="plain" data-action="recipe-action" data-recipe-command="close">Close</wa-button>} /><div class="recipe-master-detail kui-pane__content"><nav class="recipe-master-detail__master kui-pane" aria-label="Projects"><MenuHeader label="Recent projects" /><div class="kui-pane__content kui-content"><section>{Object.entries(records).map(([id, record]) => <MenuItem action="recipe-action" itemId={id} label={record.name} selected={selected.value === id} multiline />)}</section></div></nav><section class="recipe-master-detail__detail kui-content" aria-live="polite"><h3 class="recipe-master-detail__title kui-recipe__pane-title kui-content-item">{records[selected.value].name}</h3><ValueTable label="Project details"><ValueTableRow label="Owner" value={records[selected.value].owner} /><ValueTableRow label="Status" value={records[selected.value].state} /><ValueTableRow label="Updated" value={records[selected.value].updated} /></ValueTable><div class="recipe-master-detail__actions kui-control-cluster"><wa-button appearance="outlined" data-action="recipe-action" data-recipe-command="archive">Archive</wa-button><wa-button variant="brand" appearance="accent" data-action="recipe-action" data-recipe-command="open-record">Open project</wa-button></div></section></div></div></wa-dialog></section>;
|
|
31
31
|
return {
|
|
32
32
|
render,
|
|
33
33
|
action(command, element) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { wireNavStack } from '@kerfjs/ui/wire-nav-stack';
|
|
1
2
|
import { wireResizableRegions } from '@kerfjs/ui/wire-resizable-regions';
|
|
2
3
|
import { delegate, mount } from 'kerfjs';
|
|
3
4
|
import { delegateActions } from 'kerfjs/actions';
|
|
@@ -23,11 +24,13 @@ export function mountRecipe(root: HTMLElement, controller: RecipeController): ()
|
|
|
23
24
|
const stopResize = wireResizableRegions(root, {
|
|
24
25
|
onCommit: ({ id, size }) => controller.resize?.(id, size),
|
|
25
26
|
});
|
|
27
|
+
const stopNav = wireNavStack(root, { onBack: () => controller.action('nav-back', root) });
|
|
26
28
|
|
|
27
29
|
let disposed = false;
|
|
28
30
|
return () => {
|
|
29
31
|
if (disposed) return;
|
|
30
32
|
disposed = true;
|
|
33
|
+
stopNav();
|
|
31
34
|
stopResize();
|
|
32
35
|
stopDialogs();
|
|
33
36
|
stopInputs();
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import '@kerfjs/ui/layout.css';
|
|
2
|
+
import '@kerfjs/ui/nav-stack.css';
|
|
3
|
+
import '@kerfjs/ui/toolbar-text.css';
|
|
4
|
+
import './recipes.css';
|
|
5
|
+
|
|
6
|
+
import { LucideIcon } from '@kerfjs/ui/lucide-icon';
|
|
7
|
+
import { MenuHeader } from '@kerfjs/ui/menu-header';
|
|
8
|
+
import { MenuItem } from '@kerfjs/ui/menu-item';
|
|
9
|
+
import { NavStack, type NavStackView } from '@kerfjs/ui/nav-stack';
|
|
10
|
+
import { signal } from 'kerfjs';
|
|
11
|
+
import { ChevronRight, FileText, Folder } from 'lucide';
|
|
12
|
+
|
|
13
|
+
import type { RecipeFactory } from './types.js';
|
|
14
|
+
|
|
15
|
+
interface LibraryItem {
|
|
16
|
+
id: string;
|
|
17
|
+
label: string;
|
|
18
|
+
detail: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const ITEMS: LibraryItem[] = [
|
|
22
|
+
{ id: 'tokens', label: 'Design tokens', detail: 'Semantic color, spacing, and radius scales shared across every surface.' },
|
|
23
|
+
{ id: 'controls', label: 'Toolbar controls', detail: 'Control groups and segmented controls with their hover and pressed contracts.' },
|
|
24
|
+
{ id: 'layouts', label: 'App layouts', detail: 'Navigation stacks, split views, workbench, and bottom-tab scaffolds.' },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
export const createRecipe: RecipeFactory = (announce) => {
|
|
28
|
+
const listView = (): NavStackView => ({
|
|
29
|
+
key: 'library',
|
|
30
|
+
title: 'Library',
|
|
31
|
+
content: <div class="recipe-navstack__view kui-pane__content kui-content">
|
|
32
|
+
<section>
|
|
33
|
+
<MenuHeader label="Components" />
|
|
34
|
+
{ITEMS.map((item) => <MenuItem action="recipe-action" itemId={item.id} label={item.label} icon={<LucideIcon icon={Folder} name="folder" />} trailing={<LucideIcon icon={ChevronRight} name="chevron-right" />} />)}
|
|
35
|
+
</section>
|
|
36
|
+
<p class="kui-recipe__ownership kui-content-item">The recipe owns the stack as a signal of views and pushes/pops it; `NavStack` renders the stack and `wireNavStack` slides the content and settles the chrome. The app owns selection, data, and routing.</p>
|
|
37
|
+
</div>,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const detailView = (item: LibraryItem): NavStackView => ({
|
|
41
|
+
key: item.id,
|
|
42
|
+
title: item.label,
|
|
43
|
+
content: <div class="recipe-navstack__view kui-pane__content kui-content">
|
|
44
|
+
<div class="kui-content-item">
|
|
45
|
+
<LucideIcon icon={FileText} name="file-text" />
|
|
46
|
+
<strong>{item.label}</strong>
|
|
47
|
+
<p class="kui-recipe__muted">{item.detail}</p>
|
|
48
|
+
</div>
|
|
49
|
+
</div>,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const views = signal<NavStackView[]>([listView()]);
|
|
53
|
+
|
|
54
|
+
const render = () => <section class="kui-recipe recipe-navstack kui-recipe__surface" data-recipe="recipe-navigation-stack">
|
|
55
|
+
<div class="recipe-navstack__stage"><NavStack id="recipe-nav" label="Component library" views={views.value} /></div>
|
|
56
|
+
</section>;
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
render,
|
|
60
|
+
action(command, element) {
|
|
61
|
+
if (command === 'nav-back') {
|
|
62
|
+
if (views.value.length > 1) {
|
|
63
|
+
views.value = views.value.slice(0, -1);
|
|
64
|
+
announce('Navigated back');
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const id = element.dataset.itemId;
|
|
69
|
+
const item = ITEMS.find((candidate) => candidate.id === id);
|
|
70
|
+
if (item) {
|
|
71
|
+
views.value = [...views.value, detailView(item)];
|
|
72
|
+
announce(`Opened ${item.label}`);
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
};
|
|
@@ -113,9 +113,6 @@
|
|
|
113
113
|
.recipe-header__context {
|
|
114
114
|
margin: 0;
|
|
115
115
|
}
|
|
116
|
-
.recipe-header .kui-page-header__action {
|
|
117
|
-
max-width: 100%;
|
|
118
|
-
}
|
|
119
116
|
|
|
120
117
|
.recipe-master-detail {
|
|
121
118
|
display: grid;
|
|
@@ -140,7 +137,7 @@
|
|
|
140
137
|
--spacing: 0;
|
|
141
138
|
--width: min(62rem, calc(100vw - 2rem));
|
|
142
139
|
}
|
|
143
|
-
.recipe-dialog .kui-
|
|
140
|
+
.recipe-dialog .kui-panel-header {
|
|
144
141
|
margin-inline: 0;
|
|
145
142
|
}
|
|
146
143
|
|
|
@@ -206,8 +203,11 @@
|
|
|
206
203
|
gap: var(--kui-layout-content-gap);
|
|
207
204
|
}
|
|
208
205
|
.recipe-compact-toolbar__guide {
|
|
206
|
+
/* Span the toolbar's full width instead of the 8px content-item inset — under a
|
|
207
|
+
full-bleed toolbar/divider the inset read as the boxes floating too far in. */
|
|
209
208
|
display: grid;
|
|
210
209
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
210
|
+
margin-inline: 0;
|
|
211
211
|
gap: var(--kui-layout-control-gap);
|
|
212
212
|
}
|
|
213
213
|
.recipe-compact-toolbar__guide > div {
|
|
@@ -321,10 +321,6 @@
|
|
|
321
321
|
.recipe-compact-toolbar__guide {
|
|
322
322
|
grid-template-columns: 1fr;
|
|
323
323
|
}
|
|
324
|
-
.recipe-header .kui-page-header {
|
|
325
|
-
align-items: flex-start;
|
|
326
|
-
flex-direction: column;
|
|
327
|
-
}
|
|
328
324
|
}
|
|
329
325
|
|
|
330
326
|
@media (forced-colors: active) {
|
|
@@ -342,3 +338,18 @@
|
|
|
342
338
|
transition-duration: 1ms !important;
|
|
343
339
|
}
|
|
344
340
|
}
|
|
341
|
+
|
|
342
|
+
.recipe-navstack {
|
|
343
|
+
display: flex;
|
|
344
|
+
width: min(28rem, 100%);
|
|
345
|
+
margin-inline: auto;
|
|
346
|
+
flex-direction: column;
|
|
347
|
+
}
|
|
348
|
+
.recipe-navstack__stage {
|
|
349
|
+
height: 24rem;
|
|
350
|
+
}
|
|
351
|
+
.recipe-navstack__view {
|
|
352
|
+
/* The view is a .kui-content stack whose rows own their 8px margins; add only a
|
|
353
|
+
little block breathing room, not an inline pad that would double-inset them. */
|
|
354
|
+
padding-block: var(--kui-space-2xs);
|
|
355
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import '@kerfjs/ui/layout.css';
|
|
2
2
|
import './recipes.css';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { PanelHeader } from '@kerfjs/ui/panel-header';
|
|
5
5
|
import { StateBanner } from '@kerfjs/ui/state-banner';
|
|
6
6
|
|
|
7
7
|
import type { RecipeFactory } from './types.js';
|
|
8
8
|
|
|
9
9
|
export const createRecipe: RecipeFactory = (announce) => ({
|
|
10
|
-
render: () => <section class="kui-recipe recipe-header kui-recipe__surface kui-content" data-recipe="recipe-workspace-header"><p class="recipe-header__context kui-inline-metadata kui-content-item"><span>Northstar workspace</span><span>·</span><span>Product planning</span></p><
|
|
10
|
+
render: () => <section class="kui-recipe recipe-header kui-recipe__surface kui-content" data-recipe="recipe-workspace-header"><p class="recipe-header__context kui-inline-metadata kui-content-item"><span>Northstar workspace</span><span>·</span><span>Product planning</span></p><PanelHeader title="Accessibility readiness and responsive navigation rollout" titleId="recipe-workspace-title" actions={<div class="kui-control-cluster"><button class="kui-recipe__button" type="button" data-action="recipe-action" data-recipe-command="share">Share</button><button class="kui-recipe__button" type="button" data-action="recipe-action" data-recipe-command="more">More actions</button><button class="kui-recipe__button" data-primary="true" type="button" data-action="recipe-action" data-recipe-command="publish">Publish update</button></div>} /><StateBanner title="Ready for review" detail="All required checks passed 18 minutes ago." tone="success" /><p class="kui-recipe__ownership kui-content-item">The recipe owns page hierarchy and action relocation. The app owns authorization, command behavior, breadcrumbs, and product copy.</p></section>,
|
|
11
11
|
action(command) { announce(`${command} requested`); },
|
|
12
12
|
});
|
package/dist/chunk-2PES33HS.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { jsxs, jsx } from 'kerfjs/jsx-runtime';
|
|
2
|
-
|
|
3
|
-
// src/page-header.tsx
|
|
4
|
-
function PageHeader({ title, action }) {
|
|
5
|
-
return /* @__PURE__ */ jsxs("header", { class: "kui-page-header", "data-component": "page-header", children: [
|
|
6
|
-
/* @__PURE__ */ jsx("h1", { children: title }),
|
|
7
|
-
action && /* @__PURE__ */ jsx("div", { class: "kui-page-header__action", children: action })
|
|
8
|
-
] });
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export { PageHeader };
|
|
12
|
-
|
|
13
|
-
//# sourceMappingURL=chunk-2PES33HS.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/page-header.tsx"],"names":[],"mappings":";;;AAOO,SAAS,UAAA,CAAW,EAAE,KAAA,EAAO,MAAA,EAAO,EAAoB;AAC7D,EAAA,uBAAO,IAAA,CAAC,QAAA,EAAA,EAAO,KAAA,EAAM,iBAAA,EAAkB,kBAAe,aAAA,EAAc,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,QAAI,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,IAAM,MAAA,oBAAU,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,2BAA2B,QAAA,EAAA,MAAA,EAAO;AAAA,GAAA,EAAO,CAAA;AACrJ","file":"chunk-2PES33HS.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nexport interface PageHeaderProps {\n title: string | SafeHtml;\n action?: SafeHtml;\n}\n\nexport function PageHeader({ title, action }: PageHeaderProps) {\n return <header class=\"kui-page-header\" data-component=\"page-header\"><h1>{title}</h1>{action && <div class=\"kui-page-header__action\">{action}</div>}</header>;\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/toolbar-text.tsx"],"names":[],"mappings":";;;AAQO,SAAS,YAAY,EAAE,IAAA,EAAM,OAAO,SAAA,EAAW,SAAA,GAAY,IAAG,EAAqB;AACxF,EAAA,uBAAO,GAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAO,CAAA,iBAAA,EAAoB,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK,EAAG,gBAAA,EAAe,cAAA,EAAe,WAAA,EAAW,IAAA,EAAO,QAAA,EAAA,IAAA,EAAK,CAAA;AACnH","file":"chunk-GY5WH7TO.js","sourcesContent":["export type ToolbarTextSize = 'large' | 'default' | 'small';\n\nexport interface ToolbarTextProps {\n text: string;\n size?: ToolbarTextSize;\n className?: string;\n}\n\nexport function ToolbarText({ text, size = 'default', className = '' }: ToolbarTextProps) {\n return <span class={`kui-toolbar-text ${className}`.trim()} data-component=\"toolbar-text\" data-size={size}>{text}</span>;\n}\n"]}
|
package/dist/chunk-H5AGGVU5.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { placeTokenSearchCaret, readTokenSearchField } from './chunk-7LDCTU7D.js';
|
|
2
|
-
import { delegate } from 'kerfjs';
|
|
3
|
-
|
|
4
|
-
function editorFromEvent(root, event) {
|
|
5
|
-
const target = event.target;
|
|
6
|
-
if (!(target instanceof Element)) return;
|
|
7
|
-
const editor = target.closest("[data-token-search-editor]");
|
|
8
|
-
return editor && root.contains(editor) ? editor : void 0;
|
|
9
|
-
}
|
|
10
|
-
function caretQueryOffset(editor) {
|
|
11
|
-
const selection = editor.ownerDocument.getSelection();
|
|
12
|
-
if (!selection || selection.rangeCount === 0) return;
|
|
13
|
-
const selectionRange = selection.getRangeAt(0);
|
|
14
|
-
if (!editor.contains(selectionRange.startContainer)) return;
|
|
15
|
-
const prefixRange = editor.ownerDocument.createRange();
|
|
16
|
-
prefixRange.selectNodeContents(editor);
|
|
17
|
-
prefixRange.setEnd(selectionRange.startContainer, selectionRange.startOffset);
|
|
18
|
-
const prefix = editor.ownerDocument.createElement("div");
|
|
19
|
-
prefix.append(prefixRange.cloneContents());
|
|
20
|
-
return readTokenSearchField(prefix).query.length;
|
|
21
|
-
}
|
|
22
|
-
function replacementEditor(root, id) {
|
|
23
|
-
return [...root.querySelectorAll("[data-token-search-editor]")].find((editor) => editor.dataset.tokenSearchEditor === id);
|
|
24
|
-
}
|
|
25
|
-
function wireTokenSearchFields(root, { onSubmit }) {
|
|
26
|
-
const pending = /* @__PURE__ */ new WeakMap();
|
|
27
|
-
const onBeforeInput = (event) => {
|
|
28
|
-
const inputEvent = event;
|
|
29
|
-
const editor = editorFromEvent(root, event);
|
|
30
|
-
if (!editor || !inputEvent.inputType.startsWith("delete") || editor.ownerDocument.activeElement !== editor) return;
|
|
31
|
-
const field = editor.closest('[data-component="token-search-field"]');
|
|
32
|
-
const id = field?.dataset.tokenSearchId;
|
|
33
|
-
const offset = caretQueryOffset(editor);
|
|
34
|
-
if (!id || offset === void 0 || field?.dataset.disabled === "true") return;
|
|
35
|
-
pending.set(editor, {
|
|
36
|
-
id,
|
|
37
|
-
offset,
|
|
38
|
-
tokenCount: editor.querySelectorAll('[data-component="token-search-token"]').length
|
|
39
|
-
});
|
|
40
|
-
};
|
|
41
|
-
const onInput = (event) => {
|
|
42
|
-
const editor = editorFromEvent(root, event);
|
|
43
|
-
if (!editor) return;
|
|
44
|
-
const deletion = pending.get(editor);
|
|
45
|
-
pending.delete(editor);
|
|
46
|
-
if (!deletion || editor.querySelectorAll('[data-component="token-search-token"]').length >= deletion.tokenCount) return;
|
|
47
|
-
editor.ownerDocument.defaultView.requestAnimationFrame(() => {
|
|
48
|
-
const replacement = replacementEditor(root, deletion.id);
|
|
49
|
-
if (!replacement) return;
|
|
50
|
-
const active = replacement.ownerDocument.activeElement;
|
|
51
|
-
if (active !== editor && active !== replacement && active !== replacement.ownerDocument.body) return;
|
|
52
|
-
placeTokenSearchCaret(replacement, deletion.offset);
|
|
53
|
-
});
|
|
54
|
-
};
|
|
55
|
-
root.addEventListener("beforeinput", onBeforeInput, true);
|
|
56
|
-
root.addEventListener("input", onInput, true);
|
|
57
|
-
const stopSubmit = delegate(root, "keydown", "[data-token-search-editor]", (event, element) => {
|
|
58
|
-
const keyboardEvent = event;
|
|
59
|
-
const editor = element;
|
|
60
|
-
const field = editor.closest('[data-component="token-search-field"]');
|
|
61
|
-
const id = field?.dataset.tokenSearchId;
|
|
62
|
-
if (keyboardEvent.key !== "Enter" || keyboardEvent.isComposing || !id || field?.dataset.disabled === "true") return;
|
|
63
|
-
keyboardEvent.preventDefault();
|
|
64
|
-
onSubmit({ id, editor });
|
|
65
|
-
});
|
|
66
|
-
return () => {
|
|
67
|
-
root.removeEventListener("beforeinput", onBeforeInput, true);
|
|
68
|
-
root.removeEventListener("input", onInput, true);
|
|
69
|
-
stopSubmit();
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export { wireTokenSearchFields };
|
|
74
|
-
|
|
75
|
-
//# sourceMappingURL=chunk-H5AGGVU5.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/wire-token-search-fields.ts"],"names":[],"mappings":"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","file":"chunk-H5AGGVU5.js","sourcesContent":["import { delegate } from 'kerfjs';\n\nimport { placeTokenSearchCaret, readTokenSearchField } from './token-search-field.js';\n\nexport interface TokenSearchSubmit {\n id: string;\n editor: HTMLElement;\n}\n\nexport interface WireTokenSearchFieldsOptions {\n onSubmit: (submission: TokenSearchSubmit) => void;\n}\n\ninterface PendingTokenDeletion {\n id: string;\n offset: number;\n tokenCount: number;\n}\n\nfunction editorFromEvent(root: HTMLElement, event: Event): HTMLElement | undefined {\n const target = event.target;\n if (!(target instanceof Element)) return;\n const editor = target.closest<HTMLElement>('[data-token-search-editor]');\n return editor && root.contains(editor) ? editor : undefined;\n}\n\nfunction caretQueryOffset(editor: HTMLElement): number | undefined {\n const selection = editor.ownerDocument.getSelection();\n if (!selection || selection.rangeCount === 0) return;\n const selectionRange = selection.getRangeAt(0);\n if (!editor.contains(selectionRange.startContainer)) return;\n const prefixRange = editor.ownerDocument.createRange();\n prefixRange.selectNodeContents(editor);\n prefixRange.setEnd(selectionRange.startContainer, selectionRange.startOffset);\n const prefix = editor.ownerDocument.createElement('div');\n prefix.append(prefixRange.cloneContents());\n return readTokenSearchField(prefix).query.length;\n}\n\nfunction replacementEditor(root: HTMLElement, id: string): HTMLElement | undefined {\n return [...root.querySelectorAll<HTMLElement>('[data-token-search-editor]')]\n .find((editor) => editor.dataset.tokenSearchEditor === id);\n}\n\n/** Keep TokenSearchField wrapping, submit Enter, and preserve its caret across controlled token deletion. */\nexport function wireTokenSearchFields(root: HTMLElement, { onSubmit }: WireTokenSearchFieldsOptions) {\n const pending = new WeakMap<HTMLElement, PendingTokenDeletion>();\n const onBeforeInput = (event: Event) => {\n const inputEvent = event as InputEvent;\n const editor = editorFromEvent(root, event);\n if (!editor || !inputEvent.inputType.startsWith('delete') || editor.ownerDocument.activeElement !== editor) return;\n const field = editor.closest<HTMLElement>('[data-component=\"token-search-field\"]');\n const id = field?.dataset.tokenSearchId;\n const offset = caretQueryOffset(editor);\n if (!id || offset === undefined || field?.dataset.disabled === 'true') return;\n pending.set(editor, {\n id,\n offset,\n tokenCount: editor.querySelectorAll('[data-component=\"token-search-token\"]').length,\n });\n };\n const onInput = (event: Event) => {\n const editor = editorFromEvent(root, event);\n if (!editor) return;\n const deletion = pending.get(editor);\n pending.delete(editor);\n if (!deletion || editor.querySelectorAll('[data-component=\"token-search-token\"]').length >= deletion.tokenCount) return;\n editor.ownerDocument.defaultView!.requestAnimationFrame(() => {\n const replacement = replacementEditor(root, deletion.id);\n if (!replacement) return;\n const active = replacement.ownerDocument.activeElement;\n if (active !== editor && active !== replacement && active !== replacement.ownerDocument.body) return;\n placeTokenSearchCaret(replacement, deletion.offset);\n });\n };\n root.addEventListener('beforeinput', onBeforeInput, true);\n root.addEventListener('input', onInput, true);\n\n const stopSubmit = delegate(root, 'keydown', '[data-token-search-editor]', (event, element) => {\n const keyboardEvent = event as KeyboardEvent;\n const editor = element as HTMLElement;\n const field = editor.closest<HTMLElement>('[data-component=\"token-search-field\"]');\n const id = field?.dataset.tokenSearchId;\n if (keyboardEvent.key !== 'Enter' || keyboardEvent.isComposing || !id || field?.dataset.disabled === 'true') return;\n keyboardEvent.preventDefault();\n onSubmit({ id, editor });\n });\n return () => {\n root.removeEventListener('beforeinput', onBeforeInput, true);\n root.removeEventListener('input', onInput, true);\n stopSubmit();\n };\n}\n"]}
|
package/dist/chunk-K3G72I6D.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { ToolbarControlGroup } from './chunk-MFKMHK5W.js';
|
|
2
|
-
import { Toolbar } from './chunk-DAFFDO33.js';
|
|
3
|
-
import { jsxs, jsx } from 'kerfjs/jsx-runtime';
|
|
4
|
-
|
|
5
|
-
function DialogHeader({ title, titleId, summary, summaryId, icon, iconClassName = "", actions, actionsLabel }) {
|
|
6
|
-
const titleContent = /* @__PURE__ */ jsx("div", { class: "kui-dialog-header__copy", children: /* @__PURE__ */ jsx("h2", { id: titleId, children: title }) });
|
|
7
|
-
const identity = /* @__PURE__ */ jsx(
|
|
8
|
-
ToolbarControlGroup,
|
|
9
|
-
{
|
|
10
|
-
appearance: "borderless",
|
|
11
|
-
className: "kui-dialog-header__identity",
|
|
12
|
-
children: icon ? [/* @__PURE__ */ jsx("span", { class: `kui-dialog-header__icon ${iconClassName}`.trim(), children: icon }), titleContent] : titleContent
|
|
13
|
-
}
|
|
14
|
-
);
|
|
15
|
-
const actionGroup = actions && /* @__PURE__ */ jsx(ToolbarControlGroup, { className: "kui-dialog-header__actions", label: actionsLabel, children: actions });
|
|
16
|
-
return /* @__PURE__ */ jsxs("div", { class: "kui-dialog-header", "data-component": "dialog-header", "data-has-icon": String(Boolean(icon)), "data-has-actions": String(Boolean(actions)), "data-has-summary": String(Boolean(summary)), children: [
|
|
17
|
-
/* @__PURE__ */ jsx(Toolbar, { leading: identity, trailing: actionGroup, divider: false, className: "kui-dialog-header__toolbar" }),
|
|
18
|
-
summary && /* @__PURE__ */ jsx("p", { class: "kui-dialog-header__summary", id: summaryId, children: summary })
|
|
19
|
-
] });
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export { DialogHeader };
|
|
23
|
-
|
|
24
|
-
//# sourceMappingURL=chunk-K3G72I6D.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/dialog-header.tsx"],"names":[],"mappings":";;;;AAgBO,SAAS,YAAA,CAAa,EAAE,KAAA,EAAO,OAAA,EAAS,OAAA,EAAS,SAAA,EAAW,IAAA,EAAM,aAAA,GAAgB,EAAA,EAAI,OAAA,EAAS,YAAA,EAAa,EAAsB;AACvI,EAAA,MAAM,YAAA,mBAAe,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,yBAAA,EAA0B,8BAAC,IAAA,EAAA,EAAG,EAAA,EAAI,OAAA,EAAU,QAAA,EAAA,KAAA,EAAM,CAAA,EAAK,CAAA;AACvF,EAAA,MAAM,QAAA,mBAAW,GAAA;AAAA,IAAC,mBAAA;AAAA,IAAA;AAAA,MAChB,UAAA,EAAW,YAAA;AAAA,MACX,SAAA,EAAU,6BAAA;AAAA,MACV,QAAA,EAAU,IAAA,GAAO,iBAAC,GAAA,CAAC,UAAK,KAAA,EAAO,CAAA,wBAAA,EAA2B,aAAa,CAAA,CAAA,CAAG,IAAA,EAAK,EAAI,QAAA,EAAA,IAAA,EAAK,CAAA,EAAS,YAAY,CAAA,GAAI;AAAA;AAAA,GACnH;AACA,EAAA,MAAM,WAAA,GAAc,2BAAW,GAAA,CAAC,mBAAA,EAAA,EAAoB,WAAU,4BAAA,EAA6B,KAAA,EAAO,cAAe,QAAA,EAAA,OAAA,EAAQ,CAAA;AAEzH,EAAA,uBAAO,IAAA,CAAC,SAAI,KAAA,EAAM,mBAAA,EAAoB,kBAAe,eAAA,EAAgB,eAAA,EAAe,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAC,GAAG,kBAAA,EAAkB,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAC,CAAA,EAAG,oBAAkB,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAC,CAAA,EAC9L,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,OAAA,EAAA,EAAQ,SAAS,QAAA,EAAU,QAAA,EAAU,aAAa,OAAA,EAAS,KAAA,EAAO,WAAU,4BAAA,EAA6B,CAAA;AAAA,IACzG,2BAAW,GAAA,CAAC,GAAA,EAAA,EAAE,OAAM,4BAAA,EAA6B,EAAA,EAAI,WAAY,QAAA,EAAA,OAAA,EAAQ;AAAA,GAAA,EAC5E,CAAA;AACF","file":"chunk-K3G72I6D.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport { Toolbar } from './toolbar.js';\nimport { ToolbarControlGroup } from './toolbar-control-group.js';\n\nexport interface DialogHeaderProps {\n title: string;\n titleId: string;\n summary?: string;\n summaryId?: string;\n icon?: SafeHtml;\n iconClassName?: string;\n actions?: SafeHtml;\n actionsLabel?: string;\n}\n\nexport function DialogHeader({ title, titleId, summary, summaryId, icon, iconClassName = '', actions, actionsLabel }: DialogHeaderProps) {\n const titleContent = <div class=\"kui-dialog-header__copy\"><h2 id={titleId}>{title}</h2></div>;\n const identity = <ToolbarControlGroup\n appearance=\"borderless\"\n className=\"kui-dialog-header__identity\"\n children={icon ? [<span class={`kui-dialog-header__icon ${iconClassName}`.trim()}>{icon}</span>, titleContent] : titleContent}\n />;\n const actionGroup = actions && <ToolbarControlGroup className=\"kui-dialog-header__actions\" label={actionsLabel}>{actions}</ToolbarControlGroup>;\n\n return <div class=\"kui-dialog-header\" data-component=\"dialog-header\" data-has-icon={String(Boolean(icon))} data-has-actions={String(Boolean(actions))} data-has-summary={String(Boolean(summary))}>\n <Toolbar leading={identity} trailing={actionGroup} divider={false} className=\"kui-dialog-header__toolbar\" />\n {summary && <p class=\"kui-dialog-header__summary\" id={summaryId}>{summary}</p>}\n </div>;\n}\n"]}
|
package/dist/dialog-header.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { SafeHtml } from 'kerfjs';
|
|
2
|
-
|
|
3
|
-
interface DialogHeaderProps {
|
|
4
|
-
title: string;
|
|
5
|
-
titleId: string;
|
|
6
|
-
summary?: string;
|
|
7
|
-
summaryId?: string;
|
|
8
|
-
icon?: SafeHtml;
|
|
9
|
-
iconClassName?: string;
|
|
10
|
-
actions?: SafeHtml;
|
|
11
|
-
actionsLabel?: string;
|
|
12
|
-
}
|
|
13
|
-
declare function DialogHeader({ title, titleId, summary, summaryId, icon, iconClassName, actions, actionsLabel }: DialogHeaderProps): SafeHtml;
|
|
14
|
-
|
|
15
|
-
export { DialogHeader, type DialogHeaderProps };
|
package/dist/dialog-header.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"dialog-header.js"}
|
package/dist/page-header.d.ts
DELETED
package/dist/page-header.js
DELETED
package/dist/page-header.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"page-header.js"}
|