@kerfjs/ui 5.0.0-beta.4 → 5.0.0-beta.7
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 +9 -8
- package/ai/component-catalog.json +379 -174
- package/ai/public-api-signatures-v1.md +387 -33
- package/ai/skill.md +15 -7
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/dist/app-tab.d.ts +3 -1
- package/dist/app-tab.js +2 -1
- package/dist/browser/app-tab.js +1 -0
- package/dist/browser/menu-action-row.js +1 -0
- package/dist/browser/menu-header.js +1 -0
- package/dist/browser/menu-item.js +1 -0
- package/dist/browser/panel-header.js +7 -0
- package/dist/browser/segmented-control.js +1 -0
- package/dist/browser/select.js +1 -0
- package/dist/browser/skeleton.js +3 -0
- package/dist/browser/state-banner.js +1 -0
- package/dist/browser/toolbar-text.js +1 -0
- package/dist/browser/value-table.js +1 -0
- package/dist/chunk-2RJBFNB6.js +172 -0
- package/dist/chunk-2RJBFNB6.js.map +1 -0
- package/dist/{chunk-XZXRLY5Q.js → chunk-2Z5TWHKQ.js} +7 -6
- package/dist/chunk-2Z5TWHKQ.js.map +1 -0
- package/dist/{chunk-EGHL5S4P.js → chunk-3YD6ZNRA.js} +8 -7
- package/dist/chunk-3YD6ZNRA.js.map +1 -0
- package/dist/chunk-B24C4A6T.js +22 -0
- package/dist/chunk-B24C4A6T.js.map +1 -0
- package/dist/{chunk-NPPD2A6O.js → chunk-H6URQGJI.js} +8 -6
- package/dist/chunk-H6URQGJI.js.map +1 -0
- package/dist/chunk-I7INRJO4.js +17 -0
- package/dist/chunk-I7INRJO4.js.map +1 -0
- package/dist/chunk-KJYXOTG3.js +10 -0
- package/dist/chunk-KJYXOTG3.js.map +1 -0
- package/dist/{chunk-CTQKI2XL.js → chunk-LC3YSAZC.js} +14 -3
- package/dist/chunk-LC3YSAZC.js.map +1 -0
- package/dist/chunk-N3SUXLLS.js +23 -0
- package/dist/chunk-N3SUXLLS.js.map +1 -0
- package/dist/{chunk-R6JHHBKQ.js → chunk-PONXLV5U.js} +5 -5
- package/dist/chunk-PONXLV5U.js.map +1 -0
- package/dist/chunk-TZNCCM5H.js +20 -0
- package/dist/chunk-TZNCCM5H.js.map +1 -0
- package/dist/{chunk-TRNBHPNW.js → chunk-YGN7OMLT.js} +11 -8
- package/dist/chunk-YGN7OMLT.js.map +1 -0
- package/dist/chunk-YKUUZPED.js +24 -0
- package/dist/chunk-YKUUZPED.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 +3 -3
- package/dist/index.js +15 -15
- package/dist/menu-action-row.d.ts +3 -1
- package/dist/menu-action-row.js +2 -1
- package/dist/menu-header.d.ts +3 -1
- package/dist/menu-header.js +2 -1
- package/dist/menu-item.d.ts +3 -1
- package/dist/menu-item.js +2 -1
- package/dist/nav-stack.d.ts +38 -0
- package/dist/nav-stack.js +5 -0
- package/dist/nav-stack.js.map +1 -0
- package/dist/panel-header.d.ts +28 -0
- package/dist/panel-header.js +7 -0
- package/dist/panel-header.js.map +1 -0
- package/dist/segmented-control.d.ts +3 -1
- package/dist/segmented-control.js +2 -1
- package/dist/select.d.ts +5 -2
- package/dist/select.js +2 -1
- package/dist/skeleton.d.ts +24 -0
- package/dist/skeleton.js +3 -0
- package/dist/skeleton.js.map +1 -0
- package/dist/split-view.d.ts +42 -0
- package/dist/split-view.js +21 -0
- package/dist/split-view.js.map +1 -0
- package/dist/state-banner.d.ts +3 -1
- package/dist/state-banner.js +2 -1
- 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 +64 -0
- package/dist/styles/select.css +42 -0
- package/dist/styles/skeleton.css +31 -0
- package/dist/styles/split-view.css +35 -0
- package/dist/styles/styles.css +2 -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 +6 -2
- package/dist/toolbar-text.js +2 -1
- package/dist/value-table.d.ts +3 -1
- package/dist/value-table.js +2 -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 +46 -9
- package/docs/component-selection.md +57 -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 +116 -43
- package/package.json +43 -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/dialog-header.js +0 -5
- 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-CTQKI2XL.js.map +0 -1
- package/dist/chunk-EGHL5S4P.js.map +0 -1
- package/dist/chunk-GY5WH7TO.js +0 -10
- 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/chunk-NPPD2A6O.js.map +0 -1
- package/dist/chunk-R6JHHBKQ.js.map +0 -1
- package/dist/chunk-S3TPM2LP.js +0 -23
- package/dist/chunk-S3TPM2LP.js.map +0 -1
- package/dist/chunk-TRNBHPNW.js.map +0 -1
- package/dist/chunk-VHW5YNIB.js +0 -17
- package/dist/chunk-VHW5YNIB.js.map +0 -1
- package/dist/chunk-XZXRLY5Q.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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Public API signatures for the UI authoring corpus
|
|
2
2
|
|
|
3
|
-
Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.
|
|
3
|
+
Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.7` and `kerfjs@5.0.0-beta.7`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
|
|
4
4
|
|
|
5
5
|
## `@kerfjs/ui/disclosure-arrow`
|
|
6
6
|
|
|
@@ -44,13 +44,17 @@ export { Toolbar, type ToolbarProps };
|
|
|
44
44
|
```ts
|
|
45
45
|
import * as kerfjs from 'kerfjs';
|
|
46
46
|
|
|
47
|
-
type ToolbarTextSize = 'large' | 'default' | 'small';
|
|
47
|
+
type ToolbarTextSize = 'xlarge' | 'large' | 'default' | 'small';
|
|
48
48
|
interface ToolbarTextProps {
|
|
49
49
|
text: string;
|
|
50
50
|
size?: ToolbarTextSize;
|
|
51
51
|
className?: string;
|
|
52
|
+
/** Optional id, e.g. so a dialog can reference the title via aria-labelledby. */
|
|
53
|
+
id?: string;
|
|
54
|
+
/** Render the text as an unanimated loading skeleton instead of its value. */
|
|
55
|
+
placeholder?: boolean;
|
|
52
56
|
}
|
|
53
|
-
declare function ToolbarText({ text, size, className }: ToolbarTextProps): kerfjs.SafeHtml;
|
|
57
|
+
declare function ToolbarText({ text, size, className, id, placeholder }: ToolbarTextProps): kerfjs.SafeHtml;
|
|
54
58
|
|
|
55
59
|
export { ToolbarText, type ToolbarTextProps, type ToolbarTextSize };
|
|
56
60
|
```
|
|
@@ -103,6 +107,8 @@ interface MenuHeaderBaseProps {
|
|
|
103
107
|
disabledReason?: string;
|
|
104
108
|
expanded?: boolean;
|
|
105
109
|
toggle?: boolean;
|
|
110
|
+
/** Render as an unanimated loading skeleton: keep the label and action affordance, disable interaction. */
|
|
111
|
+
placeholder?: boolean;
|
|
106
112
|
rootAttributes?: MenuHeaderRootAttributes;
|
|
107
113
|
triggerAttributes?: MenuHeaderTriggerAttributes;
|
|
108
114
|
}
|
|
@@ -116,7 +122,7 @@ type MenuHeaderIndicatorProps = {
|
|
|
116
122
|
badge?: SafeHtml;
|
|
117
123
|
};
|
|
118
124
|
type MenuHeaderProps = MenuHeaderBaseProps & MenuHeaderIndicatorProps;
|
|
119
|
-
declare function MenuHeader({ label, count, countLabel, badge, action, actionLabel, actionIcon, actionDisabled, disabledReason, expanded, toggle, rootAttributes, triggerAttributes }: MenuHeaderProps): SafeHtml;
|
|
125
|
+
declare function MenuHeader({ label, count, countLabel, badge, action, actionLabel, actionIcon, actionDisabled, disabledReason, expanded, toggle, placeholder, rootAttributes, triggerAttributes }: MenuHeaderProps): SafeHtml;
|
|
120
126
|
|
|
121
127
|
export { MenuHeader, type MenuHeaderProps };
|
|
122
128
|
```
|
|
@@ -160,6 +166,8 @@ interface MenuActionRowProps {
|
|
|
160
166
|
state?: string;
|
|
161
167
|
disabled?: boolean;
|
|
162
168
|
tabIndex?: number;
|
|
169
|
+
/** Render as an unanimated loading skeleton, disabling both actions. */
|
|
170
|
+
placeholder?: boolean;
|
|
163
171
|
trailingAction: string;
|
|
164
172
|
trailingActionLabel: string;
|
|
165
173
|
/** Decorative dormant content for the trailing button. Must not contain interactive descendants. */
|
|
@@ -171,7 +179,7 @@ interface MenuActionRowProps {
|
|
|
171
179
|
rootAttributes?: MenuActionRowRootAttributes;
|
|
172
180
|
trailingActionAttributes?: MenuActionRowTrailingAttributes;
|
|
173
181
|
}
|
|
174
|
-
declare function MenuActionRow({ label, icon, action, itemId, selected, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, trailingAction, trailingActionLabel, trailingActionIcon, trailingActionDisabled, trailingActionTitle, className, style, rootAttributes, trailingActionAttributes }: MenuActionRowProps): SafeHtml;
|
|
182
|
+
declare function MenuActionRow({ label, icon, action, itemId, selected, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, placeholder, trailingAction, trailingActionLabel, trailingActionIcon, trailingActionDisabled, trailingActionTitle, className, style, rootAttributes, trailingActionAttributes }: MenuActionRowProps): SafeHtml;
|
|
175
183
|
|
|
176
184
|
export { MenuActionRow, type MenuActionRowProps };
|
|
177
185
|
```
|
|
@@ -205,33 +213,21 @@ interface MenuItemProps {
|
|
|
205
213
|
state?: string;
|
|
206
214
|
disabled?: boolean;
|
|
207
215
|
tabIndex?: number;
|
|
216
|
+
/** Render the row as an unanimated loading skeleton, disabling its action. */
|
|
217
|
+
placeholder?: boolean;
|
|
208
218
|
rootAttributes?: MenuItemRootAttributes;
|
|
209
219
|
}
|
|
210
|
-
declare function MenuItem({ label, icon, trailing, selected, action, itemId, className, style, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, rootAttributes }: MenuItemProps): SafeHtml;
|
|
220
|
+
declare function MenuItem({ label, icon, trailing, selected, action, itemId, className, style, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, placeholder, rootAttributes }: MenuItemProps): SafeHtml;
|
|
211
221
|
|
|
212
222
|
export { MenuItem, type MenuItemProps };
|
|
213
223
|
```
|
|
214
224
|
|
|
215
|
-
## `@kerfjs/ui/
|
|
225
|
+
## `@kerfjs/ui/panel-header`
|
|
216
226
|
|
|
217
227
|
```ts
|
|
218
228
|
import { SafeHtml } from 'kerfjs';
|
|
219
229
|
|
|
220
|
-
interface
|
|
221
|
-
title: string | SafeHtml;
|
|
222
|
-
action?: SafeHtml;
|
|
223
|
-
}
|
|
224
|
-
declare function PageHeader({ title, action }: PageHeaderProps): SafeHtml;
|
|
225
|
-
|
|
226
|
-
export { PageHeader, type PageHeaderProps };
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
## `@kerfjs/ui/dialog-header`
|
|
230
|
-
|
|
231
|
-
```ts
|
|
232
|
-
import { SafeHtml } from 'kerfjs';
|
|
233
|
-
|
|
234
|
-
interface DialogHeaderProps {
|
|
230
|
+
interface PanelHeaderProps {
|
|
235
231
|
title: string;
|
|
236
232
|
titleId: string;
|
|
237
233
|
summary?: string;
|
|
@@ -239,11 +235,24 @@ interface DialogHeaderProps {
|
|
|
239
235
|
icon?: SafeHtml;
|
|
240
236
|
iconClassName?: string;
|
|
241
237
|
actions?: SafeHtml;
|
|
242
|
-
|
|
238
|
+
/** Render the title and summary as unanimated loading skeletons, keeping the icon and actions. */
|
|
239
|
+
placeholder?: boolean;
|
|
243
240
|
}
|
|
244
|
-
|
|
241
|
+
/**
|
|
242
|
+
* The heading of a panel, dialog, or page: a plain `Toolbar` whose leading zone
|
|
243
|
+
* holds an optional icon control group and the title as extra-large `ToolbarText`,
|
|
244
|
+
* whose trailing zone holds the app's action controls, and with an optional
|
|
245
|
+
* subtitle on its own row, left-aligned with the title.
|
|
246
|
+
*
|
|
247
|
+
* PanelHeader overrides no Toolbar styles — it is just a Toolbar with an xl title.
|
|
248
|
+
* The only styling it adds is the icon group's fill/border color and the subtitle.
|
|
249
|
+
* When no icon is provided, the icon group is omitted entirely. The `actions` slot
|
|
250
|
+
* is passed straight into the toolbar's trailing zone; the app supplies whatever
|
|
251
|
+
* trailing controls it needs (typically a `ToolbarControlGroup`).
|
|
252
|
+
*/
|
|
253
|
+
declare function PanelHeader({ title, titleId, summary, summaryId, icon, iconClassName, actions, placeholder }: PanelHeaderProps): SafeHtml;
|
|
245
254
|
|
|
246
|
-
export {
|
|
255
|
+
export { PanelHeader, type PanelHeaderProps };
|
|
247
256
|
```
|
|
248
257
|
|
|
249
258
|
## `@kerfjs/ui/value-table`
|
|
@@ -256,8 +265,10 @@ interface ValueTableRowProps {
|
|
|
256
265
|
value: string | SafeHtml;
|
|
257
266
|
icon?: SafeHtml;
|
|
258
267
|
className?: string;
|
|
268
|
+
/** Render the value as an unanimated loading skeleton, keeping the field label. */
|
|
269
|
+
placeholder?: boolean;
|
|
259
270
|
}
|
|
260
|
-
declare function ValueTableRow({ label, value, icon, className }: ValueTableRowProps): SafeHtml;
|
|
271
|
+
declare function ValueTableRow({ label, value, icon, className, placeholder }: ValueTableRowProps): SafeHtml;
|
|
261
272
|
|
|
262
273
|
interface ValueTableProps {
|
|
263
274
|
label: string;
|
|
@@ -295,9 +306,11 @@ interface AppTabProps {
|
|
|
295
306
|
selectAction?: string;
|
|
296
307
|
closeAction?: string;
|
|
297
308
|
className?: string;
|
|
309
|
+
/** Render as an unanimated loading skeleton, disabling select/close and dragging. */
|
|
310
|
+
placeholder?: boolean;
|
|
298
311
|
rootAttributes?: AppTabRootAttributes;
|
|
299
312
|
}
|
|
300
|
-
declare function AppTab({ id, name, selected, closable, draggable, leading, trailing, closeIcon, selectAction, closeAction, className, rootAttributes }: AppTabProps): SafeHtml;
|
|
313
|
+
declare function AppTab({ id, name, selected, closable, draggable, leading, trailing, closeIcon, selectAction, closeAction, className, placeholder, rootAttributes }: AppTabProps): SafeHtml;
|
|
301
314
|
|
|
302
315
|
export { AppTab, type AppTabProps };
|
|
303
316
|
```
|
|
@@ -343,6 +356,205 @@ declare function wireTabBars(root: HTMLElement | Document, { onReorder }: WireTa
|
|
|
343
356
|
export { type TabDropPosition, type TabReorder, type TabReorderSource, type WireTabBarsOptions, reorderTabs, wireTabBars };
|
|
344
357
|
```
|
|
345
358
|
|
|
359
|
+
## `@kerfjs/ui/nav-stack`
|
|
360
|
+
|
|
361
|
+
```ts
|
|
362
|
+
import { SafeHtml } from 'kerfjs';
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* One entry in a {@link NavStack}. The app owns the stack as an array (usually a
|
|
366
|
+
* signal); `NavStack` renders it and `wireNavStack` animates the transitions.
|
|
367
|
+
*/
|
|
368
|
+
interface NavStackView {
|
|
369
|
+
/** Stable identity for keyed reconcile and transition direction. */
|
|
370
|
+
key: string;
|
|
371
|
+
content: SafeHtml;
|
|
372
|
+
/** Title shown in the top toolbar for this view. */
|
|
373
|
+
title?: string;
|
|
374
|
+
/** Trailing actions for this view's top toolbar. */
|
|
375
|
+
toolbar?: SafeHtml;
|
|
376
|
+
}
|
|
377
|
+
interface NavStackProps {
|
|
378
|
+
id: string;
|
|
379
|
+
/** Accessible name for the stack region. */
|
|
380
|
+
label: string;
|
|
381
|
+
/** The stack, root first; the last entry is the active top view. */
|
|
382
|
+
views: NavStackView[];
|
|
383
|
+
/** Accessible label for the back control (default "Back"). */
|
|
384
|
+
backLabel?: string;
|
|
385
|
+
/** Hide the top toolbar entirely (rare — a fully custom-chrome view). */
|
|
386
|
+
hideToolbar?: boolean;
|
|
387
|
+
/** Optional persistent bottom toolbar. */
|
|
388
|
+
bottomToolbar?: SafeHtml;
|
|
389
|
+
className?: string;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* A navigation stack (iOS-style push/pop). Renders every entry stacked, the last
|
|
393
|
+
* one active; `@kerfjs/ui/wire-nav-stack`'s `wireNavStack` slides the content and
|
|
394
|
+
* cross-fades the chrome across a change. A single-pane layout is a `NavStack`
|
|
395
|
+
* with one entry. See `docs/23-app-layouts.md` §3.1.
|
|
396
|
+
*/
|
|
397
|
+
declare function NavStack({ id, label, views, backLabel, hideToolbar, bottomToolbar, className }: NavStackProps): SafeHtml;
|
|
398
|
+
|
|
399
|
+
export { NavStack, type NavStackProps, type NavStackView };
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
## `@kerfjs/ui/wire-nav-stack`
|
|
403
|
+
|
|
404
|
+
```ts
|
|
405
|
+
interface WireNavStackOptions {
|
|
406
|
+
/** Invoked when the back control is activated. The app pops its own stack. */
|
|
407
|
+
onBack?: () => void;
|
|
408
|
+
/** Transition duration in ms (default 200). Set 0 to disable animation. */
|
|
409
|
+
duration?: number;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Animate a `NavStack`'s push/pop transitions and wire its back control. The app
|
|
413
|
+
* owns the stack (a signal of `NavStackView[]`) and re-renders `NavStack` when it
|
|
414
|
+
* changes; this helper slides the content and settles the chrome across each
|
|
415
|
+
* change, and calls `onBack` when the back control is used. Returns a disposer.
|
|
416
|
+
*/
|
|
417
|
+
declare function wireNavStack(root: Element, options?: WireNavStackOptions): () => void;
|
|
418
|
+
|
|
419
|
+
export { type WireNavStackOptions, wireNavStack };
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## `@kerfjs/ui/split-view`
|
|
423
|
+
|
|
424
|
+
```ts
|
|
425
|
+
import { SafeHtml } from 'kerfjs';
|
|
426
|
+
|
|
427
|
+
interface SplitViewResizable {
|
|
428
|
+
size: number;
|
|
429
|
+
min: number;
|
|
430
|
+
max: number;
|
|
431
|
+
}
|
|
432
|
+
interface SplitViewProps {
|
|
433
|
+
id: string;
|
|
434
|
+
label: string;
|
|
435
|
+
/** The list (primary) pane. */
|
|
436
|
+
list: SafeHtml;
|
|
437
|
+
/** The detail (secondary) pane. */
|
|
438
|
+
detail: SafeHtml;
|
|
439
|
+
/**
|
|
440
|
+
* Compact ("one pane at a time") classes — a handset or portrait tablet.
|
|
441
|
+
* Derive from `deviceClass().value.compact`. When true the split collapses to
|
|
442
|
+
* a `NavStack`: the list is the root and the detail is pushed over it.
|
|
443
|
+
*/
|
|
444
|
+
compact?: boolean;
|
|
445
|
+
/** In compact mode, whether the detail is currently pushed over the list. */
|
|
446
|
+
detailActive?: boolean;
|
|
447
|
+
/** Title/label for the list (compact NavStack root + region label). */
|
|
448
|
+
listTitle?: string;
|
|
449
|
+
/** Title/label for the detail (compact NavStack pushed view + region label). */
|
|
450
|
+
detailTitle?: string;
|
|
451
|
+
/** Back label for the compact NavStack (default "Back"). */
|
|
452
|
+
backLabel?: string;
|
|
453
|
+
/** A resizable separator on roomy classes (min/max px). Omit for a fixed split. */
|
|
454
|
+
resizable?: SplitViewResizable;
|
|
455
|
+
className?: string;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* A list-detail (master-detail) split. On roomy classes it shows both panes side
|
|
459
|
+
* by side with an optional resizable separator; on compact classes it collapses
|
|
460
|
+
* to a `NavStack` (list → detail). See `docs/23-app-layouts.md` §3.2. Compose the
|
|
461
|
+
* resizable wiring with `wireResizableRegions` and the compact back with
|
|
462
|
+
* `wireNavStack`.
|
|
463
|
+
*/
|
|
464
|
+
declare function SplitView({ id, label, list, detail, compact, detailActive, listTitle, detailTitle, backLabel, resizable, className }: SplitViewProps): SafeHtml;
|
|
465
|
+
|
|
466
|
+
export { SplitView, type SplitViewProps, type SplitViewResizable };
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
## `@kerfjs/ui/workbench`
|
|
470
|
+
|
|
471
|
+
```ts
|
|
472
|
+
import { SafeHtml } from 'kerfjs';
|
|
473
|
+
|
|
474
|
+
/** A collapsible Workbench panel — a side rail or the bottom drawer. */
|
|
475
|
+
interface WorkbenchPanel {
|
|
476
|
+
content: SafeHtml;
|
|
477
|
+
/** Whether the panel is currently collapsed (the app owns this). */
|
|
478
|
+
collapsed?: boolean;
|
|
479
|
+
/** Rail width, or drawer height, in px. Overrides the CSS default. */
|
|
480
|
+
size?: number;
|
|
481
|
+
/** Accessible name for the panel region. */
|
|
482
|
+
label?: string;
|
|
483
|
+
}
|
|
484
|
+
interface WorkbenchProps {
|
|
485
|
+
id: string;
|
|
486
|
+
label: string;
|
|
487
|
+
/** The central work area. */
|
|
488
|
+
main: SafeHtml;
|
|
489
|
+
leftRail?: WorkbenchPanel;
|
|
490
|
+
rightRail?: WorkbenchPanel;
|
|
491
|
+
bottomDrawer?: WorkbenchPanel;
|
|
492
|
+
className?: string;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* The Xcode-like multi-panel workspace: a collapsible left rail, right rail, and
|
|
496
|
+
* bottom drawer around a central work area (any absent). Collapsing snaps the
|
|
497
|
+
* panel's track to zero in one reflow while its fixed-size content slides out via
|
|
498
|
+
* a composited transform — the instant-width / sliding-content technique, so the
|
|
499
|
+
* work area relayouts once, not per frame. The app owns each `collapsed` flag;
|
|
500
|
+
* the collapse is pure CSS (no wire). See `docs/23-app-layouts.md` §3.3.
|
|
501
|
+
*/
|
|
502
|
+
declare function Workbench({ id, label, main, leftRail, rightRail, bottomDrawer, className }: WorkbenchProps): SafeHtml;
|
|
503
|
+
|
|
504
|
+
export { Workbench, type WorkbenchPanel, type WorkbenchProps };
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
## `@kerfjs/ui/tab-scaffold`
|
|
508
|
+
|
|
509
|
+
```ts
|
|
510
|
+
import { SafeHtml } from 'kerfjs';
|
|
511
|
+
|
|
512
|
+
interface TabScaffoldTab {
|
|
513
|
+
id: string;
|
|
514
|
+
label: string;
|
|
515
|
+
/** Decorative icon shown above the label in the bottom bar. */
|
|
516
|
+
icon?: SafeHtml;
|
|
517
|
+
/** The tab's content — typically a `NavStack` so each tab keeps its own stack. */
|
|
518
|
+
content: SafeHtml;
|
|
519
|
+
}
|
|
520
|
+
interface TabScaffoldProps {
|
|
521
|
+
id: string;
|
|
522
|
+
/** Accessible name for the tab bar. */
|
|
523
|
+
label: string;
|
|
524
|
+
tabs: TabScaffoldTab[];
|
|
525
|
+
/** The controlled active tab id (the app owns selection). */
|
|
526
|
+
active: string;
|
|
527
|
+
className?: string;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* A mobile-first, iOS-like bottom tab scaffold: a bottom tab bar that switches
|
|
531
|
+
* between major sections, each tab keeping its own content (usually a `NavStack`)
|
|
532
|
+
* mounted so its stack and scroll survive a switch. Controlled — the app owns
|
|
533
|
+
* `active`; wire selection with `@kerfjs/ui/wire-tab-scaffold`'s `wireTabScaffold`.
|
|
534
|
+
* On larger classes, promote the tabs to a `Workbench` rail or sidebar instead of
|
|
535
|
+
* a bottom bar. See `docs/23-app-layouts.md` §3.4.
|
|
536
|
+
*/
|
|
537
|
+
declare function TabScaffold({ id, label, tabs, active, className }: TabScaffoldProps): SafeHtml;
|
|
538
|
+
|
|
539
|
+
export { TabScaffold, type TabScaffoldProps, type TabScaffoldTab };
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
## `@kerfjs/ui/wire-tab-scaffold`
|
|
543
|
+
|
|
544
|
+
```ts
|
|
545
|
+
interface WireTabScaffoldOptions {
|
|
546
|
+
/** Invoked with the selected tab id when a bottom-bar tab is activated. */
|
|
547
|
+
onSelect: (tabId: string) => void;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Wire a `TabScaffold`'s bottom tab bar: clicking a tab calls `onSelect` with its
|
|
551
|
+
* id (the app then updates its controlled `active`). Returns a disposer.
|
|
552
|
+
*/
|
|
553
|
+
declare function wireTabScaffold(root: Element, options: WireTabScaffoldOptions): () => void;
|
|
554
|
+
|
|
555
|
+
export { type WireTabScaffoldOptions, wireTabScaffold };
|
|
556
|
+
```
|
|
557
|
+
|
|
346
558
|
## `@kerfjs/ui/resizable-region`
|
|
347
559
|
|
|
348
560
|
```ts
|
|
@@ -391,6 +603,73 @@ declare function wireResizableRegions(root: HTMLElement, { step, largeStep, onPr
|
|
|
391
603
|
export { type ResizeCommit, type WireResizableRegionsOptions, wireResizableRegions };
|
|
392
604
|
```
|
|
393
605
|
|
|
606
|
+
## `@kerfjs/ui/device-class`
|
|
607
|
+
|
|
608
|
+
```ts
|
|
609
|
+
import { ReadonlySignal } from 'kerfjs';
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Reactive device-class detection for `@kerfjs/ui` (see `docs/23-app-layouts.md`
|
|
613
|
+
* §2). `deviceClass()` returns a `ReadonlySignal<DeviceClass>` describing the
|
|
614
|
+
* current viewport as a size bucket × orientation × viewport-segment count, so a
|
|
615
|
+
* layout can pick its presentation reactively instead of hand-wiring `matchMedia`.
|
|
616
|
+
*
|
|
617
|
+
* One shared viewport source backs every reader; the pure `classifyViewport`
|
|
618
|
+
* core is DOM-free and directly unit-tested.
|
|
619
|
+
*/
|
|
620
|
+
type DeviceSize = 'xs-mobile' | 'mobile' | 'tablet' | 'desktop' | 'xl-desktop';
|
|
621
|
+
type DeviceOrientation = 'portrait' | 'landscape';
|
|
622
|
+
/** Minimum widths (px) at which each larger bucket begins. `xs-mobile` is 0. */
|
|
623
|
+
interface DeviceBreakpoints {
|
|
624
|
+
mobile: number;
|
|
625
|
+
tablet: number;
|
|
626
|
+
desktop: number;
|
|
627
|
+
'xl-desktop': number;
|
|
628
|
+
}
|
|
629
|
+
interface DeviceClass {
|
|
630
|
+
size: DeviceSize;
|
|
631
|
+
orientation: DeviceOrientation;
|
|
632
|
+
/** Horizontal viewport segments (foldables / dual-screen); 1 on ordinary devices. */
|
|
633
|
+
segments: number;
|
|
634
|
+
/** Vertical viewport segments; 1 on ordinary devices. */
|
|
635
|
+
verticalSegments: number;
|
|
636
|
+
/** Small phones — `xs-mobile` or `mobile`. */
|
|
637
|
+
handset: boolean;
|
|
638
|
+
/** "One pane at a time" — a handset or a portrait tablet. */
|
|
639
|
+
compact: boolean;
|
|
640
|
+
/** True when the current size is `size` or larger, e.g. `atLeast('tablet')`. */
|
|
641
|
+
atLeast(size: DeviceSize): boolean;
|
|
642
|
+
}
|
|
643
|
+
/** A raw viewport snapshot, before breakpoints are applied. */
|
|
644
|
+
interface Viewport {
|
|
645
|
+
width: number;
|
|
646
|
+
height: number;
|
|
647
|
+
segments: number;
|
|
648
|
+
verticalSegments: number;
|
|
649
|
+
}
|
|
650
|
+
interface DeviceClassOptions {
|
|
651
|
+
/** Override any of the default bucket thresholds. */
|
|
652
|
+
breakpoints?: Partial<DeviceBreakpoints>;
|
|
653
|
+
/** The viewport assumed when there is no DOM (SSR). Defaults to 1024×768, one segment. */
|
|
654
|
+
ssr?: Partial<Viewport>;
|
|
655
|
+
}
|
|
656
|
+
declare const DEFAULT_BREAKPOINTS: DeviceBreakpoints;
|
|
657
|
+
/**
|
|
658
|
+
* Classify a raw viewport into a {@link DeviceClass}. Pure and DOM-free — the
|
|
659
|
+
* single source of truth for the bucketing rules.
|
|
660
|
+
*/
|
|
661
|
+
declare function classifyViewport(width: number, orientation: DeviceOrientation, segments?: number, verticalSegments?: number, breakpoints?: DeviceBreakpoints): DeviceClass;
|
|
662
|
+
/**
|
|
663
|
+
* A reactive signal of the current {@link DeviceClass}. Reading it inside an
|
|
664
|
+
* `effect`/`computed` re-runs when the viewport crosses a breakpoint, rotates,
|
|
665
|
+
* or changes its segment count. Without a DOM it resolves to `options.ssr`
|
|
666
|
+
* (default 1024×768, landscape, one segment).
|
|
667
|
+
*/
|
|
668
|
+
declare function deviceClass(options?: DeviceClassOptions): ReadonlySignal<DeviceClass>;
|
|
669
|
+
|
|
670
|
+
export { DEFAULT_BREAKPOINTS, type DeviceBreakpoints, type DeviceClass, type DeviceClassOptions, type DeviceOrientation, type DeviceSize, type Viewport, classifyViewport, deviceClass };
|
|
671
|
+
```
|
|
672
|
+
|
|
394
673
|
## `@kerfjs/ui/segmented-control`
|
|
395
674
|
|
|
396
675
|
```ts
|
|
@@ -418,8 +697,10 @@ interface SegmentedControlProps {
|
|
|
418
697
|
size?: SegmentedControlSize;
|
|
419
698
|
layout?: SegmentedControlLayout;
|
|
420
699
|
className?: string;
|
|
700
|
+
/** Render as an unanimated loading skeleton, disabling every segment. */
|
|
701
|
+
placeholder?: boolean;
|
|
421
702
|
}
|
|
422
|
-
declare function SegmentedControl({ id, label, value, choices, action, appearance, shape, size, layout, className, }: SegmentedControlProps): SafeHtml;
|
|
703
|
+
declare function SegmentedControl({ id, label, value, choices, action, appearance, shape, size, layout, className, placeholder, }: SegmentedControlProps): SafeHtml;
|
|
423
704
|
|
|
424
705
|
export { SegmentedControl, type SegmentedControlAppearance, type SegmentedControlChoice, type SegmentedControlLayout, type SegmentedControlProps, type SegmentedControlShape, type SegmentedControlSize };
|
|
425
706
|
```
|
|
@@ -438,8 +719,10 @@ interface StateBannerProps {
|
|
|
438
719
|
tone?: StateBannerTone;
|
|
439
720
|
urgency?: 'status' | 'alert';
|
|
440
721
|
className?: string;
|
|
722
|
+
/** Render the title and detail as unanimated loading skeletons, keeping the icon and tone. */
|
|
723
|
+
placeholder?: boolean;
|
|
441
724
|
}
|
|
442
|
-
declare function StateBanner({ title, detail, icon, action, tone, urgency, className }: StateBannerProps): SafeHtml;
|
|
725
|
+
declare function StateBanner({ title, detail, icon, action, tone, urgency, className, placeholder }: StateBannerProps): SafeHtml;
|
|
443
726
|
|
|
444
727
|
export { StateBanner, type StateBannerProps, type StateBannerTone };
|
|
445
728
|
```
|
|
@@ -477,6 +760,35 @@ declare function LoadingSpinner({ className, label }: LoadingSpinnerProps): kerf
|
|
|
477
760
|
export { LoadingSpinner, type LoadingSpinnerProps };
|
|
478
761
|
```
|
|
479
762
|
|
|
763
|
+
## `@kerfjs/ui/skeleton`
|
|
764
|
+
|
|
765
|
+
```ts
|
|
766
|
+
import * as kerfjs from 'kerfjs';
|
|
767
|
+
|
|
768
|
+
interface SkeletonProps {
|
|
769
|
+
/** Width as any CSS length (e.g. `remify(120px)`, `60%`). Defaults to filling its slot. */
|
|
770
|
+
width?: string;
|
|
771
|
+
/** Height as any CSS length. Defaults to a single text line. */
|
|
772
|
+
height?: string;
|
|
773
|
+
/** Corner radius override (a CSS length). Defaults to the small radius token. */
|
|
774
|
+
radius?: string;
|
|
775
|
+
/** Render this many stacked lines (the last one shorter), for multi-line text. */
|
|
776
|
+
lines?: number;
|
|
777
|
+
/** Accessible label. Omit to keep the block decorative (`aria-hidden`). */
|
|
778
|
+
label?: string;
|
|
779
|
+
className?: string;
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* A subtle, deliberately **unanimated** loading placeholder block. Use it for a
|
|
783
|
+
* value slot whose content is not yet known, on its own or via a component's
|
|
784
|
+
* `placeholder` prop. Decorative by default (`aria-hidden`); pass `label` to
|
|
785
|
+
* announce it. Sizes to its slot unless `width`/`height` are given.
|
|
786
|
+
*/
|
|
787
|
+
declare function Skeleton({ width, height, radius, lines, label, className }: SkeletonProps): kerfjs.SafeHtml;
|
|
788
|
+
|
|
789
|
+
export { Skeleton, type SkeletonProps };
|
|
790
|
+
```
|
|
791
|
+
|
|
480
792
|
## `@kerfjs/ui/token-search-field`
|
|
481
793
|
|
|
482
794
|
```ts
|
|
@@ -528,17 +840,59 @@ export { TokenSearchField, type TokenSearchFieldProps, type TokenSearchFieldValu
|
|
|
528
840
|
## `@kerfjs/ui/wire-token-search-fields`
|
|
529
841
|
|
|
530
842
|
```ts
|
|
843
|
+
import { Signal } from 'kerfjs';
|
|
844
|
+
|
|
531
845
|
interface TokenSearchSubmit {
|
|
532
846
|
id: string;
|
|
533
847
|
editor: HTMLElement;
|
|
534
848
|
}
|
|
849
|
+
/**
|
|
850
|
+
* Managed collapsible behavior for the iconic TokenSearchField. Every piece is on
|
|
851
|
+
* by default; disable a specific one to own it in the app. Provide `signals` to
|
|
852
|
+
* drive app-owned `expanded` signals per field id instead of helper-created ones.
|
|
853
|
+
*/
|
|
854
|
+
interface TokenSearchCollapsibleOptions {
|
|
855
|
+
/** Expand the field and focus its editor when the iconic trigger is activated. Default: true. */
|
|
856
|
+
expandOnActivate?: boolean;
|
|
857
|
+
/** Collapse the field when focus leaves it while it is empty. Default: true. */
|
|
858
|
+
collapseOnEmptyBlur?: boolean;
|
|
859
|
+
/** Collapse an empty field on Escape and restore focus to its trigger. Default: true. */
|
|
860
|
+
collapseOnEscape?: boolean;
|
|
861
|
+
/** Focus the editor on expand and the trigger on Escape-collapse. Default: true. */
|
|
862
|
+
manageFocus?: boolean;
|
|
863
|
+
/** App-owned `expanded` signals keyed by field id; adopted instead of helper-created. */
|
|
864
|
+
signals?: Readonly<Record<string, Signal<boolean>>>;
|
|
865
|
+
}
|
|
535
866
|
interface WireTokenSearchFieldsOptions {
|
|
536
|
-
onSubmit
|
|
867
|
+
onSubmit?: (submission: TokenSearchSubmit) => void;
|
|
868
|
+
/** Managed collapsible transient behavior. `true`/omitted = on with defaults; `false` = fully off. */
|
|
869
|
+
collapsible?: boolean | TokenSearchCollapsibleOptions;
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* The value returned from {@link wireTokenSearchFields}: call it (or `dispose()`) to
|
|
873
|
+
* tear down. When collapsible behavior is managed, it also exposes the transient
|
|
874
|
+
* `expanded` state per field id so the app can read it in render, hand in its own
|
|
875
|
+
* signal, or drive it imperatively.
|
|
876
|
+
*/
|
|
877
|
+
interface TokenSearchFieldsHandle {
|
|
878
|
+
(): void;
|
|
879
|
+
dispose(): void;
|
|
880
|
+
/** The managed `expanded` signal for a field id (adopted or helper-created); undefined when unmanaged. */
|
|
881
|
+
expanded(id: string): Signal<boolean> | undefined;
|
|
882
|
+
/** Expand the field (and, when focus is managed, focus its editor). */
|
|
883
|
+
open(id: string): void;
|
|
884
|
+
/** Collapse the field (and, when focus is managed, restore focus to its trigger). */
|
|
885
|
+
close(id: string): void;
|
|
537
886
|
}
|
|
538
|
-
/**
|
|
539
|
-
|
|
887
|
+
/**
|
|
888
|
+
* Wire every TokenSearchField under `root`: submit on Enter, preserve the caret across
|
|
889
|
+
* controlled token deletion, and (by default) manage the collapsible field's transient
|
|
890
|
+
* expand/collapse/focus. Returns a {@link TokenSearchFieldsHandle} — a disposer that also
|
|
891
|
+
* exposes the managed `expanded` state per field id.
|
|
892
|
+
*/
|
|
893
|
+
declare function wireTokenSearchFields(root: HTMLElement, { onSubmit, collapsible }?: WireTokenSearchFieldsOptions): TokenSearchFieldsHandle;
|
|
540
894
|
|
|
541
|
-
export { type TokenSearchSubmit, type WireTokenSearchFieldsOptions, wireTokenSearchFields };
|
|
895
|
+
export { type TokenSearchCollapsibleOptions, type TokenSearchFieldsHandle, type TokenSearchSubmit, type WireTokenSearchFieldsOptions, wireTokenSearchFields };
|
|
542
896
|
```
|
|
543
897
|
|
|
544
898
|
## `kerfjs/actions`
|
package/ai/skill.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: kerf-ui
|
|
3
3
|
description: Build interfaces with kerfjs and the @kerfjs/ui production component package. Use whenever code imports @kerfjs/ui or a task asks for Kerf UI components.
|
|
4
|
-
kerf-ui-skill-version: 1.
|
|
4
|
+
kerf-ui-skill-version: 1.34.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Building with @kerfjs/ui
|
|
@@ -29,9 +29,10 @@ Quick routing:
|
|
|
29
29
|
| --- | --- | --- |
|
|
30
30
|
| Navigation row | `MenuItem` | Use an ordinary link or button when sidebar/menu anatomy and state do not apply. Multiline leading icons stay aligned with the first text line. |
|
|
31
31
|
| Navigation row with a trailing action | `MenuActionRow` | Use `MenuItem` when the trailing region is dormant; never put controls inside either component's SafeHtml slots. Multiline leading icons stay aligned with the first text line. |
|
|
32
|
-
| Page
|
|
32
|
+
| Page, panel, or dialog heading | `Toolbar` or `PanelHeader` | Toolbar is persistent chrome; `PanelHeader` heads a panel, dialog, or page. |
|
|
33
33
|
| Exclusive choice | `TabBar`, `SegmentedControl`, or `Select` | Tabs switch tabpanels; segments expose a few choices; Select handles a longer value list. |
|
|
34
34
|
| Status or absent content | `StateBanner` or `EmptyState` | Web Awesome callouts suit ecosystem content; toasts are transient and never the only record of important state. |
|
|
35
|
+
| Loading placeholder | a component's `placeholder` prop, or `Skeleton` | Set `placeholder` on a value-bearing component (`Select`, `MenuHeader`, `MenuItem`, `ValueTableRow`, `PanelHeader`, `SegmentedControl`, `StateBanner`, `AppTab`, `ToolbarText`, `MenuActionRow`) to render its real chrome with value slots as subtle **unanimated** skeletons and interactivity disabled — do not hand-rebuild an empty state. `Skeleton` is the standalone block for a custom slot. Use `LoadingSpinner` for known busy activity, not a skeleton; never animate a skeleton; prefer it over `wa-skeleton`. |
|
|
35
36
|
| Adjustable or static columns | `ResizableRegion` or CSS grid | Use the component only for a user-operable controlled separator. |
|
|
36
37
|
| Structured or ordinary search | `TokenSearchField` or an input | Use token search only when text and ordered filter chips form one editor. |
|
|
37
38
|
| Key/value facts | `ValueTable` with `ValueTableRow` | Use rows with `icon` only when the optional 24px leading visual adds context. |
|
|
@@ -56,16 +57,18 @@ Hard rules:
|
|
|
56
57
|
8. `ResizableRegion` uses `wireResizableRegions()` for Arrow, Shift+Arrow, Home/End, and pointer behavior. The app owns size persistence. `handleIcon` replaces decorative dormant glyph content only.
|
|
57
58
|
9. Use controlled `SegmentedControl` for a small exclusive choice set. Select `appearance="toolbar"` when nesting it inside `ToolbarControlGroup`; use rounded or pill shapes for standalone contexts. Handle its action, update `value`, keep meaningful choice labels, and preserve every enabled native button in sequential Tab order.
|
|
58
59
|
10. Compose `AppTab` inside controlled `TabBar`; call `wireTabBars()` once and retain its disposer. It owns same-bar drag mechanics, including proximity-based horizontal edge autoscroll, while the app applies `onReorder` and owns order, selection, panels, close policy, routing, and persistence. Use runtime-filtered `rootAttributes` for domain metadata and keep an optional `closeIcon` decorative and noninteractive.
|
|
59
|
-
11. Use token-controlled `TokenSearchField` when free text and removable structured filters share one editor. Editable text stays DOM-owned between token changes. The leading icon, first text line, clear action, and trailing slot share one fixed row when content wraps. The app owns parsing and suggestions; call `readTokenSearchField()` on input, empty `textContent` on clear, use `placeTokenSearchCaret()` after explicit controlled focus changes, and call `wireTokenSearchFields()` once so Enter submits without inserting a line break and keyboard chip deletion restores focus plus the text-relative caret after controlled replacement. Enable `collapsible` for an animated iconic closed state, standalone or inside `ToolbarControlGroup`; the field keeps text or tokens expanded
|
|
60
|
+
11. Use token-controlled `TokenSearchField` when free text and removable structured filters share one editor. Editable text stays DOM-owned between token changes. The leading icon, first text line, clear action, and trailing slot share one fixed row when content wraps. The app owns parsing and suggestions; call `readTokenSearchField()` on input, empty `textContent` on clear, use `placeTokenSearchCaret()` after explicit controlled focus changes, and call `wireTokenSearchFields()` once so Enter submits without inserting a line break and keyboard chip deletion restores focus plus the text-relative caret after controlled replacement. Enable `collapsible` for an animated iconic closed state, standalone or inside `ToolbarControlGroup`; the field keeps text or tokens expanded, and `wireTokenSearchFields()` manages the transient expand/collapse/focus by default (activate to reveal + focus, Escape or empty blur to collapse). Bind the field's `expanded` to the returned handle's `expanded(id)` signal or adopt your own via `collapsible.signals`; opt a behavior out only when the app must own it. Do not hand-roll the open handler, the focusout collapse, or a clear-button mousedown guard — the helper owns them.
|
|
60
61
|
12. Demo work uses public production component subpaths and their browser-selected CSS. Give every public visual component its own category-grouped catalog route; list themed third-party components under a clearly labeled collapsible ecosystem section, with a focused route for each. Project its deterministic repository-relative demo source and existing documentation path so the detail can expose `View demo source` and `Read guidance` links without a runtime export; also derive first-party component implementation paths from their canonical browser imports, and label Web Awesome documentation as Kerf integration guidance. Declare direct `uses` relationships so `Used by` stays derivable, and theme shell chrome through the same semantic tokens as the stage instead of drawing a substitute.
|
|
61
62
|
13. The Web Awesome theme makes Tooltip and Popover arrowless by default. Keep that default unless a pointer materially clarifies the anchor; opt back in with `--wa-tooltip-arrow-size`, `--kui-wa-popover-arrow-size`, or a popover's public `--arrow-size`, and use `without-arrow` when local no-arrow intent should survive theme changes.
|
|
62
63
|
14. Treat the complete Web Awesome catalog as support coverage, not a recommendation list. Consider Popup when it replaces custom anchored positioning. Prefer Kerf `Select` over direct Dropdown/Dropdown Item/Select/Option composition, `SegmentedControl` over Button Group, `TabBar` or `SegmentedControl` over Web Awesome Tabs, `LucideIcon` over Web Awesome Icon, and `ResizableRegion` over Split Panel. Use Tree/Tree Item, Animated Image, and Comparison only for a specific required behavior; avoid Zoomable Frame.
|
|
63
64
|
15. Build sidebars, main areas, inspectors, and dialogs from `@kerfjs/ui/layout.css`: an unpadded `.kui-pane`, optional `.kui-pane__toolbar`, one scrolling `.kui-pane__content`, and optional `.kui-pane__footer`. Add `.kui-content` for 24px major vertical separation and `.kui-content-item` for a child-owned 8px inline margin, 1px transparent-or-visible border, 8px padding, and 12px radius. Use the pill modifier for 22px. Do not pad pane shells or duplicate item geometry in wrappers.
|
|
64
65
|
16. Keep a visible collapsible pane's collapse control in its own toolbar. When hidden, move its restore control into the adjacent main toolbar on the corresponding logical edge: leading for an inline-start sidebar and trailing for an inline-end inspector. Collapse the pane completely; do not preserve an empty icon-only rail.
|
|
65
|
-
16. Wrap every toolbar item, including dormant text, in `ToolbarControlGroup`. A group remains 44px outside (`calc(2px + remify(42px))`) when its border/background are transparent; use 8px between groups and inside items. Split dormant and interactive regions: `MenuHeader` fills the available inline width and keeps its label and mutually exclusive semantic count or non-count `badge` together, with an independent logical-end 44px action unless disclosure mode makes the title cluster the button. Its action visual defaults to 18px through `--kui-menu-header-action-icon-size`; never shrink the target to match it. Pass every non-negative safe-integer section quantity through `count` with a localized full spoken `countLabel`; never concatenate it into `label` or put a number in `badge`. `MenuItem.trailing` is dormant; use `MenuActionRow` when primary and trailing actions need sibling 44px native buttons. Its `label`, `icon`, and `trailingActionIcon` slots are also dormant and cannot contain controls. Let panes relocate at narrow widths instead of shrinking targets.
|
|
66
|
-
17. When a recurring concept has no matching export or production recipe, keep its semantics in a thin application adapter while reusing the public layout vocabulary. The composer recipe uses one visible form surface, `
|
|
66
|
+
16. The only direct children of a `Toolbar` zone (`leading`/`center`/`trailing`) are `ToolbarText` (identity/title text) and `ToolbarControlGroup`; never drop bare buttons, inputs, links, or arbitrary markup straight into a zone. `SegmentedControl`, `Select`, a collapsible `TokenSearchField`, and Web Awesome controls all live inside a `ToolbarControlGroup`. A popup menu in a toolbar is a `single` `ToolbarControlGroup` wrapping a `wa-dropdown` whose `slot="trigger"` `wa-button` is the toolbar button and whose `wa-dropdown-item`s are the menu, with the dropdown kept under `data-morph-skip-children`. `PanelHeader` composes these for you as a panel/dialog/page heading. Wrap every toolbar item, including dormant text, in `ToolbarControlGroup`. A group remains 44px outside (`calc(2px + remify(42px))`) when its border/background are transparent; use 8px between groups and inside items. Split dormant and interactive regions: `MenuHeader` fills the available inline width and keeps its label and mutually exclusive semantic count or non-count `badge` together, with an independent logical-end 44px action unless disclosure mode makes the title cluster the button. Its action visual defaults to 18px through `--kui-menu-header-action-icon-size`; never shrink the target to match it. Pass every non-negative safe-integer section quantity through `count` with a localized full spoken `countLabel`; never concatenate it into `label` or put a number in `badge`. `MenuItem.trailing` is dormant; use `MenuActionRow` when primary and trailing actions need sibling 44px native buttons. Its `label`, `icon`, and `trailingActionIcon` slots are also dormant and cannot contain controls. Let panes relocate at narrow widths instead of shrinking targets.
|
|
67
|
+
17. When a recurring concept has no matching export or production recipe, keep its semantics in a thin application adapter while reusing the public layout vocabulary. The composer recipe uses one visible form surface, `PanelHeader` title/summary ids, shared 8px field/action gutters, 24px major rhythm, and a conditional StateBanner as its only nested semantic surface; do not turn every section into a card or double-inset intrinsically bordered controls. The application-local `../docs/examples/command-palette-adapter.tsx` is reference source for one such missing concept, not an `@kerfjs/ui` runtime export or catalog recipe. The application owns its registration, ranking, history, permissions, availability, shortcut policy, focus policy, dispatch, and copy. If a missing concept recurs across products, open an upstream component or recipe request.
|
|
67
68
|
18. Compose `ValueTable` from typed `ValueTableRow` entries instead of handwritten `dt`/`dd` wrappers. Pass `icon` for the optional 24px leading visual; the row owns 8px of root-scaled top and bottom padding, the 8px iconless or 40px icon-bearing separator start, and the common 8px right inset.
|
|
68
|
-
19. Use `
|
|
69
|
+
19. Use `PanelHeader` for a panel, dialog, or page heading, not a custom heading row. It is a plain top `Toolbar` (overriding no Toolbar styles): the leading zone holds an optional icon (a normal bordered `ToolbarControlGroup` given a brand fill with a matching border) and the title as extra-large `ToolbarText`; the app's trailing controls (typically a `ToolbarControlGroup`) go straight in the trailing zone, and the icon group is omitted when no icon is passed. Its optional `summary`/`summaryId` row stays below the title. The app owns modal behavior, focus, dismissal, command policy, and action handling.
|
|
70
|
+
20. Space with the official five-step scale, picked by how connected two elements are — not by eye. `0` `--kui-space-none` = no separation (one unit); `4px` `--kui-space-2xs` = very minor air on a connected cluster; `8px` `--kui-space-xs` = standard, between elements within a group; `16px` `--kui-space-m` = minor, between homogeneous groups; `24px` `--kui-space-l` = major, between heterogeneous groups (the `.kui-content` rhythm). The 8px-vs-24px distinction is inside-a-group vs between-major-differing-regions. `--kui-space-s` (12px) and `--kui-space-xl` (32px) are off-scale exceptions; prefer the five canonical remify-authored tokens. See `docs/layout.md` "Spacing scale".
|
|
71
|
+
21. Pick a whole-screen layout from the opt-in, tree-shakeable subpaths by data + interaction + device, and derive responsiveness from `@kerfjs/ui/device-class`'s `deviceClass()` (`compact` = handset or portrait tablet = one pane at a time). Simple/flat → `NavStack` with one entry (single pane), plus `TabScaffold` (`@kerfjs/ui/tab-scaffold`, iOS bottom tabs, each tab its own `NavStack`) for 2–5 co-equal sections on `compact`. Drill-down → `NavStack` (`@kerfjs/ui/nav-stack`), upgrading to `SplitView` (`@kerfjs/ui/split-view`, list-detail) once both panes fit (`atLeast('tablet')` landscape); `SplitView` collapses to a `NavStack` on `compact`. Complex tool with peripheral panels → `Workbench` (`@kerfjs/ui/workbench`, collapsible rails + drawer) `atLeast('desktop')`, degrading to `NavStack`/overlays below. These are declarative (the app owns the stack/selection/collapsed/active state as signals) with disposer-returning `wire…` helpers; each ships a companion CSS import and stays out of the barrel. Dialogs pick the same inner layout, then present per device class (full-screen modal on `compact`, inline on desktop). See `docs/app-layouts.md`.
|
|
69
72
|
|
|
70
73
|
Common mistakes:
|
|
71
74
|
|
|
@@ -89,4 +92,9 @@ Common mistakes:
|
|
|
89
92
|
| Let the document, pane, and list all scroll | Keep fixed chrome outside one `.kui-pane__content` scroll owner per pane |
|
|
90
93
|
| Invent an `@kerfjs/ui` command-palette export | Keep the semantics application-local, reuse public layout vocabulary, and propose recurring behavior upstream |
|
|
91
94
|
| Handwrite `ValueTable` row wrappers or compensate their padding or separators | Compose `ValueTableRow`; its root-scaled block padding, optional icon hook, and separator geometry are public contract |
|
|
92
|
-
| Rebuild a dialog heading
|
|
95
|
+
| Rebuild a panel/dialog heading row by hand | Use `PanelHeader`: a plain toolbar with an optional bordered icon group, an xl title, trailing controls you pass, and the optional subtitle below |
|
|
96
|
+
| Force a `width`/`height`/`padding` on a component to size or space it | Let it size to its content and tokens; a forced box leaves a halo or a stretched oval — adjust an icon-size or spacing token, not the box |
|
|
97
|
+
| Wrap a component or region in a card, border, backdrop, or outline to "contain" it | Let it sit on the surface; add a `.kui-content-item` only for a real distinction — hierarchy comes from alignment, spacing, and type first |
|
|
98
|
+
| Add another container's padding on top of a content-item's own margin | Pick one owner of the inset; a pane has no padding and its `.kui-content` children own the 8/1/8 geometry — stacking them double-insets |
|
|
99
|
+
| Keep chrome, a label, or a readout that aids no decision | Delete it; every element must help a person decide or act |
|
|
100
|
+
| Override a component's default size or color because it "looks off" | Trust the default (a LucideIcon is 24px) and fix the surrounding layout instead |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Web Awesome JSX signatures for the UI authoring corpus
|
|
2
2
|
|
|
3
|
-
Generated from the emitted `@kerfjs/ui@5.0.0-beta.
|
|
3
|
+
Generated from the emitted `@kerfjs/ui@5.0.0-beta.7` declaration boundary. Import `@kerfjs/ui/webawesome` for type effects when authoring direct `wa-*` JSX. The module emits no runtime behavior and does not register custom elements.
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
6
|
import { KerfCustomElement } from 'kerfjs/jsx-runtime';
|
package/dist/app-tab.d.ts
CHANGED
|
@@ -21,8 +21,10 @@ interface AppTabProps {
|
|
|
21
21
|
selectAction?: string;
|
|
22
22
|
closeAction?: string;
|
|
23
23
|
className?: string;
|
|
24
|
+
/** Render as an unanimated loading skeleton, disabling select/close and dragging. */
|
|
25
|
+
placeholder?: boolean;
|
|
24
26
|
rootAttributes?: AppTabRootAttributes;
|
|
25
27
|
}
|
|
26
|
-
declare function AppTab({ id, name, selected, closable, draggable, leading, trailing, closeIcon, selectAction, closeAction, className, rootAttributes }: AppTabProps): SafeHtml;
|
|
28
|
+
declare function AppTab({ id, name, selected, closable, draggable, leading, trailing, closeIcon, selectAction, closeAction, className, placeholder, rootAttributes }: AppTabProps): SafeHtml;
|
|
27
29
|
|
|
28
30
|
export { AppTab, type AppTabProps };
|
package/dist/app-tab.js
CHANGED