@kerfjs/ui 5.0.0-beta.52 → 5.0.0-beta.54
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 +1 -0
- package/ai/application-ui-diagnostic-ids-v1.json +1 -0
- package/ai/component-catalog-v2.d.ts +3 -0
- package/ai/component-catalog-v2.json +85 -7
- package/ai/component-catalog-v2.schema.json +9 -1
- package/ai/component-catalog.json +108 -11
- package/ai/public-api-signatures-v1.md +215 -25
- package/ai/skill.md +23 -17
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/analyzer/index.mjs +48 -0
- package/analyzer/loud-pairs.d.mts +49 -0
- package/analyzer/loud-pairs.mjs +357 -0
- package/dist/app-tab.d.ts +7 -5
- package/dist/app-tab.js +1 -1
- package/dist/browser/segmented-control.js +0 -1
- package/dist/catalog.js +4 -4
- package/dist/{chunk-EKYZCRCX.js → chunk-3CGQ7ZNO.js} +54 -22
- package/dist/chunk-3CGQ7ZNO.js.map +1 -0
- package/dist/{chunk-7XJO4D6A.js → chunk-3NBL3OUQ.js} +2 -2
- package/dist/{chunk-7XJO4D6A.js.map → chunk-3NBL3OUQ.js.map} +1 -1
- package/dist/{chunk-2S7OWAEV.js → chunk-44IG7PFV.js} +3 -3
- package/dist/{chunk-2S7OWAEV.js.map → chunk-44IG7PFV.js.map} +1 -1
- package/dist/{chunk-UKKSMZYT.js → chunk-46263QLK.js} +51 -1
- package/dist/chunk-46263QLK.js.map +1 -0
- package/dist/{chunk-DNCOFEUL.js → chunk-5I22LMCV.js} +2 -2
- package/dist/{chunk-DNCOFEUL.js.map → chunk-5I22LMCV.js.map} +1 -1
- package/dist/{chunk-NDUGJ4TU.js → chunk-5NDQABWK.js} +1 -1
- package/dist/{chunk-NDUGJ4TU.js.map → chunk-5NDQABWK.js.map} +1 -1
- package/dist/{chunk-KAQUUTZR.js → chunk-ATHXW6RP.js} +2 -2
- package/dist/{chunk-KAQUUTZR.js.map → chunk-ATHXW6RP.js.map} +1 -1
- package/dist/{chunk-JGFZ42ZF.js → chunk-BJJKFVWW.js} +14 -5
- package/dist/chunk-BJJKFVWW.js.map +1 -0
- package/dist/{chunk-T7C2VZUB.js → chunk-C2FAYFDE.js} +2 -2
- package/dist/{chunk-T7C2VZUB.js.map → chunk-C2FAYFDE.js.map} +1 -1
- package/dist/{chunk-SV22UIW4.js → chunk-DQA5FMOX.js} +2 -2
- package/dist/{chunk-SV22UIW4.js.map → chunk-DQA5FMOX.js.map} +1 -1
- package/dist/{chunk-LIOXPQ7N.js → chunk-GUIHMIT2.js} +12 -10
- package/dist/chunk-GUIHMIT2.js.map +1 -0
- package/dist/{chunk-3S7OUHUN.js → chunk-KPNTVB7A.js} +9 -4
- package/dist/chunk-KPNTVB7A.js.map +1 -0
- package/dist/{chunk-IR53BQUJ.js → chunk-NE5JAOQS.js} +9 -9
- package/dist/chunk-NE5JAOQS.js.map +1 -0
- package/dist/chunk-PSHUSWQH.js +18 -0
- package/dist/chunk-PSHUSWQH.js.map +1 -0
- package/dist/{chunk-OSCL5TKR.js → chunk-RNZAKSME.js} +11 -8
- package/dist/chunk-RNZAKSME.js.map +1 -0
- package/dist/{chunk-ABAXNUXU.js → chunk-T22EM4ET.js} +7 -5
- package/dist/chunk-T22EM4ET.js.map +1 -0
- package/dist/{chunk-YZV2PTY4.js → chunk-TZ5IHWB2.js} +3 -3
- package/dist/{chunk-YZV2PTY4.js.map → chunk-TZ5IHWB2.js.map} +1 -1
- package/dist/{chunk-VOCPXGQN.js → chunk-VHADGP3B.js} +9 -8
- package/dist/chunk-VHADGP3B.js.map +1 -0
- package/dist/{chunk-2CE22Q2K.js → chunk-WZ2GLEY4.js} +2 -1
- package/dist/chunk-WZ2GLEY4.js.map +1 -0
- package/dist/{chunk-M6GLI6A6.js → chunk-X5QSICJ3.js} +5 -5
- package/dist/chunk-X5QSICJ3.js.map +1 -0
- package/dist/chunk-XQV2B4IL.js +14 -0
- package/dist/chunk-XQV2B4IL.js.map +1 -0
- package/dist/{chunk-4XE2CDYB.js → chunk-ZMBU522F.js} +2 -2
- package/dist/{chunk-4XE2CDYB.js.map → chunk-ZMBU522F.js.map} +1 -1
- package/dist/collapsible-panel.d.ts +6 -2
- package/dist/collapsible-panel.js +1 -0
- package/dist/collapsible-panel.js.map +1 -1
- package/dist/css-values.d.ts +1 -1
- package/dist/css-values.js +1 -1
- package/dist/floating-toolbar.d.ts +5 -2
- package/dist/floating-toolbar.js +1 -1
- package/dist/grid.js +1 -1
- package/dist/index.js +20 -19
- package/dist/list-action-row.d.ts +2 -1
- package/dist/list-action-row.js +1 -1
- package/dist/list-header.d.ts +1 -0
- package/dist/list-header.js +1 -1
- package/dist/list-item.d.ts +5 -3
- package/dist/list-item.js +1 -1
- package/dist/list.js +1 -1
- package/dist/nav-stack.js +1 -1
- package/dist/resizable-region.d.ts +4 -1
- package/dist/resizable-region.js +1 -1
- package/dist/row.js +1 -1
- package/dist/segmented-control.d.ts +1 -1
- package/dist/segmented-control.js +1 -3
- package/dist/select.js +1 -1
- package/dist/skeleton.js +1 -1
- package/dist/spacer.js +1 -1
- package/dist/split-view.js +2 -2
- package/dist/state-banner.d.ts +1 -1
- package/dist/state-banner.js +1 -1
- package/dist/styles/badge.css +17 -6
- package/dist/styles/collapsible-panel.css +89 -11
- package/dist/styles/floating-toolbar.css +11 -6
- package/dist/styles/foundation.css +82 -26
- package/dist/styles/list-action-row.css +29 -8
- package/dist/styles/list-header.css +19 -7
- package/dist/styles/list-item.css +11 -3
- package/dist/styles/resizable-region.css +82 -14
- package/dist/styles/segmented-control.css +21 -1
- package/dist/styles/select.css +24 -13
- package/dist/styles/skeleton.css +33 -2
- package/dist/styles/state-banner.css +48 -12
- package/dist/styles/webawesome.css +47 -20
- package/dist/styles/workbench.css +473 -11
- package/dist/toolbar-text.js +1 -1
- package/dist/value-table.js +1 -1
- package/dist/wire-resizable-regions.d.ts +2 -1
- package/dist/wire-resizable-regions.js +2 -1
- package/dist/wire-tab-bars.js +1 -1
- package/dist/wire-workbench.d.ts +109 -0
- package/dist/wire-workbench.js +355 -0
- package/dist/wire-workbench.js.map +1 -0
- package/dist/workbench.d.ts +65 -6
- package/dist/workbench.js +180 -76
- package/dist/workbench.js.map +1 -1
- package/docs/accessibility.md +54 -9
- package/docs/app-layouts.md +12 -1
- package/docs/collapsible-panel.md +59 -1
- package/docs/component-contract.md +76 -4
- package/docs/component-selection.md +16 -6
- package/docs/design/templates/empty-state/with-action-dark.svg +1 -1
- package/docs/design/templates/empty-state-dark.svg +1 -1
- package/docs/design/templates/list/stack-dark.svg +1 -1
- package/docs/design/templates/list/stack.svg +1 -1
- package/docs/design/templates/list-action-row/selected.svg +1 -1
- package/docs/design/templates/list-action-row.svg +1 -1
- package/docs/design/templates/list-dark.svg +1 -1
- package/docs/design/templates/list-header/action-dark.svg +1 -1
- package/docs/design/templates/list-header/action.svg +1 -1
- package/docs/design/templates/list-header/count-dark.svg +1 -1
- package/docs/design/templates/list-header/count.svg +1 -1
- package/docs/design/templates/list-header/inline-action-dark.svg +1 -1
- package/docs/design/templates/list-header/inline-action.svg +1 -1
- package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -1
- package/docs/design/templates/list-header/toggle-expanded.svg +1 -1
- package/docs/design/templates/list-header-dark.svg +1 -1
- package/docs/design/templates/list-header.svg +1 -1
- package/docs/design/templates/list-item/selected.svg +1 -1
- package/docs/design/templates/list-item.svg +1 -1
- package/docs/design/templates/list.svg +1 -1
- package/docs/design/templates/segmented-control/equal-dark.svg +1 -1
- package/docs/design/templates/segmented-control/equal.svg +1 -1
- package/docs/design/templates/segmented-control/pill-small-dark.svg +1 -1
- package/docs/design/templates/segmented-control/pill-small.svg +1 -1
- package/docs/design/templates/segmented-control-dark.svg +1 -1
- package/docs/design/templates/segmented-control.svg +1 -1
- package/docs/design/templates/state-banner/danger-dark.svg +1 -1
- package/docs/design/templates/state-banner/danger.svg +1 -1
- package/docs/design/templates/state-banner/info-badge-dark.svg +1 -1
- package/docs/design/templates/state-banner/info-badge.svg +1 -1
- package/docs/design/templates/state-banner/info-dark.svg +1 -1
- package/docs/design/templates/state-banner/info.svg +1 -1
- package/docs/design/templates/state-banner/success-dark.svg +1 -1
- package/docs/design/templates/state-banner/success.svg +1 -1
- package/docs/design/templates/state-banner/warning-dark.svg +1 -1
- package/docs/design/templates/state-banner/warning.svg +1 -1
- package/docs/design/templates/state-banner-dark.svg +1 -1
- package/docs/design/templates/state-banner.svg +1 -1
- package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -1
- package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -1
- package/docs/design/templates/tab-bar-dark.svg +1 -1
- package/docs/design/templates/tab-bar.svg +1 -1
- package/docs/design/templates/token-search-field/tokens.svg +1 -1
- package/docs/design/templates/token-search-field.svg +1 -1
- package/docs/design/templates.md +11 -1
- package/docs/device-class.md +2 -1
- package/docs/document-baseline.md +4 -0
- package/docs/layout.md +5 -1
- package/docs/recipes.md +5 -3
- package/docs/type-contracts.md +2 -1
- package/docs/ui-analyzer.md +94 -19
- package/docs/ui-doctor.md +6 -0
- package/docs/ux-demo.md +2 -2
- package/docs/webawesome-theme.md +18 -2
- package/docs/workbench.md +302 -7
- package/llms.txt +14 -6
- package/package.json +5 -1
- package/ux-demo/recipes/collapsible-sidebar.tsx +8 -7
- package/ux-demo/recipes/loading-inspector.tsx +3 -2
- package/dist/chunk-2CE22Q2K.js.map +0 -1
- package/dist/chunk-3S7OUHUN.js.map +0 -1
- package/dist/chunk-ABAXNUXU.js.map +0 -1
- package/dist/chunk-EKYZCRCX.js.map +0 -1
- package/dist/chunk-IR53BQUJ.js.map +0 -1
- package/dist/chunk-JGFZ42ZF.js.map +0 -1
- package/dist/chunk-LIOXPQ7N.js.map +0 -1
- package/dist/chunk-M6GLI6A6.js.map +0 -1
- package/dist/chunk-OSCL5TKR.js.map +0 -1
- package/dist/chunk-UKKSMZYT.js.map +0 -1
- package/dist/chunk-VOCPXGQN.js.map +0 -1
package/dist/workbench.d.ts
CHANGED
|
@@ -2,24 +2,64 @@ import { SafeHtml } from 'kerfjs';
|
|
|
2
2
|
import { ResizableRegionSeparator, ResizableRegionCollapseMotion, ResizableRegionContentOverflow, ResizableRegionPresentation, ResizableRegionRestorePosition } from './resizable-region.js';
|
|
3
3
|
import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* The Workbench container breakpoint below which a panel presents as an
|
|
7
|
+
* overlay: `narrow` (704px or less) or `compact` (448px or less) — the same
|
|
8
|
+
* breakpoints as `ResizableRegion`'s `responsiveFillAt`.
|
|
9
|
+
*/
|
|
10
|
+
type WorkbenchResponsiveOverlayAt = 'compact' | 'narrow';
|
|
11
|
+
/** Drag-resize limits for a resizable Workbench panel, in px. */
|
|
12
|
+
interface WorkbenchPanelResizable {
|
|
13
|
+
/** Smallest size (default 180 for a rail, 120 for the drawer). */
|
|
14
|
+
min?: number;
|
|
15
|
+
/** Largest size (default 480). */
|
|
16
|
+
max?: number;
|
|
17
|
+
}
|
|
5
18
|
/** A collapsible Workbench panel — a side rail or the bottom drawer. */
|
|
6
19
|
interface WorkbenchPanel {
|
|
7
20
|
content: KerfUiContent;
|
|
8
21
|
/** Whether the panel is currently collapsed (the app owns this). */
|
|
9
22
|
collapsed?: boolean;
|
|
10
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Rail width, or drawer height, in px. Overrides the CSS default. For a
|
|
25
|
+
* resizable panel it is the current size (default 280 for a rail, 220 for
|
|
26
|
+
* the drawer), clamped to the limits; the app owns it and `wireWorkbench`
|
|
27
|
+
* reports each resize.
|
|
28
|
+
*/
|
|
11
29
|
size?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Opt in to drag and keyboard resizing on the panel's inner separator:
|
|
32
|
+
* `true` for the default limits, or `{ min, max }`. Off by default. Pair it
|
|
33
|
+
* with `wireWorkbench` from `@kerfjs/ui/wire-workbench`.
|
|
34
|
+
*/
|
|
35
|
+
resizable?: boolean | WorkbenchPanelResizable;
|
|
12
36
|
/** Accessible name for the panel region. */
|
|
13
37
|
label?: string;
|
|
14
38
|
separator?: ResizableRegionSeparator;
|
|
15
39
|
collapseMotion?: ResizableRegionCollapseMotion;
|
|
16
40
|
contentOverflow?: ResizableRegionContentOverflow;
|
|
17
41
|
presentation?: ResizableRegionPresentation;
|
|
18
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* Present the panel as an overlay, without a separator, below a Workbench
|
|
44
|
+
* container breakpoint, and inline above it — the CSS decides, so the app
|
|
45
|
+
* needs no device-class check. A rail overlays from its side at full
|
|
46
|
+
* height; the bottom drawer overlays the bottom of the work-area column.
|
|
47
|
+
*/
|
|
48
|
+
responsiveOverlayAt?: WorkbenchResponsiveOverlayAt;
|
|
49
|
+
/**
|
|
50
|
+
* Control shown while collapsed, in a safe-area-aware corner of the
|
|
51
|
+
* Workbench (not the viewport); the bottom drawer's sits in the work-area
|
|
52
|
+
* column.
|
|
53
|
+
*/
|
|
19
54
|
restoreControl?: SafeHtml;
|
|
20
55
|
restorePosition?: ResizableRegionRestorePosition;
|
|
21
56
|
}
|
|
22
57
|
interface WorkbenchProps {
|
|
58
|
+
/**
|
|
59
|
+
* The Workbench's `id`. Each panel's `id` derives from it —
|
|
60
|
+
* `<id>-left-rail`, `<id>-right-rail`, `<id>-bottom-drawer` — so a toggle
|
|
61
|
+
* can name the panel it shows and hides with `aria-controls`.
|
|
62
|
+
*/
|
|
23
63
|
id: string;
|
|
24
64
|
label: string;
|
|
25
65
|
/** The central work area. */
|
|
@@ -27,6 +67,22 @@ interface WorkbenchProps {
|
|
|
27
67
|
leftRail?: WorkbenchPanel;
|
|
28
68
|
rightRail?: WorkbenchPanel;
|
|
29
69
|
bottomDrawer?: WorkbenchPanel;
|
|
70
|
+
/**
|
|
71
|
+
* Minimum width, in px, the work area keeps beside the inline rails, fixed
|
|
72
|
+
* or `resizable` (default 320; `0` turns it off). Resizing a rail stops
|
|
73
|
+
* where the work area would drop below it, and inline rails shrink
|
|
74
|
+
* proportionally when the Workbench gets narrower. Workbenches without a
|
|
75
|
+
* rail ignore it.
|
|
76
|
+
*/
|
|
77
|
+
mainMinSize?: number;
|
|
78
|
+
/**
|
|
79
|
+
* Minimum height, in px, the work area keeps above an inline bottom drawer,
|
|
80
|
+
* fixed or `resizable` (default 120; `0` turns it off) — the drawer's
|
|
81
|
+
* counterpart of `mainMinSize`. Resizing the drawer stops where the work
|
|
82
|
+
* area would drop below it, and the drawer shrinks when the Workbench gets
|
|
83
|
+
* shorter. Workbenches without a drawer ignore it.
|
|
84
|
+
*/
|
|
85
|
+
mainMinHeight?: number;
|
|
30
86
|
className?: string;
|
|
31
87
|
/** Native named-slot assignment when composed inside a web component. */
|
|
32
88
|
slot?: string;
|
|
@@ -38,9 +94,12 @@ interface WorkbenchProps {
|
|
|
38
94
|
* a composited transform — the instant-width / sliding-content technique, so the
|
|
39
95
|
* work area relayouts once, not per frame. Bottom-drawer content stays anchored
|
|
40
96
|
* to the shell's stable bottom edge throughout that transition. The app owns
|
|
41
|
-
* each `collapsed` flag; the collapse is pure CSS
|
|
42
|
-
* `
|
|
97
|
+
* each `collapsed` flag; the collapse is pure CSS. A collapsed panel renders
|
|
98
|
+
* `inert` and `aria-hidden`, so neither Tab nor assistive technology reaches
|
|
99
|
+
* controls that have slid out of view or an empty landmark (its restore control
|
|
100
|
+
* lives outside it and stays reachable). A panel may opt in to drag and keyboard resizing with
|
|
101
|
+
* `resizable`, which `wireWorkbench` drives. See `docs/23-app-layouts.md` §3.3.
|
|
43
102
|
*/
|
|
44
|
-
declare function Workbench({ id, label, main, leftRail, rightRail, bottomDrawer, className, slot, }: WorkbenchProps): SafeHtml;
|
|
103
|
+
declare function Workbench({ id, label, main, leftRail, rightRail, bottomDrawer, mainMinSize, mainMinHeight, className, slot, }: WorkbenchProps): SafeHtml;
|
|
45
104
|
|
|
46
|
-
export { Workbench, type WorkbenchPanel, type WorkbenchProps };
|
|
105
|
+
export { Workbench, type WorkbenchPanel, type WorkbenchPanelResizable, type WorkbenchProps, type WorkbenchResponsiveOverlayAt };
|
package/dist/workbench.js
CHANGED
|
@@ -1,6 +1,167 @@
|
|
|
1
|
+
import { WORKBENCH_MAIN_MIN_SIZE, WORKBENCH_MAIN_MIN_HEIGHT, workbenchRegionId, WORKBENCH_RESIZE_DEFAULTS } from './chunk-PSHUSWQH.js';
|
|
2
|
+
import { clampRegionSize, ResizeGrip } from './chunk-VHADGP3B.js';
|
|
1
3
|
import { jsxs, jsx } from 'kerfjs/jsx-runtime';
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
var EDGES = {
|
|
6
|
+
leftRail: "end",
|
|
7
|
+
rightRail: "start",
|
|
8
|
+
bottomDrawer: "start"
|
|
9
|
+
};
|
|
10
|
+
var DEFAULT_LABELS = {
|
|
11
|
+
leftRail: "left rail",
|
|
12
|
+
rightRail: "right rail",
|
|
13
|
+
bottomDrawer: "bottom drawer"
|
|
14
|
+
};
|
|
15
|
+
function resolveResize(id, key, panel) {
|
|
16
|
+
if (!panel.resizable) return void 0;
|
|
17
|
+
const defaults = WORKBENCH_RESIZE_DEFAULTS[key];
|
|
18
|
+
const limits = panel.resizable === true ? {} : panel.resizable;
|
|
19
|
+
const min = limits.min ?? defaults.min;
|
|
20
|
+
const max = Math.max(min, limits.max ?? defaults.max);
|
|
21
|
+
return {
|
|
22
|
+
regionId: workbenchRegionId(id, key),
|
|
23
|
+
axis: key === "bottomDrawer" ? "vertical" : "horizontal",
|
|
24
|
+
edge: EDGES[key],
|
|
25
|
+
size: clampRegionSize(panel.size ?? defaults.size, min, max),
|
|
26
|
+
min,
|
|
27
|
+
max
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
var resizeAttributes = (resize) => resize ? {
|
|
31
|
+
"data-resizable": "true",
|
|
32
|
+
"data-region-id": resize.regionId,
|
|
33
|
+
"data-axis": resize.axis,
|
|
34
|
+
"data-edge": resize.edge
|
|
35
|
+
} : {};
|
|
36
|
+
function panelStyle(panel, resize, sizeProperty) {
|
|
37
|
+
if (resize) return `--kui-resizable-region-size:${resize.size}px`;
|
|
38
|
+
return panel.size ? `${sizeProperty}: ${panel.size}px` : void 0;
|
|
39
|
+
}
|
|
40
|
+
function PanelHandle({
|
|
41
|
+
panel,
|
|
42
|
+
resize,
|
|
43
|
+
label
|
|
44
|
+
}) {
|
|
45
|
+
const inert = Boolean(panel.collapsed) || (panel.presentation ?? "inline") !== "inline";
|
|
46
|
+
return /* @__PURE__ */ jsx(
|
|
47
|
+
"div",
|
|
48
|
+
{
|
|
49
|
+
class: "kui-workbench__handle",
|
|
50
|
+
role: "separator",
|
|
51
|
+
tabindex: inert ? "-1" : "0",
|
|
52
|
+
"aria-label": `Resize ${label}`,
|
|
53
|
+
"aria-orientation": resize.axis === "horizontal" ? "vertical" : "horizontal",
|
|
54
|
+
"aria-valuemin": resize.min,
|
|
55
|
+
"aria-valuemax": resize.max,
|
|
56
|
+
"aria-valuenow": resize.size,
|
|
57
|
+
"aria-hidden": inert ? "true" : void 0,
|
|
58
|
+
"data-kui-resize-handle": true,
|
|
59
|
+
"data-region-id": resize.regionId,
|
|
60
|
+
children: /* @__PURE__ */ jsx("span", { class: "kui-workbench__handle-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(ResizeGrip, { axis: resize.axis }) })
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
function hiddenFromAccessibility(panel) {
|
|
65
|
+
return panel.collapsed || panel.presentation === "hidden" ? "true" : void 0;
|
|
66
|
+
}
|
|
67
|
+
function Rail({
|
|
68
|
+
id,
|
|
69
|
+
side,
|
|
70
|
+
panel
|
|
71
|
+
}) {
|
|
72
|
+
const key = side === "left" ? "leftRail" : "rightRail";
|
|
73
|
+
const resize = resolveResize(id, key, panel);
|
|
74
|
+
return /* @__PURE__ */ jsxs(
|
|
75
|
+
"aside",
|
|
76
|
+
{
|
|
77
|
+
id: workbenchRegionId(id, key),
|
|
78
|
+
class: `kui-workbench__rail kui-workbench__rail--${side}`,
|
|
79
|
+
"data-workbench-rail": side,
|
|
80
|
+
"data-collapsed": String(panel.collapsed ?? false),
|
|
81
|
+
"data-separator": panel.separator ?? "auto",
|
|
82
|
+
"data-collapse-motion": panel.collapseMotion ?? "slide",
|
|
83
|
+
"data-content-overflow": panel.contentOverflow ?? "clip",
|
|
84
|
+
"data-presentation": panel.presentation ?? "inline",
|
|
85
|
+
"data-responsive-overlay-at": panel.responsiveOverlayAt,
|
|
86
|
+
...resizeAttributes(resize),
|
|
87
|
+
"aria-label": panel.label || void 0,
|
|
88
|
+
"aria-hidden": hiddenFromAccessibility(panel),
|
|
89
|
+
inert: Boolean(panel.collapsed),
|
|
90
|
+
style: panelStyle(panel, resize, "--kui-workbench-rail-width"),
|
|
91
|
+
children: [
|
|
92
|
+
/* @__PURE__ */ jsx(
|
|
93
|
+
"div",
|
|
94
|
+
{
|
|
95
|
+
class: "kui-workbench__panel-content",
|
|
96
|
+
inert: Boolean(panel.collapsed),
|
|
97
|
+
children: panel.content
|
|
98
|
+
}
|
|
99
|
+
),
|
|
100
|
+
resize && /* @__PURE__ */ jsx(
|
|
101
|
+
PanelHandle,
|
|
102
|
+
{
|
|
103
|
+
panel,
|
|
104
|
+
resize,
|
|
105
|
+
label: panel.label || DEFAULT_LABELS[key]
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
function Drawer({ id, panel }) {
|
|
113
|
+
const resize = resolveResize(id, "bottomDrawer", panel);
|
|
114
|
+
return /* @__PURE__ */ jsxs(
|
|
115
|
+
"section",
|
|
116
|
+
{
|
|
117
|
+
id: workbenchRegionId(id, "bottomDrawer"),
|
|
118
|
+
class: "kui-workbench__drawer",
|
|
119
|
+
"data-workbench-drawer": true,
|
|
120
|
+
"data-collapsed": String(panel.collapsed ?? false),
|
|
121
|
+
"data-separator": panel.separator ?? "auto",
|
|
122
|
+
"data-collapse-motion": panel.collapseMotion ?? "slide",
|
|
123
|
+
"data-content-overflow": panel.contentOverflow ?? "clip",
|
|
124
|
+
"data-presentation": panel.presentation ?? "inline",
|
|
125
|
+
"data-responsive-overlay-at": panel.responsiveOverlayAt,
|
|
126
|
+
...resizeAttributes(resize),
|
|
127
|
+
"aria-label": panel.label || void 0,
|
|
128
|
+
"aria-hidden": hiddenFromAccessibility(panel),
|
|
129
|
+
inert: Boolean(panel.collapsed),
|
|
130
|
+
style: panelStyle(panel, resize, "--kui-workbench-drawer-height"),
|
|
131
|
+
children: [
|
|
132
|
+
/* @__PURE__ */ jsx(
|
|
133
|
+
"div",
|
|
134
|
+
{
|
|
135
|
+
class: "kui-workbench__panel-content",
|
|
136
|
+
inert: Boolean(panel.collapsed),
|
|
137
|
+
children: panel.content
|
|
138
|
+
}
|
|
139
|
+
),
|
|
140
|
+
resize && /* @__PURE__ */ jsx(
|
|
141
|
+
PanelHandle,
|
|
142
|
+
{
|
|
143
|
+
panel,
|
|
144
|
+
resize,
|
|
145
|
+
label: panel.label || DEFAULT_LABELS.bottomDrawer
|
|
146
|
+
}
|
|
147
|
+
)
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
function restore(panel, side, position) {
|
|
153
|
+
if (!panel?.collapsed || !panel.restoreControl || panel.presentation === "hidden")
|
|
154
|
+
return "";
|
|
155
|
+
return /* @__PURE__ */ jsx(
|
|
156
|
+
"div",
|
|
157
|
+
{
|
|
158
|
+
class: "kui-workbench__restore",
|
|
159
|
+
"data-panel": side,
|
|
160
|
+
"data-position": panel.restorePosition ?? position,
|
|
161
|
+
children: panel.restoreControl
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
}
|
|
4
165
|
function Workbench({
|
|
5
166
|
id,
|
|
6
167
|
label,
|
|
@@ -8,96 +169,39 @@ function Workbench({
|
|
|
8
169
|
leftRail,
|
|
9
170
|
rightRail,
|
|
10
171
|
bottomDrawer,
|
|
172
|
+
mainMinSize = WORKBENCH_MAIN_MIN_SIZE,
|
|
173
|
+
mainMinHeight = WORKBENCH_MAIN_MIN_HEIGHT,
|
|
11
174
|
className = "",
|
|
12
175
|
slot
|
|
13
176
|
}) {
|
|
177
|
+
const px = (value) => Math.max(0, Math.round(value));
|
|
178
|
+
const mainMin = leftRail || rightRail ? px(mainMinSize) : void 0;
|
|
179
|
+
const mainMinBlock = bottomDrawer ? px(mainMinHeight) : void 0;
|
|
180
|
+
const style = [
|
|
181
|
+
mainMin === void 0 ? "" : `--_kui-workbench-main-min-width:${mainMin}px`,
|
|
182
|
+
mainMinBlock === void 0 ? "" : `--_kui-workbench-main-min-height:${mainMinBlock}px`
|
|
183
|
+
].filter(Boolean).join(";");
|
|
14
184
|
return /* @__PURE__ */ jsxs(
|
|
15
185
|
"section",
|
|
16
186
|
{
|
|
17
187
|
class: `kui-workbench ${className}`.trim(),
|
|
18
188
|
id,
|
|
19
189
|
"data-component": "workbench",
|
|
190
|
+
"data-main-min-size": mainMin === void 0 ? void 0 : String(mainMin),
|
|
191
|
+
"data-main-min-height": mainMinBlock === void 0 ? void 0 : String(mainMinBlock),
|
|
192
|
+
style: style || void 0,
|
|
20
193
|
"aria-label": label,
|
|
21
194
|
slot,
|
|
22
195
|
children: [
|
|
23
|
-
leftRail && /* @__PURE__ */ jsx(
|
|
24
|
-
|
|
25
|
-
{
|
|
26
|
-
class: "kui-workbench__rail kui-workbench__rail--left",
|
|
27
|
-
"data-workbench-rail": "left",
|
|
28
|
-
"data-collapsed": String(leftRail.collapsed ?? false),
|
|
29
|
-
"data-separator": leftRail.separator ?? "auto",
|
|
30
|
-
"data-collapse-motion": leftRail.collapseMotion ?? "slide",
|
|
31
|
-
"data-content-overflow": leftRail.contentOverflow ?? "clip",
|
|
32
|
-
"data-presentation": leftRail.presentation ?? "inline",
|
|
33
|
-
"aria-label": leftRail.label || void 0,
|
|
34
|
-
"aria-hidden": leftRail.presentation === "hidden" ? "true" : void 0,
|
|
35
|
-
style: leftRail.size ? `--kui-workbench-rail-width: ${leftRail.size}px` : void 0,
|
|
36
|
-
children: /* @__PURE__ */ jsx("div", { class: "kui-workbench__panel-content", children: leftRail.content })
|
|
37
|
-
}
|
|
38
|
-
),
|
|
39
|
-
leftRail?.collapsed && leftRail.restoreControl && leftRail.presentation !== "hidden" && /* @__PURE__ */ jsx(
|
|
40
|
-
"div",
|
|
41
|
-
{
|
|
42
|
-
class: "kui-workbench__restore",
|
|
43
|
-
"data-panel": "left",
|
|
44
|
-
"data-position": leftRail.restorePosition ?? "bottom-start",
|
|
45
|
-
children: leftRail.restoreControl
|
|
46
|
-
}
|
|
47
|
-
),
|
|
196
|
+
leftRail && /* @__PURE__ */ jsx(Rail, { id, side: "left", panel: leftRail }),
|
|
197
|
+
restore(leftRail, "left", "bottom-start"),
|
|
48
198
|
/* @__PURE__ */ jsxs("div", { class: "kui-workbench__center", children: [
|
|
49
199
|
/* @__PURE__ */ jsx("div", { class: "kui-workbench__main", "data-workbench-main": true, children: main }),
|
|
50
|
-
bottomDrawer && /* @__PURE__ */ jsx(
|
|
51
|
-
|
|
52
|
-
{
|
|
53
|
-
class: "kui-workbench__drawer",
|
|
54
|
-
"data-workbench-drawer": true,
|
|
55
|
-
"data-collapsed": String(bottomDrawer.collapsed ?? false),
|
|
56
|
-
"data-separator": bottomDrawer.separator ?? "auto",
|
|
57
|
-
"data-collapse-motion": bottomDrawer.collapseMotion ?? "slide",
|
|
58
|
-
"data-content-overflow": bottomDrawer.contentOverflow ?? "clip",
|
|
59
|
-
"data-presentation": bottomDrawer.presentation ?? "inline",
|
|
60
|
-
"aria-label": bottomDrawer.label || void 0,
|
|
61
|
-
"aria-hidden": bottomDrawer.presentation === "hidden" ? "true" : void 0,
|
|
62
|
-
style: bottomDrawer.size ? `--kui-workbench-drawer-height: ${bottomDrawer.size}px` : void 0,
|
|
63
|
-
children: /* @__PURE__ */ jsx("div", { class: "kui-workbench__panel-content", children: bottomDrawer.content })
|
|
64
|
-
}
|
|
65
|
-
),
|
|
66
|
-
bottomDrawer?.collapsed && bottomDrawer.restoreControl && bottomDrawer.presentation !== "hidden" && /* @__PURE__ */ jsx(
|
|
67
|
-
"div",
|
|
68
|
-
{
|
|
69
|
-
class: "kui-workbench__restore",
|
|
70
|
-
"data-panel": "bottom",
|
|
71
|
-
"data-position": bottomDrawer.restorePosition ?? "bottom-end",
|
|
72
|
-
children: bottomDrawer.restoreControl
|
|
73
|
-
}
|
|
74
|
-
)
|
|
200
|
+
bottomDrawer && /* @__PURE__ */ jsx(Drawer, { id, panel: bottomDrawer }),
|
|
201
|
+
restore(bottomDrawer, "bottom", "bottom-end")
|
|
75
202
|
] }),
|
|
76
|
-
rightRail && /* @__PURE__ */ jsx(
|
|
77
|
-
|
|
78
|
-
{
|
|
79
|
-
class: "kui-workbench__rail kui-workbench__rail--right",
|
|
80
|
-
"data-workbench-rail": "right",
|
|
81
|
-
"data-collapsed": String(rightRail.collapsed ?? false),
|
|
82
|
-
"data-separator": rightRail.separator ?? "auto",
|
|
83
|
-
"data-collapse-motion": rightRail.collapseMotion ?? "slide",
|
|
84
|
-
"data-content-overflow": rightRail.contentOverflow ?? "clip",
|
|
85
|
-
"data-presentation": rightRail.presentation ?? "inline",
|
|
86
|
-
"aria-label": rightRail.label || void 0,
|
|
87
|
-
"aria-hidden": rightRail.presentation === "hidden" ? "true" : void 0,
|
|
88
|
-
style: rightRail.size ? `--kui-workbench-rail-width: ${rightRail.size}px` : void 0,
|
|
89
|
-
children: /* @__PURE__ */ jsx("div", { class: "kui-workbench__panel-content", children: rightRail.content })
|
|
90
|
-
}
|
|
91
|
-
),
|
|
92
|
-
rightRail?.collapsed && rightRail.restoreControl && rightRail.presentation !== "hidden" && /* @__PURE__ */ jsx(
|
|
93
|
-
"div",
|
|
94
|
-
{
|
|
95
|
-
class: "kui-workbench__restore",
|
|
96
|
-
"data-panel": "right",
|
|
97
|
-
"data-position": rightRail.restorePosition ?? "bottom-end",
|
|
98
|
-
children: rightRail.restoreControl
|
|
99
|
-
}
|
|
100
|
-
)
|
|
203
|
+
rightRail && /* @__PURE__ */ jsx(Rail, { id, side: "right", panel: rightRail }),
|
|
204
|
+
restore(rightRail, "right", "bottom-end")
|
|
101
205
|
]
|
|
102
206
|
}
|
|
103
207
|
);
|
package/dist/workbench.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/workbench.tsx"],"names":[],"mappings":"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","file":"workbench.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport type {\n ResizableRegionCollapseMotion,\n ResizableRegionContentOverflow,\n ResizableRegionPresentation,\n ResizableRegionRestorePosition,\n ResizableRegionSeparator,\n} from './resizable-region.js';\nimport type { KerfUiContent } from './semantic-content.js';\n\n/** A collapsible Workbench panel — a side rail or the bottom drawer. */\nexport interface WorkbenchPanel {\n content: KerfUiContent;\n /** Whether the panel is currently collapsed (the app owns this). */\n collapsed?: boolean;\n /** Rail width, or drawer height, in px. Overrides the CSS default. */\n size?: number;\n /** Accessible name for the panel region. */\n label?: string;\n separator?: ResizableRegionSeparator;\n collapseMotion?: ResizableRegionCollapseMotion;\n contentOverflow?: ResizableRegionContentOverflow;\n presentation?: ResizableRegionPresentation;\n /** Control shown in a safe-area-aware viewport corner while collapsed. */\n restoreControl?: SafeHtml;\n restorePosition?: ResizableRegionRestorePosition;\n}\n\nexport interface WorkbenchProps {\n id: string;\n label: string;\n /** The central work area. */\n main: KerfUiContent;\n leftRail?: WorkbenchPanel;\n rightRail?: WorkbenchPanel;\n bottomDrawer?: WorkbenchPanel;\n className?: string;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\n/**\n * The Xcode-like multi-panel workspace: a collapsible left rail, right rail, and\n * bottom drawer around a central work area (any absent). Collapsing snaps the\n * panel's track to zero in one reflow while its fixed-size content slides out via\n * a composited transform — the instant-width / sliding-content technique, so the\n * work area relayouts once, not per frame. Bottom-drawer content stays anchored\n * to the shell's stable bottom edge throughout that transition. The app owns\n * each `collapsed` flag; the collapse is pure CSS (no wire). See\n * `docs/23-app-layouts.md` §3.3.\n */\nexport function Workbench({\n id,\n label,\n main,\n leftRail,\n rightRail,\n bottomDrawer,\n className = '',\n slot,\n}: WorkbenchProps) {\n return (\n <section\n class={`kui-workbench ${className}`.trim()}\n id={id}\n data-component=\"workbench\"\n aria-label={label}\n slot={slot}\n >\n {leftRail && (\n <aside\n class=\"kui-workbench__rail kui-workbench__rail--left\"\n data-workbench-rail=\"left\"\n data-collapsed={String(leftRail.collapsed ?? false)}\n data-separator={leftRail.separator ?? 'auto'}\n data-collapse-motion={leftRail.collapseMotion ?? 'slide'}\n data-content-overflow={leftRail.contentOverflow ?? 'clip'}\n data-presentation={leftRail.presentation ?? 'inline'}\n aria-label={leftRail.label || undefined}\n aria-hidden={leftRail.presentation === 'hidden' ? 'true' : undefined}\n style={\n leftRail.size\n ? `--kui-workbench-rail-width: ${leftRail.size}px`\n : undefined\n }\n >\n <div class=\"kui-workbench__panel-content\">{leftRail.content}</div>\n </aside>\n )}\n {leftRail?.collapsed &&\n leftRail.restoreControl &&\n leftRail.presentation !== 'hidden' && (\n <div\n class=\"kui-workbench__restore\"\n data-panel=\"left\"\n data-position={leftRail.restorePosition ?? 'bottom-start'}\n >\n {leftRail.restoreControl}\n </div>\n )}\n <div class=\"kui-workbench__center\">\n <div class=\"kui-workbench__main\" data-workbench-main>\n {main}\n </div>\n {bottomDrawer && (\n <section\n class=\"kui-workbench__drawer\"\n data-workbench-drawer\n data-collapsed={String(bottomDrawer.collapsed ?? false)}\n data-separator={bottomDrawer.separator ?? 'auto'}\n data-collapse-motion={bottomDrawer.collapseMotion ?? 'slide'}\n data-content-overflow={bottomDrawer.contentOverflow ?? 'clip'}\n data-presentation={bottomDrawer.presentation ?? 'inline'}\n aria-label={bottomDrawer.label || undefined}\n aria-hidden={\n bottomDrawer.presentation === 'hidden' ? 'true' : undefined\n }\n style={\n bottomDrawer.size\n ? `--kui-workbench-drawer-height: ${bottomDrawer.size}px`\n : undefined\n }\n >\n <div class=\"kui-workbench__panel-content\">\n {bottomDrawer.content}\n </div>\n </section>\n )}\n {bottomDrawer?.collapsed &&\n bottomDrawer.restoreControl &&\n bottomDrawer.presentation !== 'hidden' && (\n <div\n class=\"kui-workbench__restore\"\n data-panel=\"bottom\"\n data-position={bottomDrawer.restorePosition ?? 'bottom-end'}\n >\n {bottomDrawer.restoreControl}\n </div>\n )}\n </div>\n {rightRail && (\n <aside\n class=\"kui-workbench__rail kui-workbench__rail--right\"\n data-workbench-rail=\"right\"\n data-collapsed={String(rightRail.collapsed ?? false)}\n data-separator={rightRail.separator ?? 'auto'}\n data-collapse-motion={rightRail.collapseMotion ?? 'slide'}\n data-content-overflow={rightRail.contentOverflow ?? 'clip'}\n data-presentation={rightRail.presentation ?? 'inline'}\n aria-label={rightRail.label || undefined}\n aria-hidden={rightRail.presentation === 'hidden' ? 'true' : undefined}\n style={\n rightRail.size\n ? `--kui-workbench-rail-width: ${rightRail.size}px`\n : undefined\n }\n >\n <div class=\"kui-workbench__panel-content\">{rightRail.content}</div>\n </aside>\n )}\n {rightRail?.collapsed &&\n rightRail.restoreControl &&\n rightRail.presentation !== 'hidden' && (\n <div\n class=\"kui-workbench__restore\"\n data-panel=\"right\"\n data-position={rightRail.restorePosition ?? 'bottom-end'}\n >\n {rightRail.restoreControl}\n </div>\n )}\n </section>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/workbench.tsx"],"names":[],"mappings":"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","file":"workbench.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport {\n clampRegionSize,\n type ResizableRegionAxis,\n type ResizableRegionCollapseMotion,\n type ResizableRegionContentOverflow,\n type ResizableRegionEdge,\n type ResizableRegionPresentation,\n type ResizableRegionRestorePosition,\n type ResizableRegionSeparator,\n} from './resizable-region.js';\nimport { ResizeGrip } from './resize-grip.js';\nimport type { KerfUiContent } from './semantic-content.js';\nimport {\n WORKBENCH_MAIN_MIN_HEIGHT,\n WORKBENCH_MAIN_MIN_SIZE,\n WORKBENCH_RESIZE_DEFAULTS,\n type WorkbenchPanelKey,\n workbenchRegionId,\n} from './workbench-resize.js';\n\n/**\n * The Workbench container breakpoint below which a panel presents as an\n * overlay: `narrow` (704px or less) or `compact` (448px or less) — the same\n * breakpoints as `ResizableRegion`'s `responsiveFillAt`.\n */\nexport type WorkbenchResponsiveOverlayAt = 'compact' | 'narrow';\n\n/** Drag-resize limits for a resizable Workbench panel, in px. */\nexport interface WorkbenchPanelResizable {\n /** Smallest size (default 180 for a rail, 120 for the drawer). */\n min?: number;\n /** Largest size (default 480). */\n max?: number;\n}\n\n/** A collapsible Workbench panel — a side rail or the bottom drawer. */\nexport interface WorkbenchPanel {\n content: KerfUiContent;\n /** Whether the panel is currently collapsed (the app owns this). */\n collapsed?: boolean;\n /**\n * Rail width, or drawer height, in px. Overrides the CSS default. For a\n * resizable panel it is the current size (default 280 for a rail, 220 for\n * the drawer), clamped to the limits; the app owns it and `wireWorkbench`\n * reports each resize.\n */\n size?: number;\n /**\n * Opt in to drag and keyboard resizing on the panel's inner separator:\n * `true` for the default limits, or `{ min, max }`. Off by default. Pair it\n * with `wireWorkbench` from `@kerfjs/ui/wire-workbench`.\n */\n resizable?: boolean | WorkbenchPanelResizable;\n /** Accessible name for the panel region. */\n label?: string;\n separator?: ResizableRegionSeparator;\n collapseMotion?: ResizableRegionCollapseMotion;\n contentOverflow?: ResizableRegionContentOverflow;\n presentation?: ResizableRegionPresentation;\n /**\n * Present the panel as an overlay, without a separator, below a Workbench\n * container breakpoint, and inline above it — the CSS decides, so the app\n * needs no device-class check. A rail overlays from its side at full\n * height; the bottom drawer overlays the bottom of the work-area column.\n */\n responsiveOverlayAt?: WorkbenchResponsiveOverlayAt;\n /**\n * Control shown while collapsed, in a safe-area-aware corner of the\n * Workbench (not the viewport); the bottom drawer's sits in the work-area\n * column.\n */\n restoreControl?: SafeHtml;\n restorePosition?: ResizableRegionRestorePosition;\n}\n\nexport interface WorkbenchProps {\n /**\n * The Workbench's `id`. Each panel's `id` derives from it —\n * `<id>-left-rail`, `<id>-right-rail`, `<id>-bottom-drawer` — so a toggle\n * can name the panel it shows and hides with `aria-controls`.\n */\n id: string;\n label: string;\n /** The central work area. */\n main: KerfUiContent;\n leftRail?: WorkbenchPanel;\n rightRail?: WorkbenchPanel;\n bottomDrawer?: WorkbenchPanel;\n /**\n * Minimum width, in px, the work area keeps beside the inline rails, fixed\n * or `resizable` (default 320; `0` turns it off). Resizing a rail stops\n * where the work area would drop below it, and inline rails shrink\n * proportionally when the Workbench gets narrower. Workbenches without a\n * rail ignore it.\n */\n mainMinSize?: number;\n /**\n * Minimum height, in px, the work area keeps above an inline bottom drawer,\n * fixed or `resizable` (default 120; `0` turns it off) — the drawer's\n * counterpart of `mainMinSize`. Resizing the drawer stops where the work\n * area would drop below it, and the drawer shrinks when the Workbench gets\n * shorter. Workbenches without a drawer ignore it.\n */\n mainMinHeight?: number;\n className?: string;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\ninterface ResolvedResize {\n regionId: string;\n axis: ResizableRegionAxis;\n edge: ResizableRegionEdge;\n size: number;\n min: number;\n max: number;\n}\n\n/**\n * Each panel's separator is its inner edge. Growing the left rail moves that\n * edge toward the inline end; growing the right rail or the drawer moves it\n * toward the start — the same edge vocabulary `ResizableRegion` uses.\n */\nconst EDGES: Record<WorkbenchPanelKey, ResizableRegionEdge> = {\n leftRail: 'end',\n rightRail: 'start',\n bottomDrawer: 'start',\n};\n\nconst DEFAULT_LABELS: Record<WorkbenchPanelKey, string> = {\n leftRail: 'left rail',\n rightRail: 'right rail',\n bottomDrawer: 'bottom drawer',\n};\n\nfunction resolveResize(\n id: string,\n key: WorkbenchPanelKey,\n panel: WorkbenchPanel,\n): ResolvedResize | undefined {\n if (!panel.resizable) return undefined;\n const defaults = WORKBENCH_RESIZE_DEFAULTS[key];\n const limits = panel.resizable === true ? {} : panel.resizable;\n const min = limits.min ?? defaults.min;\n const max = Math.max(min, limits.max ?? defaults.max);\n return {\n regionId: workbenchRegionId(id, key),\n axis: key === 'bottomDrawer' ? 'vertical' : 'horizontal',\n edge: EDGES[key],\n size: clampRegionSize(panel.size ?? defaults.size, min, max),\n min,\n max,\n };\n}\n\n/** The attributes a resizable panel adds for the resize wiring to read. */\nconst resizeAttributes = (resize: ResolvedResize | undefined) =>\n resize\n ? {\n 'data-resizable': 'true',\n 'data-region-id': resize.regionId,\n 'data-axis': resize.axis,\n 'data-edge': resize.edge,\n }\n : {};\n\n/**\n * A resizable panel's size lives in the resize wiring's track property, so a\n * drag or key press resizes the panel before the app re-renders; the CSS maps\n * it onto the rail width or drawer height.\n */\nfunction panelStyle(\n panel: WorkbenchPanel,\n resize: ResolvedResize | undefined,\n sizeProperty: string,\n) {\n if (resize) return `--kui-resizable-region-size:${resize.size}px`;\n return panel.size ? `${sizeProperty}: ${panel.size}px` : undefined;\n}\n\n/**\n * The separator a resizable panel owns: the same focusable `role=\"separator\"`\n * contract a `ResizableRegion` handle carries, so one resize wiring drives\n * both. It leaves the tab order while the panel is collapsed or not inline.\n */\nfunction PanelHandle({\n panel,\n resize,\n label,\n}: {\n panel: WorkbenchPanel;\n resize: ResolvedResize;\n label: string;\n}) {\n const inert =\n Boolean(panel.collapsed) || (panel.presentation ?? 'inline') !== 'inline';\n return (\n <div\n class=\"kui-workbench__handle\"\n role=\"separator\"\n tabindex={inert ? '-1' : '0'}\n aria-label={`Resize ${label}`}\n aria-orientation={\n resize.axis === 'horizontal' ? 'vertical' : 'horizontal'\n }\n aria-valuemin={resize.min}\n aria-valuemax={resize.max}\n aria-valuenow={resize.size}\n aria-hidden={inert ? 'true' : undefined}\n data-kui-resize-handle\n data-region-id={resize.regionId}\n >\n <span class=\"kui-workbench__handle-icon\" aria-hidden=\"true\">\n <ResizeGrip axis={resize.axis} />\n </span>\n </div>\n );\n}\n\n/**\n * A collapsed panel leaves the accessibility tree as a whole, like a collapsed\n * `CollapsiblePanel`: its content and separator are already inert or hidden,\n * so the labeled landmark would otherwise stay behind, empty, for a screen\n * reader to land on. Its restore control renders outside it and stays\n * reachable.\n */\nfunction hiddenFromAccessibility(panel: WorkbenchPanel) {\n return panel.collapsed || panel.presentation === 'hidden'\n ? ('true' as const)\n : undefined;\n}\n\nfunction Rail({\n id,\n side,\n panel,\n}: {\n id: string;\n side: 'left' | 'right';\n panel: WorkbenchPanel;\n}) {\n const key: WorkbenchPanelKey = side === 'left' ? 'leftRail' : 'rightRail';\n const resize = resolveResize(id, key, panel);\n return (\n <aside\n id={workbenchRegionId(id, key)}\n class={`kui-workbench__rail kui-workbench__rail--${side}`}\n data-workbench-rail={side}\n data-collapsed={String(panel.collapsed ?? false)}\n data-separator={panel.separator ?? 'auto'}\n data-collapse-motion={panel.collapseMotion ?? 'slide'}\n data-content-overflow={panel.contentOverflow ?? 'clip'}\n data-presentation={panel.presentation ?? 'inline'}\n data-responsive-overlay-at={panel.responsiveOverlayAt}\n {...resizeAttributes(resize)}\n aria-label={panel.label || undefined}\n aria-hidden={hiddenFromAccessibility(panel)}\n inert={Boolean(panel.collapsed)}\n style={panelStyle(panel, resize, '--kui-workbench-rail-width')}\n >\n <div\n class=\"kui-workbench__panel-content\"\n inert={Boolean(panel.collapsed)}\n >\n {panel.content}\n </div>\n {resize && (\n <PanelHandle\n panel={panel}\n resize={resize}\n label={panel.label || DEFAULT_LABELS[key]}\n />\n )}\n </aside>\n );\n}\n\nfunction Drawer({ id, panel }: { id: string; panel: WorkbenchPanel }) {\n const resize = resolveResize(id, 'bottomDrawer', panel);\n return (\n <section\n id={workbenchRegionId(id, 'bottomDrawer')}\n class=\"kui-workbench__drawer\"\n data-workbench-drawer\n data-collapsed={String(panel.collapsed ?? false)}\n data-separator={panel.separator ?? 'auto'}\n data-collapse-motion={panel.collapseMotion ?? 'slide'}\n data-content-overflow={panel.contentOverflow ?? 'clip'}\n data-presentation={panel.presentation ?? 'inline'}\n data-responsive-overlay-at={panel.responsiveOverlayAt}\n {...resizeAttributes(resize)}\n aria-label={panel.label || undefined}\n aria-hidden={hiddenFromAccessibility(panel)}\n inert={Boolean(panel.collapsed)}\n style={panelStyle(panel, resize, '--kui-workbench-drawer-height')}\n >\n <div\n class=\"kui-workbench__panel-content\"\n inert={Boolean(panel.collapsed)}\n >\n {panel.content}\n </div>\n {resize && (\n <PanelHandle\n panel={panel}\n resize={resize}\n label={panel.label || DEFAULT_LABELS.bottomDrawer}\n />\n )}\n </section>\n );\n}\n\n/** A collapsed panel's restore control, outside the clipped panel. */\nfunction restore(\n panel: WorkbenchPanel | undefined,\n side: 'left' | 'right' | 'bottom',\n position: ResizableRegionRestorePosition,\n) {\n if (\n !panel?.collapsed ||\n !panel.restoreControl ||\n panel.presentation === 'hidden'\n )\n return '';\n return (\n <div\n class=\"kui-workbench__restore\"\n data-panel={side}\n data-position={panel.restorePosition ?? position}\n >\n {panel.restoreControl}\n </div>\n );\n}\n\n/**\n * The Xcode-like multi-panel workspace: a collapsible left rail, right rail, and\n * bottom drawer around a central work area (any absent). Collapsing snaps the\n * panel's track to zero in one reflow while its fixed-size content slides out via\n * a composited transform — the instant-width / sliding-content technique, so the\n * work area relayouts once, not per frame. Bottom-drawer content stays anchored\n * to the shell's stable bottom edge throughout that transition. The app owns\n * each `collapsed` flag; the collapse is pure CSS. A collapsed panel renders\n * `inert` and `aria-hidden`, so neither Tab nor assistive technology reaches\n * controls that have slid out of view or an empty landmark (its restore control\n * lives outside it and stays reachable). A panel may opt in to drag and keyboard resizing with\n * `resizable`, which `wireWorkbench` drives. See `docs/23-app-layouts.md` §3.3.\n */\nexport function Workbench({\n id,\n label,\n main,\n leftRail,\n rightRail,\n bottomDrawer,\n mainMinSize = WORKBENCH_MAIN_MIN_SIZE,\n mainMinHeight = WORKBENCH_MAIN_MIN_HEIGHT,\n className = '',\n slot,\n}: WorkbenchProps) {\n // Each work-area minimum applies only on an axis a panel shares with it:\n // the width beside a rail, the height above the drawer.\n const px = (value: number) => Math.max(0, Math.round(value));\n const mainMin = leftRail || rightRail ? px(mainMinSize) : undefined;\n const mainMinBlock = bottomDrawer ? px(mainMinHeight) : undefined;\n const style = [\n mainMin === undefined ? '' : `--_kui-workbench-main-min-width:${mainMin}px`,\n mainMinBlock === undefined\n ? ''\n : `--_kui-workbench-main-min-height:${mainMinBlock}px`,\n ]\n .filter(Boolean)\n .join(';');\n return (\n <section\n class={`kui-workbench ${className}`.trim()}\n id={id}\n data-component=\"workbench\"\n data-main-min-size={mainMin === undefined ? undefined : String(mainMin)}\n data-main-min-height={\n mainMinBlock === undefined ? undefined : String(mainMinBlock)\n }\n style={style || undefined}\n aria-label={label}\n slot={slot}\n >\n {leftRail && <Rail id={id} side=\"left\" panel={leftRail} />}\n {restore(leftRail, 'left', 'bottom-start')}\n <div class=\"kui-workbench__center\">\n <div class=\"kui-workbench__main\" data-workbench-main>\n {main}\n </div>\n {bottomDrawer && <Drawer id={id} panel={bottomDrawer} />}\n {restore(bottomDrawer, 'bottom', 'bottom-end')}\n </div>\n {rightRail && <Rail id={id} side=\"right\" panel={rightRail} />}\n {restore(rightRail, 'right', 'bottom-end')}\n </section>\n );\n}\n"]}
|
package/docs/accessibility.md
CHANGED
|
@@ -12,6 +12,7 @@ These are normative contracts for package components and consuming applications.
|
|
|
12
12
|
- `wireNavStack` moves focus into every newly active top view and remembers the last focused descendant of each mounted view for pop restoration. Mark a preferred initial heading or control with `data-nav-focus` (and `tabindex="-1"` when it is not normally tabbable); otherwise the helper uses the first enabled focusable descendant, then a programmatically focusable view-container fallback.
|
|
13
13
|
- In any `.kui-pane`, menu rows, `ListHeader` actions, and toolbar groups keep a minimum 44px target in both dimensions. A `ListHeader` fills the available inline width and keeps that action at the logical end, including in RTL. Its action visual defaults to 18px through `--kui-list-header-action-icon-size` inside a fitted 36px hover square; a transparent hit layer extends the pointer target to at least 44 × 44 around that square without moving layout or enlarging the painted surface. Compact headers are 36px tall like the rest of the compact List family, so their action target is 44px wide and clamped to the 36px header height instead of reaching into a neighboring row. Do not reduce the target to the visible icon, and do not force the button itself to 44px.
|
|
14
14
|
- `LoadingSpinner` is either labeled (`role="img"`) or hidden. Its rotation stops for reduced motion.
|
|
15
|
+
- An unlabeled `Skeleton` (including the value slots a component's `placeholder` prop renders) is decorative loading chrome: it is `aria-hidden`, conveys no information a user needs to act on, and is deliberately exempt from the WCAG 3:1 non-text-contrast target. Inside a solid `Badge` it stays subtle (about 2.1–4.4:1 in light, 2.4–2.8:1 in dark, depending on tone); `tests/browser/tone-contrast.spec.ts` holds those values as a regression floor rather than an accessibility threshold. Announce loading through a labeled `Skeleton`, `LoadingSpinner`, or `aria-busy` on the owning region, never through the skeleton's visibility.
|
|
15
16
|
- `StateBanner` defaults to polite `role="status"`; use `urgency="alert"` only for an attention-requiring failure. Its optional `badge` is persistent inline status/count content beside the title and is announced as part of the banner; keep it terse and do not rely on color alone for its meaning.
|
|
16
17
|
- `EmptyState` reports busy state through `aria-busy` and never relies on an illustration as its label.
|
|
17
18
|
- `Select` follows the Web Awesome host's standard `input`/`change` events. Application tests verify the live `value`, focus, and events—not attributes alone. Its decorative option icons and value-dependent selected content remain present after controlled rerenders, so the visible choice does not silently lose its non-text cue.
|
|
@@ -120,6 +121,15 @@ The handle exposes separator role, orientation, name, minimum, maximum, and curr
|
|
|
120
121
|
them after a re-render writes the rendered props back, so assistive
|
|
121
122
|
technology never reads an unclamped size between interactions (the
|
|
122
123
|
disposer disconnects both observers);
|
|
124
|
+
- a truthful value below the minimum: when the container shows the track at
|
|
125
|
+
less than `min` (a narrowing `Workbench` squeezes its resizable rails), the
|
|
126
|
+
handle reports that shown size in `aria-valuenow` and pins `aria-valuemin`
|
|
127
|
+
and `aria-valuemax` to it — WAI-ARIA requires the value to lie inside the
|
|
128
|
+
range, and the separator cannot move a track its container holds. A pinned
|
|
129
|
+
separator — squeezed, or with a reachable range collapsed to one size —
|
|
130
|
+
commits nothing: arrow keys, Home/End, and drags leave the app's size (and
|
|
131
|
+
its storage) as it was, so the track returns to it, with the rendered range,
|
|
132
|
+
once it has room;
|
|
123
133
|
- a whole hit target and focus ring at a clamped edge: when the separator has
|
|
124
134
|
less room past it than the handle's 10px overhang (a parent clamps the track
|
|
125
135
|
to its own edge), the region carries `data-handle-inset` and the handle sits
|
|
@@ -162,7 +172,10 @@ forced to a dark color scheme, but it is **not** in the top layer, so it never
|
|
|
162
172
|
covers dialogs, popovers, or other overlays. Its children are the app's controls
|
|
163
173
|
(normally `ToolbarControlGroup`s), which keep their own names, focus, and
|
|
164
174
|
keyboard behavior; the application owns their actions, the toolbar's visibility,
|
|
165
|
-
and — via `position` and `--kui-floating-toolbar-inset
|
|
175
|
+
and — via `position` and `--kui-floating-toolbar-inset`, set on the toolbar
|
|
176
|
+
or an ancestor — where it sits. Inside a `Workbench`, `CollapsiblePanel`, or
|
|
177
|
+
`ResizableRegion` `restoreControl` the restore corner owns the inset, so the
|
|
178
|
+
toolbar floats from that corner.
|
|
166
179
|
|
|
167
180
|
## Tabs
|
|
168
181
|
|
|
@@ -180,8 +193,11 @@ that geometry; applications must not override its anatomy to create the split.
|
|
|
180
193
|
presentations. Icon-only tabs keep the required `name` as the tab button's
|
|
181
194
|
accessible name while visually hiding the duplicate label. A `pending` tab (known,
|
|
182
195
|
still opening) keeps its visible name as the tab button's accessible name, so a
|
|
183
|
-
trailing spinner's own label is not folded into it
|
|
184
|
-
|
|
196
|
+
trailing spinner's own label is not folded into it. It is `aria-busy` and stays
|
|
197
|
+
selectable (in the roving tab order like a live tab), so the application can
|
|
198
|
+
show placeholder content in its panel until loading completes; its close
|
|
199
|
+
button is disabled and hidden and it is not draggable. A `placeholder` tab
|
|
200
|
+
(unknown) is instead disabled, out of the tab order, and not draggable. These props own
|
|
185
201
|
component appearance only; the application still owns the bar's outer placement.
|
|
186
202
|
`AppTab.rootAttributes` accepts application `data-*` metadata only; runtime
|
|
187
203
|
filtering rejects roles plus case variants of component- or wiring-owned
|
|
@@ -192,8 +208,11 @@ and must not contain interactive descendants.
|
|
|
192
208
|
activation, same-bar pointer reordering, `Alt+Shift+ArrowLeft/ArrowRight`
|
|
193
209
|
reordering, focus restoration, scroll-into-view, and pointer-proximity
|
|
194
210
|
autoscroll at either horizontal edge. Edge autoscroll is direct manipulation,
|
|
195
|
-
stops on drop/drag end/disposal, and does not change keyboard behavior.
|
|
196
|
-
|
|
211
|
+
stops on drop/drag end/disposal, and does not change keyboard behavior. When
|
|
212
|
+
the application changes a bar's selected tab without focus (adding and
|
|
213
|
+
selecting a tab), the wiring scrolls that tab's strip — never the page — so
|
|
214
|
+
the new selection is fully visible; a re-render that keeps the same selection
|
|
215
|
+
leaves a strip the user scrolled where it is. It returns a disposer.
|
|
197
216
|
|
|
198
217
|
Arrow / Home / End follow the ARIA Tabs **automatic-activation** pattern by
|
|
199
218
|
default: they move roving focus and select the focused tab. If selection synchronously
|
|
@@ -276,15 +295,26 @@ and focus deliberately moved to another control; no app-level reopen callback is
|
|
|
276
295
|
|
|
277
296
|
## CollapsiblePanel / sidebar
|
|
278
297
|
|
|
279
|
-
`CollapsiblePanel` is a labeled `aside` region that
|
|
280
|
-
collapsed
|
|
298
|
+
`CollapsiblePanel` is a labeled `aside` region that renders `inert` and
|
|
299
|
+
`aria-hidden` while collapsed, straight from the app's `collapsed` flag, so its
|
|
300
|
+
controls leave the Tab order and the accessibility tree even while the content
|
|
301
|
+
is still sliding out, and focusing one can never scroll clipped content back into
|
|
302
|
+
view (`aria-hidden` alone left them focusable inside a hidden subtree). Its
|
|
303
|
+
content slides via transform (disabled under reduced motion); `inert` does not
|
|
304
|
+
affect rendering, so the slide still plays. A `restoreControl` renders outside
|
|
305
|
+
the panel and stays reachable. A collapsed `ResizableRegion` renders `inert` and
|
|
306
|
+
`aria-hidden` on the region itself the same way (its content wrapper is `inert`
|
|
307
|
+
too, and its separator is already hidden and not displayed), so no empty
|
|
308
|
+
labeled region landmark stays behind; its `restoreControl` stays outside the
|
|
309
|
+
region.
|
|
281
310
|
`CollapsiblePanelToggle` is a named button carrying `aria-expanded` and the
|
|
282
311
|
standard per-side collapse/expand glyph (`PanelLeft*` / `PanelRight*` /
|
|
283
312
|
`PanelBottom*`); place a collapse toggle inside the panel and an expand toggle in
|
|
284
313
|
an always-visible location so it is reachable while collapsed. `wireSidebar()`
|
|
285
314
|
moves focus into the panel when it opens and restores it to the trigger when it
|
|
286
|
-
closes; a trigger inside the now-
|
|
287
|
-
outside it instead
|
|
315
|
+
closes; a trigger inside the now-inert panel hands focus to the panel's toggle
|
|
316
|
+
outside it instead (and focus stranded there by a wide → compact crossing is
|
|
317
|
+
rescued the same way), so an app may render the expand toggle only while the panel
|
|
288
318
|
is collapsed. When a `deviceClass()` reports `compact`, an open panel becomes an overlay
|
|
289
319
|
with a dismissable backdrop, Escape and backdrop-click collapse it, and Tab is
|
|
290
320
|
trapped within it (the ARIA dialog pattern). That modal state only ever begins
|
|
@@ -293,6 +323,21 @@ crossing, every panel starts collapsed, so no page load or resize traps focus. T
|
|
|
293
323
|
signal, the panels, their sizes, and content; the wire may persist the collapsed
|
|
294
324
|
state per panel.
|
|
295
325
|
|
|
326
|
+
## Workbench
|
|
327
|
+
|
|
328
|
+
Each `Workbench` rail and drawer is a labeled region (`aside` for a rail, `section`
|
|
329
|
+
for the drawer). While a panel is collapsed the region itself renders `inert` and
|
|
330
|
+
`aria-hidden="true"`, and so does not linger as an empty landmark, and its content
|
|
331
|
+
wrapper is `inert`, all rendered from the app's `collapsed` flag, so its controls
|
|
332
|
+
leave the Tab order and the accessibility tree even while the content is still
|
|
333
|
+
sliding out, and focusing one can never scroll clipped content back into view. The panel's `restoreControl` renders
|
|
334
|
+
outside that content and stays reachable; a resizable panel's separator also leaves
|
|
335
|
+
the Tab order while collapsed or not inline. `wireWorkbench`, given a panel's
|
|
336
|
+
`collapsed` signal, returns focus stranded in a closing panel to the control that
|
|
337
|
+
opened it, else the restore control, else a control whose `aria-controls` names the
|
|
338
|
+
panel; an open overlay panel takes focus and keeps Tab inside it (the ARIA dialog
|
|
339
|
+
pattern). See [`workbench.md`](workbench.md).
|
|
340
|
+
|
|
296
341
|
## Verification matrix
|
|
297
342
|
|
|
298
343
|
For each changed component, inspect default, hover, focus, disabled, selected/pressed, busy/error, long-content, wide, narrow, light, dark, increased-contrast, reduced-motion, keyboard-only, and 200%-zoom states where applicable. DOM order must match reading and focus order, with no clipping or unreachable action.
|
package/docs/app-layouts.md
CHANGED
|
@@ -113,7 +113,18 @@ device in tests.
|
|
|
113
113
|
selection).
|
|
114
114
|
- **IDE (complex tool):** `Workbench` with a left navigator rail, a right
|
|
115
115
|
inspector rail, and a bottom console drawer, each `collapsed` bound to a
|
|
116
|
-
signal. Only offer this `atLeast('desktop')`.
|
|
116
|
+
signal. Only offer this `atLeast('desktop')`. To let people size the
|
|
117
|
+
navigator or console, opt those panels in with `resizable` and call
|
|
118
|
+
`wireWorkbench` with their size signals and `deviceClass`; resizing is
|
|
119
|
+
suspended on `compact`, where the rails become overlay drawers — or give each
|
|
120
|
+
rail `responsiveOverlayAt: "narrow"` and let the Workbench's own width decide
|
|
121
|
+
when it overlays, with no device check; pass each rail's `collapsed` signal
|
|
122
|
+
to `wireWorkbench` so the overlay starts collapsed, takes focus and keeps Tab
|
|
123
|
+
inside it while open, and closes on Escape or an outside press. Inline
|
|
124
|
+
rails, fixed or resizable, leave the editor its `mainMinSize` (320px by
|
|
125
|
+
default) and shrink in proportion when the window narrows; the bottom drawer
|
|
126
|
+
leaves it `mainMinHeight` (120px by default) and shrinks when the window gets
|
|
127
|
+
shorter.
|
|
117
128
|
- **Social app (tabbed):** `TabScaffold` with Home / Search / Profile tabs, each
|
|
118
129
|
`content` a `NavStack`. On a tablet/desktop, render the same sections as a
|
|
119
130
|
`Workbench` left rail instead of a bottom bar.
|