@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
package/dist/select.d.ts
CHANGED
|
@@ -18,11 +18,14 @@ interface SelectProps<Value extends string = string> {
|
|
|
18
18
|
ariaLabel?: string;
|
|
19
19
|
choices: readonly SelectChoice<Value>[];
|
|
20
20
|
className?: string;
|
|
21
|
-
placeholder
|
|
21
|
+
/** Empty-value hint text shown in the closed control (the native select placeholder). */
|
|
22
|
+
placeholderText?: string;
|
|
22
23
|
disabled?: boolean;
|
|
23
24
|
fitMenu?: boolean;
|
|
24
25
|
renderSelected?: (choice: SelectChoice<Value>) => SafeHtml;
|
|
26
|
+
/** Render as an unanimated loading skeleton: the label above a static, empty control box. */
|
|
27
|
+
placeholder?: boolean;
|
|
25
28
|
}
|
|
26
|
-
declare function Select<Value extends string>({ name, value, label, ariaLabel, choices, className,
|
|
29
|
+
declare function Select<Value extends string>({ name, value, label, ariaLabel, choices, className, placeholderText, disabled, fitMenu, renderSelected, placeholder }: SelectProps<Value>): SafeHtml;
|
|
27
30
|
|
|
28
31
|
export { Select, type SelectChoice, type SelectProps };
|
package/dist/select.js
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as kerfjs from 'kerfjs';
|
|
2
|
+
|
|
3
|
+
interface SkeletonProps {
|
|
4
|
+
/** Width as any CSS length (e.g. `remify(120px)`, `60%`). Defaults to filling its slot. */
|
|
5
|
+
width?: string;
|
|
6
|
+
/** Height as any CSS length. Defaults to a single text line. */
|
|
7
|
+
height?: string;
|
|
8
|
+
/** Corner radius override (a CSS length). Defaults to the small radius token. */
|
|
9
|
+
radius?: string;
|
|
10
|
+
/** Render this many stacked lines (the last one shorter), for multi-line text. */
|
|
11
|
+
lines?: number;
|
|
12
|
+
/** Accessible label. Omit to keep the block decorative (`aria-hidden`). */
|
|
13
|
+
label?: string;
|
|
14
|
+
className?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A subtle, deliberately **unanimated** loading placeholder block. Use it for a
|
|
18
|
+
* value slot whose content is not yet known, on its own or via a component's
|
|
19
|
+
* `placeholder` prop. Decorative by default (`aria-hidden`); pass `label` to
|
|
20
|
+
* announce it. Sizes to its slot unless `width`/`height` are given.
|
|
21
|
+
*/
|
|
22
|
+
declare function Skeleton({ width, height, radius, lines, label, className }: SkeletonProps): kerfjs.SafeHtml;
|
|
23
|
+
|
|
24
|
+
export { Skeleton, type SkeletonProps };
|
package/dist/skeleton.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"skeleton.js"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { SafeHtml } from 'kerfjs';
|
|
2
|
+
|
|
3
|
+
interface SplitViewResizable {
|
|
4
|
+
size: number;
|
|
5
|
+
min: number;
|
|
6
|
+
max: number;
|
|
7
|
+
}
|
|
8
|
+
interface SplitViewProps {
|
|
9
|
+
id: string;
|
|
10
|
+
label: string;
|
|
11
|
+
/** The list (primary) pane. */
|
|
12
|
+
list: SafeHtml;
|
|
13
|
+
/** The detail (secondary) pane. */
|
|
14
|
+
detail: SafeHtml;
|
|
15
|
+
/**
|
|
16
|
+
* Compact ("one pane at a time") classes — a handset or portrait tablet.
|
|
17
|
+
* Derive from `deviceClass().value.compact`. When true the split collapses to
|
|
18
|
+
* a `NavStack`: the list is the root and the detail is pushed over it.
|
|
19
|
+
*/
|
|
20
|
+
compact?: boolean;
|
|
21
|
+
/** In compact mode, whether the detail is currently pushed over the list. */
|
|
22
|
+
detailActive?: boolean;
|
|
23
|
+
/** Title/label for the list (compact NavStack root + region label). */
|
|
24
|
+
listTitle?: string;
|
|
25
|
+
/** Title/label for the detail (compact NavStack pushed view + region label). */
|
|
26
|
+
detailTitle?: string;
|
|
27
|
+
/** Back label for the compact NavStack (default "Back"). */
|
|
28
|
+
backLabel?: string;
|
|
29
|
+
/** A resizable separator on roomy classes (min/max px). Omit for a fixed split. */
|
|
30
|
+
resizable?: SplitViewResizable;
|
|
31
|
+
className?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A list-detail (master-detail) split. On roomy classes it shows both panes side
|
|
35
|
+
* by side with an optional resizable separator; on compact classes it collapses
|
|
36
|
+
* to a `NavStack` (list → detail). See `docs/23-app-layouts.md` §3.2. Compose the
|
|
37
|
+
* resizable wiring with `wireResizableRegions` and the compact back with
|
|
38
|
+
* `wireNavStack`.
|
|
39
|
+
*/
|
|
40
|
+
declare function SplitView({ id, label, list, detail, compact, detailActive, listTitle, detailTitle, backLabel, resizable, className }: SplitViewProps): SafeHtml;
|
|
41
|
+
|
|
42
|
+
export { SplitView, type SplitViewProps, type SplitViewResizable };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { NavStack } from './chunk-N3SUXLLS.js';
|
|
2
|
+
|
|
3
|
+
import { ResizableRegion } from './chunk-M5JRI6MN.js';
|
|
4
|
+
|
|
5
|
+
import { jsx, jsxs } from 'kerfjs/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
function SplitView({ id, label, list, detail, compact = false, detailActive = false, listTitle = "", detailTitle = "", backLabel = "Back", resizable, className = "" }) {
|
|
8
|
+
if (compact) {
|
|
9
|
+
const views = detailActive ? [{ key: "list", title: listTitle, content: list }, { key: "detail", title: detailTitle, content: detail }] : [{ key: "list", title: listTitle, content: list }];
|
|
10
|
+
return /* @__PURE__ */ jsx("div", { class: `kui-split-view kui-split-view--compact ${className}`.trim(), id, "data-component": "split-view", "data-split-mode": "compact", children: /* @__PURE__ */ jsx(NavStack, { id: `${id}-stack`, label, views, backLabel }) });
|
|
11
|
+
}
|
|
12
|
+
const listPane = resizable ? /* @__PURE__ */ jsx(ResizableRegion, { id: `${id}-list`, label: listTitle || "List", size: resizable.size, min: resizable.min, max: resizable.max, children: /* @__PURE__ */ jsx("div", { class: "kui-split-view__list kui-pane", "data-split-list": true, children: list }) }) : /* @__PURE__ */ jsx("div", { class: "kui-split-view__list kui-pane", "data-split-list": true, children: list });
|
|
13
|
+
return /* @__PURE__ */ jsxs("div", { class: `kui-split-view ${className}`.trim(), id, "data-component": "split-view", "data-split-mode": "split", "aria-label": label, children: [
|
|
14
|
+
listPane,
|
|
15
|
+
/* @__PURE__ */ jsx("div", { class: "kui-split-view__detail kui-pane", "data-split-detail": true, "aria-label": detailTitle || void 0, children: detail })
|
|
16
|
+
] });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { SplitView };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=split-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/split-view.tsx"],"names":[],"mappings":";;;;;;AA4CO,SAAS,UAAU,EAAE,EAAA,EAAI,OAAO,IAAA,EAAM,MAAA,EAAQ,UAAU,KAAA,EAAO,YAAA,GAAe,OAAO,SAAA,GAAY,EAAA,EAAI,cAAc,EAAA,EAAI,SAAA,GAAY,QAAQ,SAAA,EAAW,SAAA,GAAY,IAAG,EAAmB;AAC7L,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,MAAM,KAAA,GAAQ,YAAA,GACV,CAAC,EAAE,GAAA,EAAK,MAAA,EAAQ,KAAA,EAAO,SAAA,EAAW,OAAA,EAAS,IAAA,EAAK,EAAG,EAAE,GAAA,EAAK,QAAA,EAAU,KAAA,EAAO,WAAA,EAAa,OAAA,EAAS,MAAA,EAAQ,CAAA,GACzG,CAAC,EAAE,GAAA,EAAK,MAAA,EAAQ,KAAA,EAAO,SAAA,EAAW,OAAA,EAAS,IAAA,EAAM,CAAA;AACrD,IAAA,uBAAO,GAAA,CAAC,SAAI,KAAA,EAAO,CAAA,uCAAA,EAA0C,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK,EAAG,EAAA,EAAQ,gBAAA,EAAe,YAAA,EAAa,mBAAgB,SAAA,EACnI,QAAA,kBAAA,GAAA,CAAC,YAAS,EAAA,EAAI,CAAA,EAAG,EAAE,CAAA,MAAA,CAAA,EAAU,KAAA,EAAc,KAAA,EAAc,SAAA,EAAsB,CAAA,EACjF,CAAA;AAAA,EACF;AACA,EAAA,MAAM,QAAA,GAAW,SAAA,mBACb,GAAA,CAAC,eAAA,EAAA,EAAgB,IAAI,CAAA,EAAG,EAAE,CAAA,KAAA,CAAA,EAAS,KAAA,EAAO,aAAa,MAAA,EAAQ,IAAA,EAAM,SAAA,CAAU,IAAA,EAAM,KAAK,SAAA,CAAU,GAAA,EAAK,GAAA,EAAK,SAAA,CAAU,KACtH,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,+BAAA,EAAgC,mBAAe,IAAA,EAAE,QAAA,EAAA,IAAA,EAAK,CAAA,EACnE,CAAA,uBACC,KAAA,EAAA,EAAI,KAAA,EAAM,+BAAA,EAAgC,iBAAA,EAAe,MAAE,QAAA,EAAA,IAAA,EAAK,CAAA;AACrE,EAAA,uBAAO,IAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,CAAA,eAAA,EAAkB,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK,EAAG,EAAA,EAAQ,gBAAA,EAAe,YAAA,EAAa,iBAAA,EAAgB,OAAA,EAAQ,cAAY,KAAA,EAC9H,QAAA,EAAA;AAAA,IAAA,QAAA;AAAA,oBACD,GAAA,CAAC,SAAI,KAAA,EAAM,iCAAA,EAAkC,qBAAiB,IAAA,EAAC,YAAA,EAAY,WAAA,IAAe,MAAA,EAAY,QAAA,EAAA,MAAA,EAAO;AAAA,GAAA,EAC/G,CAAA;AACF","file":"split-view.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport { NavStack } from './nav-stack.js';\nimport { ResizableRegion } from './resizable-region.js';\n\nexport interface SplitViewResizable {\n size: number;\n min: number;\n max: number;\n}\n\nexport interface SplitViewProps {\n id: string;\n label: string;\n /** The list (primary) pane. */\n list: SafeHtml;\n /** The detail (secondary) pane. */\n detail: SafeHtml;\n /**\n * Compact (\"one pane at a time\") classes — a handset or portrait tablet.\n * Derive from `deviceClass().value.compact`. When true the split collapses to\n * a `NavStack`: the list is the root and the detail is pushed over it.\n */\n compact?: boolean;\n /** In compact mode, whether the detail is currently pushed over the list. */\n detailActive?: boolean;\n /** Title/label for the list (compact NavStack root + region label). */\n listTitle?: string;\n /** Title/label for the detail (compact NavStack pushed view + region label). */\n detailTitle?: string;\n /** Back label for the compact NavStack (default \"Back\"). */\n backLabel?: string;\n /** A resizable separator on roomy classes (min/max px). Omit for a fixed split. */\n resizable?: SplitViewResizable;\n className?: string;\n}\n\n/**\n * A list-detail (master-detail) split. On roomy classes it shows both panes side\n * by side with an optional resizable separator; on compact classes it collapses\n * to a `NavStack` (list → detail). See `docs/23-app-layouts.md` §3.2. Compose the\n * resizable wiring with `wireResizableRegions` and the compact back with\n * `wireNavStack`.\n */\nexport function SplitView({ id, label, list, detail, compact = false, detailActive = false, listTitle = '', detailTitle = '', backLabel = 'Back', resizable, className = '' }: SplitViewProps) {\n if (compact) {\n const views = detailActive\n ? [{ key: 'list', title: listTitle, content: list }, { key: 'detail', title: detailTitle, content: detail }]\n : [{ key: 'list', title: listTitle, content: list }];\n return <div class={`kui-split-view kui-split-view--compact ${className}`.trim()} id={id} data-component=\"split-view\" data-split-mode=\"compact\">\n <NavStack id={`${id}-stack`} label={label} views={views} backLabel={backLabel} />\n </div>;\n }\n const listPane = resizable\n ? <ResizableRegion id={`${id}-list`} label={listTitle || 'List'} size={resizable.size} min={resizable.min} max={resizable.max}>\n <div class=\"kui-split-view__list kui-pane\" data-split-list>{list}</div>\n </ResizableRegion>\n : <div class=\"kui-split-view__list kui-pane\" data-split-list>{list}</div>;\n return <div class={`kui-split-view ${className}`.trim()} id={id} data-component=\"split-view\" data-split-mode=\"split\" aria-label={label}>\n {listPane}\n <div class=\"kui-split-view__detail kui-pane\" data-split-detail aria-label={detailTitle || undefined}>{detail}</div>\n </div>;\n}\n"]}
|
package/dist/state-banner.d.ts
CHANGED
|
@@ -9,7 +9,9 @@ interface StateBannerProps {
|
|
|
9
9
|
tone?: StateBannerTone;
|
|
10
10
|
urgency?: 'status' | 'alert';
|
|
11
11
|
className?: string;
|
|
12
|
+
/** Render the title and detail as unanimated loading skeletons, keeping the icon and tone. */
|
|
13
|
+
placeholder?: boolean;
|
|
12
14
|
}
|
|
13
|
-
declare function StateBanner({ title, detail, icon, action, tone, urgency, className }: StateBannerProps): SafeHtml;
|
|
15
|
+
declare function StateBanner({ title, detail, icon, action, tone, urgency, className, placeholder }: StateBannerProps): SafeHtml;
|
|
14
16
|
|
|
15
17
|
export { StateBanner, type StateBannerProps, type StateBannerTone };
|
package/dist/state-banner.js
CHANGED
|
@@ -24,12 +24,33 @@
|
|
|
24
24
|
--kui-font-l: var(--wa-font-size-l, 20px);
|
|
25
25
|
--kui-font-2xl: var(--wa-font-size-2xl, 32px);
|
|
26
26
|
--kui-font-weight-semibold: var(--wa-font-weight-semibold, 650);
|
|
27
|
+
/* Official spacing scale (see docs/layout.md "Spacing scale"). Five canonical
|
|
28
|
+
steps, each expressing one relationship — pick by how connected the elements
|
|
29
|
+
are, not by eye:
|
|
30
|
+
0px --kui-space-none no separation (elements read as one unit)
|
|
31
|
+
4px --kui-space-2xs very minor — connected, but readability/aesthetics
|
|
32
|
+
demand a hair of air
|
|
33
|
+
8px --kui-space-xs standard — between elements within a group
|
|
34
|
+
16px --kui-space-m minor — between homogeneous groups
|
|
35
|
+
24px --kui-space-l major — between heterogeneous groups
|
|
36
|
+
--kui-space-s (12px) and --kui-space-xl (32px) are off the canonical rhythm;
|
|
37
|
+
reach for them only as deliberate exceptions, not as default steps. All
|
|
38
|
+
scalable values are remify-authored against the fixed 16px baseline. */
|
|
39
|
+
--kui-space-none: 0;
|
|
27
40
|
--kui-space-2xs: var(--wa-space-2xs, 0.25rem);
|
|
28
41
|
--kui-space-xs: var(--wa-space-xs, 0.5rem);
|
|
29
42
|
--kui-space-s: var(--wa-space-s, 0.75rem);
|
|
30
43
|
--kui-space-m: var(--wa-space-m, 1rem);
|
|
31
44
|
--kui-space-l: var(--wa-space-l, 1.5rem);
|
|
32
45
|
--kui-space-xl: var(--wa-space-xl, 2rem);
|
|
46
|
+
/* Device-class breakpoints, mirrored from @kerfjs/ui/device-class's
|
|
47
|
+
DEFAULT_BREAKPOINTS so CSS media queries and the reactive deviceClass()
|
|
48
|
+
signal read the same minimum widths. Authored as literal px (a media-query
|
|
49
|
+
boundary is a physical width, not a scalable length). */
|
|
50
|
+
--kui-bp-mobile: 360px;
|
|
51
|
+
--kui-bp-tablet: 720px;
|
|
52
|
+
--kui-bp-desktop: 1024px;
|
|
53
|
+
--kui-bp-xl-desktop: 1440px;
|
|
33
54
|
--kui-layout-inline-margin: var(--kui-space-xs);
|
|
34
55
|
--kui-layout-item-border-width: 1px;
|
|
35
56
|
--kui-layout-item-padding: var(--kui-space-xs);
|
|
@@ -93,10 +93,23 @@
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
.kui-menu-header__action {
|
|
96
|
+
/* A fitted square: the icon size plus its padding and 1px border, instead of a
|
|
97
|
+
forced 44px box that left an oversized halo around the ~18px icon and, via the
|
|
98
|
+
parent's align-items: stretch, a vertically stretched oval. `align-self:
|
|
99
|
+
center` keeps it centered in the header's 44px row without stretching. Sizing
|
|
100
|
+
off the icon variable keeps it square and correct at any icon size. */
|
|
101
|
+
--_kui-menu-header-action-size: calc(
|
|
102
|
+
var(--_kui-menu-header-action-icon-size) +
|
|
103
|
+
(2 * var(--kui-layout-item-padding, 0.5rem)) + 2px
|
|
104
|
+
);
|
|
96
105
|
appearance: none;
|
|
97
|
-
|
|
98
|
-
|
|
106
|
+
display: inline-flex;
|
|
107
|
+
width: var(--_kui-menu-header-action-size);
|
|
108
|
+
height: var(--_kui-menu-header-action-size);
|
|
99
109
|
padding: var(--kui-layout-item-padding, 0.5rem);
|
|
110
|
+
align-items: center;
|
|
111
|
+
justify-content: center;
|
|
112
|
+
align-self: center;
|
|
100
113
|
cursor: pointer;
|
|
101
114
|
flex: none;
|
|
102
115
|
}
|
|
@@ -138,6 +151,10 @@
|
|
|
138
151
|
|
|
139
152
|
.kui-menu-header__action > svg,
|
|
140
153
|
.kui-menu-header__action-layer > svg {
|
|
154
|
+
/* min-block-size: 0 overrides the flex item's automatic minimum, which would
|
|
155
|
+
otherwise resolve to the icon SVG's intrinsic 24px viewBox height and stretch
|
|
156
|
+
the button past its used icon size. */
|
|
141
157
|
width: var(--_kui-menu-header-action-icon-size);
|
|
142
158
|
height: var(--_kui-menu-header-action-icon-size);
|
|
159
|
+
min-block-size: 0;
|
|
143
160
|
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
.kui-nav-stack {
|
|
2
|
+
display: flex;
|
|
3
|
+
min-width: 0;
|
|
4
|
+
min-height: 0;
|
|
5
|
+
height: 100%;
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
background: var(--kui-color-surface);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.kui-nav-stack__chrome {
|
|
12
|
+
display: flex;
|
|
13
|
+
min-width: 0;
|
|
14
|
+
min-height: calc(2px + 2.625rem);
|
|
15
|
+
padding-inline: var(--kui-space-xs);
|
|
16
|
+
border-block-end: 1px solid var(--kui-color-border);
|
|
17
|
+
align-items: center;
|
|
18
|
+
gap: var(--kui-space-xs);
|
|
19
|
+
background: var(--kui-color-surface);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.kui-nav-stack__lead {
|
|
23
|
+
display: flex;
|
|
24
|
+
flex: 1;
|
|
25
|
+
min-width: 0;
|
|
26
|
+
align-items: center;
|
|
27
|
+
gap: var(--kui-space-2xs);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.kui-nav-stack__back {
|
|
31
|
+
display: grid;
|
|
32
|
+
width: 2.5rem;
|
|
33
|
+
height: 2.5rem;
|
|
34
|
+
padding: 0;
|
|
35
|
+
border: 1px solid transparent;
|
|
36
|
+
border-radius: var(--kui-layout-rounded-radius, calc(1px + 0.6875rem));
|
|
37
|
+
color: inherit;
|
|
38
|
+
background: transparent;
|
|
39
|
+
cursor: pointer;
|
|
40
|
+
place-items: center;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.kui-nav-stack__back:hover {
|
|
44
|
+
border-color: var(--kui-color-border);
|
|
45
|
+
background: var(--kui-color-neutral-fill-normal);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.kui-nav-stack__back:focus-visible {
|
|
49
|
+
outline: var(--kui-focus-ring);
|
|
50
|
+
outline-offset: 1px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.kui-nav-stack__back-icon {
|
|
54
|
+
display: block;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* The title is ToolbarText (which owns its font, height, and ellipsis); it just
|
|
58
|
+
needs to flex to fill the lead zone beside the back control. */
|
|
59
|
+
.kui-nav-stack__title {
|
|
60
|
+
flex: 1 1 auto;
|
|
61
|
+
min-width: 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.kui-nav-stack__actions {
|
|
65
|
+
display: flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
gap: var(--kui-space-2xs);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.kui-nav-stack__viewport {
|
|
71
|
+
position: relative;
|
|
72
|
+
flex: 1;
|
|
73
|
+
min-height: 0;
|
|
74
|
+
overflow: hidden;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.kui-nav-stack__view {
|
|
78
|
+
position: absolute;
|
|
79
|
+
overflow: auto;
|
|
80
|
+
background: var(--kui-color-surface);
|
|
81
|
+
transition: transform 200ms ease;
|
|
82
|
+
inset: 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* Only the active view (and any view mid-exit) is shown; lower entries stay
|
|
86
|
+
mounted for state/focus but hidden behind it. */
|
|
87
|
+
.kui-nav-stack__view[data-nav-active="false"] {
|
|
88
|
+
visibility: hidden;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.kui-nav-stack__view[data-nav-exiting="true"] {
|
|
92
|
+
z-index: 1;
|
|
93
|
+
visibility: visible;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Push: the incoming view starts off the trailing edge and slides to rest.
|
|
97
|
+
Pop: the outgoing view starts at rest and slides off the trailing edge. */
|
|
98
|
+
.kui-nav-stack__view--entering,
|
|
99
|
+
.kui-nav-stack__view--exiting {
|
|
100
|
+
transform: translateX(100%);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.kui-nav-stack__bottom {
|
|
104
|
+
border-block-start: 1px solid var(--kui-color-border);
|
|
105
|
+
background: var(--kui-color-surface);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@media (prefers-reduced-motion: reduce) {
|
|
109
|
+
.kui-nav-stack__view {
|
|
110
|
+
transition: none;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
.kui-panel-header {
|
|
2
|
+
display: grid;
|
|
3
|
+
width: 100%;
|
|
4
|
+
gap: 0;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/* The only chrome PanelHeader adds to the plain Toolbar: the icon group is a
|
|
8
|
+
normal (bordered) control group given a brand fill, with its border color set
|
|
9
|
+
to match the fill so it reads as a solid filled tile rather than a bordered
|
|
10
|
+
pill. Both are the group's own theming variables — no Toolbar style is
|
|
11
|
+
overridden. */
|
|
12
|
+
.kui-panel-header__icon.kui-toolbar-control-group {
|
|
13
|
+
--kui-toolbar-control-background: var(
|
|
14
|
+
--kui-panel-header-icon-background,
|
|
15
|
+
var(--kui-color-brand-fill-quiet)
|
|
16
|
+
);
|
|
17
|
+
--kui-toolbar-control-border: var(
|
|
18
|
+
--kui-panel-header-icon-border,
|
|
19
|
+
var(--kui-color-brand-fill-quiet)
|
|
20
|
+
);
|
|
21
|
+
color: var(
|
|
22
|
+
--kui-panel-header-icon-foreground,
|
|
23
|
+
var(--kui-color-brand-on-quiet)
|
|
24
|
+
);
|
|
25
|
+
/* The icon is a raw svg child (not a button), so the group's default
|
|
26
|
+
flex-start alignment would pin it to the left of the filled tile. Center it
|
|
27
|
+
on both axes inside the circle. */
|
|
28
|
+
justify-content: center;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.kui-panel-header__icon svg {
|
|
32
|
+
display: block;
|
|
33
|
+
width: 1.375rem;
|
|
34
|
+
height: 1.375rem;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* The subtitle sits on its own row, its text left-aligned with the title text
|
|
38
|
+
(past the toolbar padding, the optional icon group + leading gap, and the
|
|
39
|
+
title's own inline padding). */
|
|
40
|
+
.kui-panel-header__summary {
|
|
41
|
+
min-width: 0;
|
|
42
|
+
margin: 0;
|
|
43
|
+
overflow: hidden;
|
|
44
|
+
padding-inline-start: var(--kui-panel-header-title-inset);
|
|
45
|
+
color: var(--kui-color-text-quiet);
|
|
46
|
+
text-overflow: ellipsis;
|
|
47
|
+
white-space: nowrap;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.kui-panel-header[data-has-icon="false"] {
|
|
51
|
+
--kui-panel-header-title-inset: calc(
|
|
52
|
+
var(--kui-toolbar-gap, 0.5rem) +
|
|
53
|
+
var(--kui-layout-item-padding, 0.5rem)
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.kui-panel-header[data-has-icon="true"] {
|
|
58
|
+
--kui-panel-header-title-inset: calc(
|
|
59
|
+
var(--kui-toolbar-gap, 0.5rem) +
|
|
60
|
+
var(--kui-toolbar-group-size, calc(2px + 2.625rem)) +
|
|
61
|
+
var(--kui-toolbar-gap, 0.5rem) +
|
|
62
|
+
var(--kui-layout-item-padding, 0.5rem)
|
|
63
|
+
);
|
|
64
|
+
}
|
package/dist/styles/select.css
CHANGED
|
@@ -94,3 +94,45 @@
|
|
|
94
94
|
letter-spacing: 0.04em;
|
|
95
95
|
text-transform: uppercase;
|
|
96
96
|
}
|
|
97
|
+
|
|
98
|
+
/* Loading placeholder: a static box that mirrors the closed combobox (label
|
|
99
|
+
above, a value skeleton and a dormant chevron inside a 44px control box).
|
|
100
|
+
Rendered instead of the interactive wa-select, so it needs no registration. */
|
|
101
|
+
.kui-select--placeholder {
|
|
102
|
+
display: block;
|
|
103
|
+
width: 100%;
|
|
104
|
+
}
|
|
105
|
+
.kui-select__placeholder-label {
|
|
106
|
+
display: block;
|
|
107
|
+
margin-block-end: 0.25rem;
|
|
108
|
+
color: var(--kui-color-text);
|
|
109
|
+
font-size: var(--kui-font-s);
|
|
110
|
+
line-height: 1.4;
|
|
111
|
+
}
|
|
112
|
+
.kui-select__placeholder-box {
|
|
113
|
+
display: flex;
|
|
114
|
+
min-height: 2.75rem;
|
|
115
|
+
align-items: center;
|
|
116
|
+
justify-content: space-between;
|
|
117
|
+
gap: 0.5rem;
|
|
118
|
+
padding-inline: var(--kui-layout-item-padding, 0.5rem);
|
|
119
|
+
border: 1px solid var(--kui-color-border);
|
|
120
|
+
border-radius: var(--kui-layout-rounded-radius, calc(1px + 0.6875rem));
|
|
121
|
+
background: var(--kui-color-surface);
|
|
122
|
+
}
|
|
123
|
+
.kui-select__placeholder-box .kui-skeleton {
|
|
124
|
+
max-width: 10rem;
|
|
125
|
+
}
|
|
126
|
+
.kui-select__placeholder-chevron {
|
|
127
|
+
display: inline-flex;
|
|
128
|
+
width: 1rem;
|
|
129
|
+
height: 1rem;
|
|
130
|
+
flex: none;
|
|
131
|
+
color: var(--kui-color-text-quiet);
|
|
132
|
+
transform: scale(var(--kui-disclosure-icon-scale, 0.85));
|
|
133
|
+
}
|
|
134
|
+
.kui-select__placeholder-chevron svg {
|
|
135
|
+
display: block;
|
|
136
|
+
width: 100%;
|
|
137
|
+
height: 100%;
|
|
138
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
.kui-skeleton {
|
|
2
|
+
--kui-skeleton-radius: var(--kui-radius-s);
|
|
3
|
+
|
|
4
|
+
display: inline-block;
|
|
5
|
+
width: 100%;
|
|
6
|
+
max-width: 100%;
|
|
7
|
+
height: 1em;
|
|
8
|
+
min-width: 1.5rem;
|
|
9
|
+
border-radius: var(--kui-skeleton-radius);
|
|
10
|
+
/* Deliberately unanimated: a flat, subtle fill that reads as "not yet loaded"
|
|
11
|
+
without drawing the eye or implying progress. */
|
|
12
|
+
background: color-mix(in srgb, var(--kui-color-text) 12%, transparent);
|
|
13
|
+
vertical-align: middle;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.kui-skeleton-lines {
|
|
17
|
+
display: flex;
|
|
18
|
+
width: 100%;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
gap: var(--kui-space-2xs);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.kui-skeleton-lines .kui-skeleton {
|
|
24
|
+
height: 0.8em;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@media (forced-colors: active) {
|
|
28
|
+
.kui-skeleton {
|
|
29
|
+
background: GrayText;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
.kui-split-view {
|
|
2
|
+
display: flex;
|
|
3
|
+
min-width: 0;
|
|
4
|
+
min-height: 0;
|
|
5
|
+
height: 100%;
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
background: var(--kui-color-surface);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* The list pane; when not resizable it takes an intrinsic column and the detail
|
|
11
|
+
grows to fill. A resizable list is sized by ResizableRegion instead. */
|
|
12
|
+
.kui-split-view__list {
|
|
13
|
+
min-width: 0;
|
|
14
|
+
min-height: 0;
|
|
15
|
+
flex: 0 0 auto;
|
|
16
|
+
width: var(--kui-split-view-list-width, 20rem);
|
|
17
|
+
border-inline-end: 1px solid var(--kui-color-border);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.kui-split-view > .kui-resizable-region .kui-split-view__list {
|
|
21
|
+
width: 100%;
|
|
22
|
+
border-inline-end: 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.kui-split-view__detail {
|
|
26
|
+
flex: 1;
|
|
27
|
+
min-width: 0;
|
|
28
|
+
min-height: 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Compact mode is a full-bleed NavStack. */
|
|
32
|
+
.kui-split-view--compact {
|
|
33
|
+
display: block;
|
|
34
|
+
height: 100%;
|
|
35
|
+
}
|
package/dist/styles/styles.css
CHANGED
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
@import "./resizable-region.css";
|
|
12
12
|
@import "./app-tab.css";
|
|
13
13
|
@import "./tab-bar.css";
|
|
14
|
-
@import "./page-header.css";
|
|
15
14
|
@import "./loading-spinner.css";
|
|
15
|
+
@import "./skeleton.css";
|
|
16
16
|
@import "./select.css";
|
|
17
17
|
@import "./segmented-control.css";
|
|
18
18
|
@import "./token-search-field.css";
|
|
19
19
|
@import "./state-banner.css";
|
|
20
20
|
@import "./empty-state.css";
|
|
21
|
-
@import "./
|
|
21
|
+
@import "./panel-header.css";
|
|
22
22
|
@import "./value-table.css";
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
.kui-tab-scaffold {
|
|
2
|
+
display: flex;
|
|
3
|
+
min-width: 0;
|
|
4
|
+
min-height: 0;
|
|
5
|
+
height: 100%;
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
background: var(--kui-color-surface);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.kui-tab-scaffold__scenes {
|
|
12
|
+
position: relative;
|
|
13
|
+
flex: 1;
|
|
14
|
+
min-height: 0;
|
|
15
|
+
overflow: hidden;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* Every tab stays mounted (its NavStack keeps its stack and scroll); only the
|
|
19
|
+
active scene is shown. */
|
|
20
|
+
.kui-tab-scaffold__scene {
|
|
21
|
+
position: absolute;
|
|
22
|
+
overflow: auto;
|
|
23
|
+
inset: 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.kui-tab-scaffold__scene[data-active="false"] {
|
|
27
|
+
visibility: hidden;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.kui-tab-scaffold__bar {
|
|
31
|
+
display: flex;
|
|
32
|
+
flex: 0 0 auto;
|
|
33
|
+
padding-block-start: var(--kui-space-2xs);
|
|
34
|
+
/* Respect the home-indicator safe area on iOS-class devices. */
|
|
35
|
+
padding-block-end: calc(
|
|
36
|
+
var(--kui-space-2xs) + env(safe-area-inset-bottom, 0px)
|
|
37
|
+
);
|
|
38
|
+
border-block-start: 1px solid var(--kui-color-border);
|
|
39
|
+
background: var(--kui-color-surface);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.kui-tab-scaffold__tab {
|
|
43
|
+
display: flex;
|
|
44
|
+
flex: 1 1 0;
|
|
45
|
+
min-width: 0;
|
|
46
|
+
min-height: 2.75rem;
|
|
47
|
+
padding: var(--kui-space-2xs) var(--kui-space-xs);
|
|
48
|
+
border: 0;
|
|
49
|
+
color: var(--kui-color-text-quiet);
|
|
50
|
+
background: transparent;
|
|
51
|
+
cursor: pointer;
|
|
52
|
+
font: inherit;
|
|
53
|
+
flex-direction: column;
|
|
54
|
+
align-items: center;
|
|
55
|
+
justify-content: center;
|
|
56
|
+
gap: 0.125rem;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.kui-tab-scaffold__tab[aria-selected="true"] {
|
|
60
|
+
color: var(--kui-color-accent-text);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.kui-tab-scaffold__tab:focus-visible {
|
|
64
|
+
outline: var(--kui-focus-ring);
|
|
65
|
+
outline-offset: -2px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.kui-tab-scaffold__tab-icon {
|
|
69
|
+
display: grid;
|
|
70
|
+
place-items: center;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.kui-tab-scaffold__tab-icon svg {
|
|
74
|
+
width: 1.375rem;
|
|
75
|
+
height: 1.375rem;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.kui-tab-scaffold__tab-label {
|
|
79
|
+
max-width: 100%;
|
|
80
|
+
overflow: hidden;
|
|
81
|
+
font-size: var(--kui-font-2xs);
|
|
82
|
+
text-overflow: ellipsis;
|
|
83
|
+
white-space: nowrap;
|
|
84
|
+
}
|