@kerfjs/ui 5.0.0-beta.82 → 5.0.0-beta.84
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/catalog-authoring.json +1 -3
- package/ai/catalog-authoring.schema.json +3 -7
- package/ai/component-catalog.json +73 -2
- package/ai/component-composition.json +64 -1
- package/ai/components/README.md +1 -0
- package/ai/components/anchored-surface.md +86 -0
- package/ai/components/tab-navigator.md +3 -2
- package/ai/public-api-signatures-v1.md +79 -35
- package/ai/skill.md +2 -2
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/dist/anchored-surface.d.ts +30 -0
- package/dist/anchored-surface.js +3 -0
- package/dist/anchored-surface.js.map +1 -0
- package/dist/app-tab.js +1 -1
- package/dist/browser/anchored-surface.js +3 -0
- package/dist/browser/catalog.js +1 -1
- package/dist/catalog-resources.d.ts +2 -2
- package/dist/catalog.d.ts +4 -4
- package/dist/catalog.js +34 -31
- package/dist/catalog.js.map +1 -1
- package/dist/{chunk-I4SDIMFA.js → chunk-2YPPNFYT.js} +2 -2
- package/dist/{chunk-I4SDIMFA.js.map → chunk-2YPPNFYT.js.map} +1 -1
- package/dist/{chunk-VHXHLZ6R.js → chunk-3DXHNLME.js} +3 -3
- package/dist/{chunk-VHXHLZ6R.js.map → chunk-3DXHNLME.js.map} +1 -1
- package/dist/{chunk-HFQDSSED.js → chunk-3PO7JEK7.js} +4 -4
- package/dist/{chunk-HFQDSSED.js.map → chunk-3PO7JEK7.js.map} +1 -1
- package/dist/chunk-6IVNMC6R.js +19 -0
- package/dist/chunk-6IVNMC6R.js.map +1 -0
- package/dist/{chunk-VR7MWD2X.js → chunk-CD5TOO22.js} +4 -13
- package/dist/chunk-CD5TOO22.js.map +1 -0
- package/dist/{chunk-H7ZJEI3H.js → chunk-DL55BSQX.js} +3 -3
- package/dist/{chunk-H7ZJEI3H.js.map → chunk-DL55BSQX.js.map} +1 -1
- package/dist/{chunk-VIGVNDXZ.js → chunk-GPCAXYMT.js} +4 -4
- package/dist/{chunk-VIGVNDXZ.js.map → chunk-GPCAXYMT.js.map} +1 -1
- package/dist/chunk-IOS2NXW7.js +53 -0
- package/dist/chunk-IOS2NXW7.js.map +1 -0
- package/dist/{chunk-HFLGWMG3.js → chunk-KAJVVN7Q.js} +3 -3
- package/dist/{chunk-HFLGWMG3.js.map → chunk-KAJVVN7Q.js.map} +1 -1
- package/dist/{chunk-LQKY5S4F.js → chunk-KL3W6FUN.js} +4 -4
- package/dist/{chunk-LQKY5S4F.js.map → chunk-KL3W6FUN.js.map} +1 -1
- package/dist/{chunk-NVKRZ5DI.js → chunk-LFPW4YC6.js} +4 -4
- package/dist/{chunk-NVKRZ5DI.js.map → chunk-LFPW4YC6.js.map} +1 -1
- package/dist/{chunk-75I3SNUJ.js → chunk-MN3POJFN.js} +2 -2
- package/dist/{chunk-75I3SNUJ.js.map → chunk-MN3POJFN.js.map} +1 -1
- package/dist/{chunk-5D3WDMIK.js → chunk-MORVLMDG.js} +4 -16
- package/dist/chunk-MORVLMDG.js.map +1 -0
- package/dist/{chunk-RRCD3A6G.js → chunk-MTCTX25G.js} +2 -2
- package/dist/{chunk-RRCD3A6G.js.map → chunk-MTCTX25G.js.map} +1 -1
- package/dist/{chunk-PQQQXJGV.js → chunk-NP3FSLNX.js} +2 -2
- package/dist/{chunk-PQQQXJGV.js.map → chunk-NP3FSLNX.js.map} +1 -1
- package/dist/{chunk-ARZ4RSQO.js → chunk-PGL6TXFW.js} +3 -3
- package/dist/{chunk-ARZ4RSQO.js.map → chunk-PGL6TXFW.js.map} +1 -1
- package/dist/{chunk-NVRJ7C3W.js → chunk-QQICWWNX.js} +4 -16
- package/dist/chunk-QQICWWNX.js.map +1 -0
- package/dist/{chunk-CVKIADIS.js → chunk-SJW5BCEQ.js} +7 -5
- package/dist/chunk-SJW5BCEQ.js.map +1 -0
- package/dist/{chunk-JZM3G6RT.js → chunk-TTGIHMDR.js} +4 -4
- package/dist/{chunk-JZM3G6RT.js.map → chunk-TTGIHMDR.js.map} +1 -1
- package/dist/{chunk-LRAIS3YB.js → chunk-U657IHFB.js} +1 -1
- package/dist/chunk-U657IHFB.js.map +1 -0
- package/dist/{chunk-3SXWV2IR.js → chunk-VLGQIYZ5.js} +2 -2
- package/dist/{chunk-3SXWV2IR.js.map → chunk-VLGQIYZ5.js.map} +1 -1
- package/dist/{chunk-YLJ6X3AC.js → chunk-XK4E225T.js} +2 -2
- package/dist/{chunk-YLJ6X3AC.js.map → chunk-XK4E225T.js.map} +1 -1
- package/dist/{chunk-BJLXE56E.js → chunk-XKZPBRU7.js} +4 -4
- package/dist/{chunk-BJLXE56E.js.map → chunk-XKZPBRU7.js.map} +1 -1
- package/dist/{chunk-62VMNBV2.js → chunk-XNXKIPU5.js} +2 -2
- package/dist/{chunk-62VMNBV2.js.map → chunk-XNXKIPU5.js.map} +1 -1
- package/dist/{chunk-EFDCALR3.js → chunk-Y43B6DS3.js} +2 -2
- package/dist/{chunk-EFDCALR3.js.map → chunk-Y43B6DS3.js.map} +1 -1
- package/dist/{chunk-3RE2JMZU.js → chunk-YOKEL75M.js} +2 -2
- package/dist/{chunk-3RE2JMZU.js.map → chunk-YOKEL75M.js.map} +1 -1
- package/dist/collapsible-panel.js +4 -3
- package/dist/collapsible-panel.js.map +1 -1
- package/dist/css-values.d.ts +8 -1
- package/dist/css-values.js +1 -1
- package/dist/disclosure-arrow.js +1 -1
- package/dist/empty-state.js +1 -1
- package/dist/grid.d.ts +5 -4
- package/dist/grid.js +2 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +26 -24
- package/dist/list-action-row.js +1 -1
- package/dist/list-header.js +1 -1
- package/dist/list-item.js +1 -1
- package/dist/list.d.ts +5 -4
- package/dist/list.js +2 -1
- package/dist/loading-spinner.js +1 -1
- package/dist/lucide-icon.js +1 -1
- package/dist/{nav-stack-B1Qc2e5q.d.ts → nav-stack-BvSKY448.d.ts} +10 -2
- package/dist/nav-stack.d.ts +1 -1
- package/dist/nav-stack.js +2 -1
- package/dist/row.d.ts +5 -4
- package/dist/row.js +2 -1
- package/dist/select.js +1 -1
- package/dist/skeleton.js +1 -1
- package/dist/spacer.js +1 -1
- package/dist/split-view.d.ts +1 -1
- package/dist/split-view.js +2 -1
- package/dist/split-view.js.map +1 -1
- package/dist/state-banner.js +1 -1
- package/dist/styles/anchored-surface.css +15 -0
- package/dist/styles/catalog/components/catalog-example.css +1 -2
- package/dist/styles/catalog/components/catalog-stage.css +40 -42
- package/dist/styles/catalog/components/catalog.css +0 -11
- package/dist/styles/components/collections/list/list.css +4 -1
- package/dist/styles/components/feedback/anchored-surface/anchored-surface.css +15 -0
- package/dist/styles/components/layout/grid/grid.css +4 -1
- package/dist/styles/components/layout/pane/pane.css +15 -0
- package/dist/styles/components/layout/row/row.css +4 -1
- package/dist/styles/components/layout/workbench/workbench.css +17 -0
- package/dist/styles/components/navigation/nav-stack/nav-stack.css +21 -5
- package/dist/styles/components/navigation/tab-navigator/tab-navigator.css +25 -9
- package/dist/styles/grid.css +4 -1
- package/dist/styles/list.css +4 -1
- package/dist/styles/nav-stack.css +21 -5
- package/dist/styles/pane.css +15 -0
- package/dist/styles/row.css +4 -1
- package/dist/styles/styles.css +1 -0
- package/dist/styles/tab-navigator.css +25 -9
- package/dist/styles/workbench.css +17 -0
- package/dist/tab-navigator.d.ts +10 -3
- package/dist/tab-navigator.js +2 -0
- package/dist/tab-navigator.js.map +1 -1
- package/dist/token-search-field.js +1 -1
- package/dist/toolbar-control-group.js +1 -1
- package/dist/toolbar-text.js +1 -1
- package/dist/{types-C9BubXfz.d.ts → types-Bn-dXLcs.d.ts} +6 -2
- package/dist/value-table.js +1 -1
- package/dist/wire-catalog.d.ts +1 -9
- package/dist/wire-catalog.js +2 -200
- package/dist/wire-catalog.js.map +1 -1
- package/dist/wire-token-search-fields.js +1 -1
- package/dist/workbench.d.ts +1 -1
- package/dist/workbench.js +2 -1
- package/docs/anchored-surface.md +51 -0
- package/docs/catalog.md +38 -82
- package/docs/component-contract.md +3 -1
- package/docs/component-selection.md +19 -1
- package/docs/layout.md +19 -2
- package/docs/nav-stack.md +13 -4
- package/docs/tab-navigator.md +12 -4
- package/docs/type-contracts.md +2 -0
- package/docs/ux-demo.md +4 -4
- package/docs/workbench.md +2 -0
- package/llms.txt +8 -3
- package/package.json +7 -1
- package/dist/chunk-5D3WDMIK.js.map +0 -1
- package/dist/chunk-CVKIADIS.js.map +0 -1
- package/dist/chunk-LRAIS3YB.js.map +0 -1
- package/dist/chunk-NVRJ7C3W.js.map +0 -1
- package/dist/chunk-VR7MWD2X.js.map +0 -1
package/docs/catalog.md
CHANGED
|
@@ -37,7 +37,17 @@ description?, tags?, resources?, related? }] }`. Each entry becomes a sidebar
|
|
|
37
37
|
the row's quiet status treatment for decision metadata such as `Discouraged`.
|
|
38
38
|
- **`content`** — the rendered preview for the active entry. Keep a map of `id →
|
|
39
39
|
() => SafeHtml` in your app and call `renderers[active]()` in your render.
|
|
40
|
-
- **`
|
|
40
|
+
- **`backgroundStyle`** — the preview stage backdrop: `checkerboard` (default,
|
|
41
|
+
8px squares),
|
|
42
|
+
`vertical-stripes` (alternating 8px bands), `layout-guide` (single-pixel
|
|
43
|
+
vertical rules at 15px, 23px, and 32px from the stage edge), `surface`, or `sunken`. The
|
|
44
|
+
patterned choices share the main Pane's sunken surface and reveal transparent
|
|
45
|
+
specimen edges; `surface` paints an
|
|
46
|
+
opaque backdrop. `sunken` leaves the stage
|
|
47
|
+
transparent so the main Pane's semitransparent sunken surface paints only once.
|
|
48
|
+
A controlled app may pass its current choice on each render.
|
|
49
|
+
- **`brand`** — `{ title, subtitle?, logoUrl? }` for the sidebar header. The
|
|
50
|
+
optional logo paints through `ToolbarControlGroup.avatarImage`.
|
|
41
51
|
- **`secondarySections`** — an optional secondary "ecosystem" group shown below the
|
|
42
52
|
primary sections with a quieter treatment: `{ label, sections, collapsible?,
|
|
43
53
|
expanded? }`. When `collapsible`, the label is a disclosure toggle controlling
|
|
@@ -74,11 +84,11 @@ chosen component.
|
|
|
74
84
|
|
|
75
85
|
Classify every entry before rendering it:
|
|
76
86
|
|
|
77
|
-
| Entry kind | Preview purpose |
|
|
78
|
-
| --------------- | ------------------------------------------------------------------------------------------------------- |
|
|
79
|
-
| **Component** | Show one public component, its meaningful variants, and adverse states. |
|
|
80
|
-
| **Composition** | Show components commonly used together in one focused container or surface. |
|
|
81
|
-
| **Recipe** | Teach the recommended production approach to a specific task, including ownership and application glue. |
|
|
87
|
+
| Entry kind | Preview purpose |
|
|
88
|
+
| --------------- | ------------------------------------------------------------------------------------------------------- |
|
|
89
|
+
| **Component** | Show one public component, its meaningful variants, and adverse states. |
|
|
90
|
+
| **Composition** | Show components commonly used together in one focused container or surface. |
|
|
91
|
+
| **Recipe** | Teach the recommended production approach to a specific task, including ownership and application glue. |
|
|
82
92
|
|
|
83
93
|
These three kinds are exhaustive. Theme/foundation showcases use a functional
|
|
84
94
|
category, third-party specimens use source metadata, and state galleries retain
|
|
@@ -94,10 +104,6 @@ field exists. Show a `Composition` tag rather than repeating the word in a
|
|
|
94
104
|
composition's display name; the generic `Catalog` accepts that through the
|
|
95
105
|
entry's `tags` array.
|
|
96
106
|
|
|
97
|
-
Keep `geometryOverlay` present and compute it from the active entry. Do not make
|
|
98
|
-
it a permanent catalog-wide `true`, and do not give individual specimens their
|
|
99
|
-
own overlay implementation.
|
|
100
|
-
|
|
101
107
|
### Required structure
|
|
102
108
|
|
|
103
109
|
- `Catalog` is the one shell. The app owns active-entry state and passes one
|
|
@@ -149,29 +155,17 @@ const buttonPreview = (
|
|
|
149
155
|
<CatalogExample label="Control" align="inline-control">
|
|
150
156
|
<SegmentedControl id="view" label="View" value="list" choices={choices} />
|
|
151
157
|
</CatalogExample>
|
|
152
|
-
<CatalogExample
|
|
153
|
-
label="Authoring note"
|
|
154
|
-
note="Explanatory chrome is not a specimen."
|
|
155
|
-
rootAttributes={{ "data-catalog-geometry-overlay-skip": "" }}
|
|
156
|
-
>
|
|
158
|
+
<CatalogExample label="Authoring note" note="Explain the specimen below.">
|
|
157
159
|
<p>Use the public helper contract.</p>
|
|
158
160
|
</CatalogExample>
|
|
159
161
|
</CatalogExampleStack>
|
|
160
162
|
);
|
|
161
163
|
```
|
|
162
164
|
|
|
163
|
-
The overlay selects every immediate child of a `CatalogExample`; the helper
|
|
164
|
-
marks its generated label and note so they are excluded automatically. It does
|
|
165
|
-
not recursively promote a nested child to be the specimen. Outside an example
|
|
166
|
-
row, it selects only top-level
|
|
167
|
-
`[data-component]` roots in the canvas and ignores nested component descendants.
|
|
168
|
-
These rules keep a row's label/group scaffolding out of the measurement and make
|
|
169
|
-
the authored nesting determine exactly what is inspected.
|
|
170
|
-
|
|
171
165
|
### Metadata ownership
|
|
172
166
|
|
|
173
|
-
Use `rootAttributes` on either helper for authoring metadata such as `data-demo
|
|
174
|
-
|
|
167
|
+
Use `rootAttributes` on either helper for authoring metadata such as `data-demo`;
|
|
168
|
+
the metadata lands on that helper's
|
|
175
169
|
rendered root. The slot accepts only `data-*` strings. Structural
|
|
176
170
|
`data-catalog-example`, `data-catalog-example-stack`, and `data-align` semantics
|
|
177
171
|
remain helper-owned and are rejected case-insensitively at runtime, including
|
|
@@ -182,50 +176,21 @@ The app owns entry ids, `kind`, routing, sources, relationships, and test hooks.
|
|
|
182
176
|
`Catalog.stageRootAttributes` places app-owned `data-*` state on the
|
|
183
177
|
catalog-owned preview stage without an extra wrapper; `data-catalog-stage`
|
|
184
178
|
remains protected.
|
|
179
|
+
The stage is the main Pane's direct scrolling content, owns an 8px inset, and
|
|
180
|
+
grows with tall specimens. The main Pane keeps its sunken appearance without a
|
|
181
|
+
deep inset.
|
|
185
182
|
The helpers own their structural markers, label/note anatomy, alignment marker,
|
|
186
183
|
and private classes. Component metadata such as margin/border/padding ownership
|
|
187
|
-
lives in `component-catalog.json
|
|
188
|
-
pixels.
|
|
189
|
-
|
|
190
|
-
### Geometry overlay and legend
|
|
184
|
+
lives in `component-catalog.json`.
|
|
191
185
|
|
|
192
|
-
|
|
193
|
-
`wireCatalogGeometryOverlay(root)` once after the first render and retain its
|
|
194
|
-
disposer alongside `wireCatalog`'s.
|
|
186
|
+
### Component metadata
|
|
195
187
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
| Quiet dashed outline | The border-box bound of a transparent selected specimen with no visible border. | A real CSS border, padding edge, or proof that the specimen owns size. |
|
|
201
|
-
|
|
202
|
-
The overlay recomputes from live rendered styles when specimens resize, preview
|
|
203
|
-
markup changes, theme/root attributes change, or loaded/inline stylesheets
|
|
204
|
-
change. Component CSS is therefore the source of truth; do not duplicate margin
|
|
205
|
-
or border values in demo data. The overlay does not visualize padding, gaps,
|
|
206
|
-
negative/zero margins, scroll overflow, hit targets, or nested descendants.
|
|
207
|
-
Inspect computed styles and the machine-readable `geometry` metadata for those
|
|
208
|
-
facts. Opaque borderless specimens do not receive the dashed bound.
|
|
209
|
-
|
|
210
|
-
```tsx
|
|
211
|
-
<Catalog
|
|
212
|
-
{...props}
|
|
213
|
-
geometryOverlay={activeEntry.kind === "component"}
|
|
214
|
-
content={renderers[active.value]()}
|
|
215
|
-
/>
|
|
216
|
-
```
|
|
188
|
+
Machine-readable geometry ownership metadata identifies whether the component,
|
|
189
|
+
its parent, or its children are responsible for margin, border, and padding.
|
|
190
|
+
Keep that metadata in `component-catalog.json` and use the rendered specimen to
|
|
191
|
+
review the actual appearance.
|
|
217
192
|
|
|
218
|
-
|
|
219
|
-
`CatalogExample` or `CatalogExampleStack` only when that whole subtree is
|
|
220
|
-
explanatory chrome rather than a specimen. The marker excludes the marked root
|
|
221
|
-
and every descendant from selection; it does not merely hide one band. It is
|
|
222
|
-
normally unnecessary in a composition because the active entry already sets the
|
|
223
|
-
global overlay to `false`.
|
|
224
|
-
|
|
225
|
-
Use the overlay together with machine-readable geometry ownership metadata; the
|
|
226
|
-
overlay verifies what is rendered, while metadata tells people and AI tools
|
|
227
|
-
whether the component, its parent, or its children are responsible for margin,
|
|
228
|
-
border, and padding. Catalogs for downstream components should conform to the
|
|
193
|
+
Catalogs for downstream components should conform to the
|
|
229
194
|
[`component-catalog-extension.schema.json`](../ai/component-catalog-extension.schema.json)
|
|
230
195
|
contract and can start from the checked
|
|
231
196
|
[`component-catalog-extension.json`](./examples/component-catalog-extension.json)
|
|
@@ -275,8 +240,8 @@ facts that source can prove without guessing at rendered intent:
|
|
|
275
240
|
|
|
276
241
|
- focused component routes import and use `CatalogExampleStack` and
|
|
277
242
|
`CatalogExample` from the public package;
|
|
278
|
-
- focused route metadata uses the helpers' `rootAttributes` slot
|
|
279
|
-
are not empty
|
|
243
|
+
- focused route metadata uses the helpers' `rootAttributes` slot and example rows
|
|
244
|
+
are not empty;
|
|
280
245
|
- every `@kerfjs/ui` import is a published package export and relative imports
|
|
281
246
|
do not reach into `ui/src`;
|
|
282
247
|
- focused demos have no relative stylesheet imports, inline style attributes,
|
|
@@ -286,20 +251,19 @@ facts that source can prove without guessing at rendered intent:
|
|
|
286
251
|
analyzer) import no stylesheet other than public `@kerfjs/ui` CSS, set no
|
|
287
252
|
inline style, and name only published layout and Web Awesome classes; each
|
|
288
253
|
recipe declares its catalog frame through an exported `presentation`; and
|
|
289
|
-
- the shell derives
|
|
290
|
-
from the active entry's source and kind.
|
|
254
|
+
- the shell derives the documented demo mode from the active entry's kind.
|
|
291
255
|
|
|
292
256
|
The gate deliberately does not infer component ownership from arbitrary class
|
|
293
|
-
names, margins, borders, or nested descendants. Runtime
|
|
294
|
-
|
|
257
|
+
names, margins, borders, or nested descendants. Runtime appearance remains the
|
|
258
|
+
browser suite's job.
|
|
295
259
|
|
|
296
260
|
A focused route may bypass the two public layout helpers only when the route's
|
|
297
261
|
stage geometry is itself the reviewed specimen. Add the narrow waiver to
|
|
298
262
|
[`catalog-conformance-exceptions.json`](../ux-demo/catalog-conformance-exceptions.json)
|
|
299
263
|
with the exact route, source file, stable diagnostic ids, a substantive reason,
|
|
300
264
|
and the reviewing `KF-*` ticket. Only helper/metadata rules are waivable;
|
|
301
|
-
private imports, local styling, private markup, empty examples,
|
|
302
|
-
|
|
265
|
+
private imports, local styling, private markup, empty examples, and shell-mode
|
|
266
|
+
drift always fail. The gate rejects duplicate, malformed, unused,
|
|
303
267
|
and stale exceptions, so delete a waiver when its route adopts the standard
|
|
304
268
|
helpers.
|
|
305
269
|
|
|
@@ -330,7 +294,6 @@ import {
|
|
|
330
294
|
import {
|
|
331
295
|
revealCatalogEntry,
|
|
332
296
|
wireCatalog,
|
|
333
|
-
wireCatalogGeometryOverlay,
|
|
334
297
|
} from "@kerfjs/ui/wire-catalog";
|
|
335
298
|
// A CSS-aware (browser-condition) bundler loads the Catalog's CSS with its import.
|
|
336
299
|
|
|
@@ -375,11 +338,7 @@ const renderers: Record<string, () => SafeHtml> = {
|
|
|
375
338
|
<CatalogExample label="Default" align="inline-control">
|
|
376
339
|
<Button label="Save" />
|
|
377
340
|
</CatalogExample>
|
|
378
|
-
<CatalogExample
|
|
379
|
-
label="Authoring note"
|
|
380
|
-
note="This explanatory row is deliberately excluded from inspection."
|
|
381
|
-
rootAttributes={{ "data-catalog-geometry-overlay-skip": "" }}
|
|
382
|
-
>
|
|
341
|
+
<CatalogExample label="Authoring note" note="Explain the specimen below.">
|
|
383
342
|
<p>The application owns product copy and actions.</p>
|
|
384
343
|
</CatalogExample>
|
|
385
344
|
</CatalogExampleStack>
|
|
@@ -402,7 +361,6 @@ const initial =
|
|
|
402
361
|
const active = signal(initial);
|
|
403
362
|
const collapsed = signal(false);
|
|
404
363
|
const theme = signal<"light" | "dark">("light");
|
|
405
|
-
const activeEntry = () => entries.find(({ id }) => id === active.value) ?? entries[0];
|
|
406
364
|
|
|
407
365
|
const app = document.getElementById("app")!;
|
|
408
366
|
mount(app, () => (
|
|
@@ -413,7 +371,6 @@ mount(app, () => (
|
|
|
413
371
|
content={renderers[active.value]?.() ?? <></>}
|
|
414
372
|
collapsed={collapsed.value}
|
|
415
373
|
theme={theme.value}
|
|
416
|
-
geometryOverlay={activeEntry().kind === "component"}
|
|
417
374
|
/>
|
|
418
375
|
));
|
|
419
376
|
|
|
@@ -431,7 +388,6 @@ wireCatalog(app, {
|
|
|
431
388
|
urlParam: "c", // mirror the active id into ?c=<id>
|
|
432
389
|
revealSelection: true, // reveal long desktop sidebars without moving focus
|
|
433
390
|
});
|
|
434
|
-
wireCatalogGeometryOverlay(app);
|
|
435
391
|
|
|
436
392
|
// Optional for an initial deep link whose row may start outside the viewport.
|
|
437
393
|
revealCatalogEntry(app, initial, { block: "center" });
|
|
@@ -484,7 +440,7 @@ matching options on `wireCatalog`) if they collide with your own action table.
|
|
|
484
440
|
|
|
485
441
|
`Catalog` composes public components (`Workbench`, `Pane`, `Toolbar`, `List`,
|
|
486
442
|
`ListHeader`, `ListItem`, `ListInsetText`, `Text`, …) and owns only the preview stage
|
|
487
|
-
(the checkerboard and
|
|
443
|
+
(the checkerboard and left-aligned canvas), the example viewport
|
|
488
444
|
options, and the brand mark's size. A bundler that honors the `browser` export
|
|
489
445
|
condition loads the Catalog's stylesheets and those of every component it renders
|
|
490
446
|
internally with the `@kerfjs/ui/catalog` import alone — no dependence on which other
|
|
@@ -502,7 +502,9 @@ spacing shorthand such as `gap="2xs"`, or pass a `CssLength` created by the
|
|
|
502
502
|
CSS-free `@kerfjs/ui/css-values` helpers: `space('m')`, `rem(0.25)`,
|
|
503
503
|
`lengthVar('--app-gap')`, or `calc(plus(rem(0.25), pct(10)))`. `plus` returns a
|
|
504
504
|
non-standalone `CssLengthExpression`; only `calc` turns it into a complete
|
|
505
|
-
length. `
|
|
505
|
+
length. `gap={{ column: 'none', row: 'm' }}` sets the two CSS gap axes
|
|
506
|
+
independently; each axis keeps the same typed spacing contract. `remify(18)` is
|
|
507
|
+
the runtime twin of source CSS's `remify(18px)`: it
|
|
506
508
|
takes a pixel count and returns a `CssLength` in rem (`1.125rem`) against the
|
|
507
509
|
same fixed 16px baseline. Raw CSS strings, and the source-only `remify(4px)`
|
|
508
510
|
string form, are not valid runtime props. The `CssLength` name intentionally includes percentages for UI dimension
|
|
@@ -61,6 +61,8 @@ Use `Row`, `Grid`, or `List` spacing shorthands for repeated sibling spacing: `g
|
|
|
61
61
|
`gap="m"`, and the other `UiSpaceName` values resolve to their matching
|
|
62
62
|
`--kui-space-*` token. Use `Spacer` for one deliberate empty width or height, or
|
|
63
63
|
pass `flex` to consume the remaining space along a flex parent's main axis.
|
|
64
|
+
Pass `gap={{ column: 'none', row: 'm' }}` when the axes need different values;
|
|
65
|
+
each axis accepts the same token or complete typed length as the scalar form.
|
|
64
66
|
The canonical rhythm is `none`, `2xs`, `xs`, `m`, and `l`; use `s` and `xl`
|
|
65
67
|
only as deliberate exceptions.
|
|
66
68
|
|
|
@@ -78,6 +80,7 @@ import { Row } from "@kerfjs/ui/row";
|
|
|
78
80
|
|
|
79
81
|
<List gap="xs">...</List>;
|
|
80
82
|
<Grid columns={3} gap="m">...</Grid>;
|
|
83
|
+
<Grid columns={3} gap={{ column: 'none', row: 'm' }}>...</Grid>;
|
|
81
84
|
<Row hAlign="full" vAlign="middle" gap="m">...</Row>;
|
|
82
85
|
<List gap={space("m")}>...</List>;
|
|
83
86
|
<List gap={calc(plus(rem(0.25), pct(10)))}>...</List>;
|
|
@@ -130,11 +133,17 @@ copyable reference compositions, not new monolithic components.
|
|
|
130
133
|
|
|
131
134
|
When presenting a component or recipe in a Catalog, use the authoritative
|
|
132
135
|
[Catalog demo authoring contract](./catalog.md#catalog-demo-authoring-contract)
|
|
133
|
-
rather than deriving preview structure
|
|
136
|
+
rather than deriving preview structure from these
|
|
134
137
|
selection entries.
|
|
135
138
|
|
|
136
139
|
## Missing recurring concepts
|
|
137
140
|
|
|
141
|
+
For arbitrary interactive help or popup content beside a control, use
|
|
142
|
+
[`openAnchoredSurface`](anchored-surface.md) with an accessible label; use
|
|
143
|
+
`openAnchoredSurfaceAt` for pointer coordinates. It owns the Kerf UI surface,
|
|
144
|
+
placement, focus, dismissal, and modal-dialog hosting while the app owns its
|
|
145
|
+
content and action policy. Use `PopupMenu` for a typed command list.
|
|
146
|
+
|
|
138
147
|
Kerf UI does not export a command-palette component. Do not invent a package
|
|
139
148
|
command-palette import.
|
|
140
149
|
The typed [application adapter example](./examples/command-palette-adapter.tsx)
|
|
@@ -558,6 +567,15 @@ menu. Call `closePopupMenu` for programmatic dismissal. The pointer anchor
|
|
|
558
567
|
retains Web Awesome's collision placement, focus management, submenu keyboard
|
|
559
568
|
navigation, and outside/Escape dismissal.
|
|
560
569
|
|
|
570
|
+
The dropdown's popup flips and shifts at viewport edges, limits its height to
|
|
571
|
+
the available space, and uses the browser top layer where supported. The app
|
|
572
|
+
supplies pointer coordinates and command policy; it does not need to calculate
|
|
573
|
+
menu offsets. For other anchored content, use Web Awesome `wa-tooltip` or
|
|
574
|
+
`wa-popover` when its interaction fits, and `wa-popup` when only placement is
|
|
575
|
+
needed. `kerfjs/overlay` provides a structural popover or tooltip with its own
|
|
576
|
+
lifecycle for applications that do not use Web Awesome. These are distinct
|
|
577
|
+
interaction contracts: a command menu still uses `PopupMenu`.
|
|
578
|
+
|
|
561
579
|
## Configuring recurring list rows
|
|
562
580
|
|
|
563
581
|
Configure the List family instead of selecting its descendant classes. Use
|
package/docs/layout.md
CHANGED
|
@@ -44,6 +44,10 @@ inline edge then sits 16px from it. The default is `false`. This extra padding
|
|
|
44
44
|
adds to any device safe-area inset; a header or footer still owns the safe-area
|
|
45
45
|
padding at its edge while the content keeps its extra 8px. The pane's header,
|
|
46
46
|
footer, background, and separators still reach their usual edges.
|
|
47
|
+
For opaque edge-to-edge content, set `--kui-pane-scrollbar-gutter: stable` on the
|
|
48
|
+
Pane or an ancestor. This reserves a vertical scrollbar gutter so a child that
|
|
49
|
+
bleeds through the inline padding stops before the scroll indicator. The default
|
|
50
|
+
is `auto`; the optional gutter is outside the published content inset values.
|
|
47
51
|
|
|
48
52
|
```tsx
|
|
49
53
|
<Pane deepInset>
|
|
@@ -65,6 +69,8 @@ draws the standard focus ring. Set `outlined` to keep that ring visible as an
|
|
|
65
69
|
application-controlled state, such as a drop target, whether or not the Pane is
|
|
66
70
|
focused. `PaneConfig` forwards both options through layouts that render a Pane.
|
|
67
71
|
Give a focusable Pane a useful `label` when it represents a distinct region.
|
|
72
|
+
The ring paints above edge-to-edge child surfaces without intercepting their
|
|
73
|
+
buttons, selects, or nested scrolling.
|
|
68
74
|
|
|
69
75
|
### Lowered work surfaces
|
|
70
76
|
|
|
@@ -376,8 +382,12 @@ The two relationships that get confused most are 8px vs 24px: 8px is _inside_ a
|
|
|
376
382
|
group, 24px is _between_ major, differing regions. 16px sits between them for
|
|
377
383
|
same-kind groups.
|
|
378
384
|
|
|
379
|
-
For `Row.gap`, `Grid.gap`, and `List.gap`, pass
|
|
380
|
-
`
|
|
385
|
+
For `Row.gap`, `Grid.gap`, and `List.gap`, pass one token name (`gap="xs"`,
|
|
386
|
+
`gap="m"`) to use it on both axes, or pass
|
|
387
|
+
`gap={{ column: 'none', row: 'm' }}` to set each axis independently. Each axis
|
|
388
|
+
also accepts a complete typed `CssLength`, including `space('xs')` from
|
|
389
|
+
`@kerfjs/ui/css-values`. A List is a single vertical column, so its row gap
|
|
390
|
+
separates items; its column gap is exposed for the same CSS axis contract.
|
|
381
391
|
Use `rem`, `em`, `px`, `pct`, `lengthVar`, and `calc(plus(...))` only when a
|
|
382
392
|
named spacing relationship does not express the requirement; do not pass raw
|
|
383
393
|
CSS strings.
|
|
@@ -523,6 +533,13 @@ import { px } from "@kerfjs/ui/css-values";
|
|
|
523
533
|
<label>Unit <input /></label>
|
|
524
534
|
</Grid>;
|
|
525
535
|
|
|
536
|
+
<Grid columns={2} gap={{ column: 'none', row: 'm' }}>
|
|
537
|
+
<DocumentTile />
|
|
538
|
+
<DocumentTile />
|
|
539
|
+
<DocumentTile />
|
|
540
|
+
<DocumentTile />
|
|
541
|
+
</Grid>;
|
|
542
|
+
|
|
526
543
|
<Grid minColumnWidth={px(376)} gap="m">
|
|
527
544
|
<label>Provider <input /></label>
|
|
528
545
|
<label>Endpoint <input /></label>
|
package/docs/nav-stack.md
CHANGED
|
@@ -138,9 +138,17 @@ When a remembered control was removed during the controlled rerender, the same
|
|
|
138
138
|
fallback order applies. Disposing the helper stops future transitions and
|
|
139
139
|
cleans up any temporary fallback `tabindex` it added.
|
|
140
140
|
|
|
141
|
-
##
|
|
141
|
+
## Separators and scroll dividers
|
|
142
142
|
|
|
143
|
-
|
|
143
|
+
`separator="auto"` (the default) draws a line under the top chrome and above
|
|
144
|
+
the optional bottom toolbar when the active view's Pane has
|
|
145
|
+
`appearance="sunken"`, even if its content fits and no scroll wiring runs. The
|
|
146
|
+
line follows the active view on push and pop. Set `separator="hidden"` to hide
|
|
147
|
+
both stack-owned edges, including lines requested by `chromeDividers`. The
|
|
148
|
+
existing 1px edges change color without moving the content. A view's own Pane
|
|
149
|
+
header and footer separators remain controlled by that Pane.
|
|
150
|
+
|
|
151
|
+
For a flat active view, the stack draws no permanent line. With
|
|
144
152
|
`wireScrollDividers(appRoot)` installed (see
|
|
145
153
|
[Layout › Scroll dividers](layout.md#scroll-dividers)), the top chrome shows a
|
|
146
154
|
line along its bottom edge only while the active view's content is scrolled
|
|
@@ -153,10 +161,11 @@ the line keys on whichever actually scrolls — the view, or the Pane's content
|
|
|
153
161
|
and a Pane header or footer draws its own boundary instead. An explicit
|
|
154
162
|
`toolbarConfig.dividerSides` still draws a permanent Toolbar edge.
|
|
155
163
|
|
|
156
|
-
`chromeDividers` configures both lines, with the same values as a `Pane`'s:
|
|
164
|
+
`chromeDividers` configures both lines for flat views, with the same values as a `Pane`'s:
|
|
157
165
|
`scroll` (the default) follows the scroll state above and draws nothing until
|
|
158
166
|
the wiring runs; `always` keeps both lines, with or without the wiring; `none`
|
|
159
|
-
drops them even when the wiring reports scroll state.
|
|
167
|
+
drops them even when the wiring reports scroll state. A sunken active Pane still
|
|
168
|
+
shows the separator unless `separator="hidden"`. This option does not change the
|
|
160
169
|
top Toolbar's own `dividerSides`.
|
|
161
170
|
|
|
162
171
|
```tsx
|
package/docs/tab-navigator.md
CHANGED
|
@@ -144,9 +144,16 @@ paint the lowered surface behind short or long content. For a scene whose
|
|
|
144
144
|
only child is a `Pane` or `NavStack`, set appearance on that child's scroll
|
|
145
145
|
owner. See [Lowered work surfaces](layout.md#lowered-work-surfaces).
|
|
146
146
|
|
|
147
|
-
##
|
|
147
|
+
## Bar separator and scroll divider
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
`separator="auto"` (the default) draws the bar's top edge when the active scene
|
|
150
|
+
has `appearance: "sunken"`. It also follows the sunken appearance of a sole
|
|
151
|
+
`Pane`, or the active `Pane` view of a sole `NavStack`, inside that scene. The
|
|
152
|
+
edge stays visible when the content fits and as the active tab or nested view
|
|
153
|
+
changes. `separator="hidden"` suppresses the top line, including any scroll
|
|
154
|
+
divider or `chromeDividers="always"` line.
|
|
155
|
+
|
|
156
|
+
For a flat active scene, the bar has no permanent top line. With `wireScrollDividers(appRoot)`
|
|
150
157
|
installed (see [Layout › Scroll dividers](layout.md#scroll-dividers)), it shows
|
|
151
158
|
a line along its top edge only while the active scene's content continues below
|
|
152
159
|
it — not at the scroll end and not when the content fits. The line keys on
|
|
@@ -156,10 +163,11 @@ own bottom toolbar sits between them, so that toolbar's line applies instead.
|
|
|
156
163
|
The line is the bar's always-present 1px top border, colored only while shown,
|
|
157
164
|
so it never moves the bar or a tab.
|
|
158
165
|
|
|
159
|
-
`chromeDividers` configures
|
|
166
|
+
`chromeDividers` configures the scroll divider, with the same values as a `Pane`'s: `scroll`
|
|
160
167
|
(the default) follows the scroll state and draws nothing until the wiring runs;
|
|
161
168
|
`always` keeps the line, with or without the wiring; `none` drops it even when
|
|
162
|
-
the wiring reports content below.
|
|
169
|
+
the wiring reports content below. It does not suppress the sunken-surface
|
|
170
|
+
separator; use `separator="hidden"` for that.
|
|
163
171
|
|
|
164
172
|
## Tab badges
|
|
165
173
|
|
package/docs/type-contracts.md
CHANGED
|
@@ -121,6 +121,8 @@ move errors into casts without making the integration safer.
|
|
|
121
121
|
- Replace raw `List.gap` strings with a direct spacing shorthand (`"xs"`,
|
|
122
122
|
`"m"`) or a complete value from `@kerfjs/ui/css-values`. Replace
|
|
123
123
|
`gap="0.25rem"` with `gap={rem(0.25)}` and wrap sums with `calc(plus(...))`.
|
|
124
|
+
`Row`, `Grid`, and `List` also accept `gap={{ column: 'none', row: 'm' }}`;
|
|
125
|
+
each axis uses the same typed value contract.
|
|
124
126
|
- Replace raw `List.flex` shorthands with `flex(grow, shrink, basis)` or a finite
|
|
125
127
|
keyword; replace raw Skeleton dimensions with `px`/`rem`/`em`/`pct` builders;
|
|
126
128
|
and replace raw choice colors with a foreground token such as
|
package/docs/ux-demo.md
CHANGED
|
@@ -22,20 +22,20 @@ deterministic state, and semantic layout owners from the [recipe guide](./recipe
|
|
|
22
22
|
|
|
23
23
|
- It imports public component subpaths, exercising their browser-selected reachable CSS rather than a catalog-only style path.
|
|
24
24
|
- The `ListActionRow` route demonstrates sibling primary/trailing native controls, independent disabled states, controlled selection, first-line leading-icon alignment for wrapped labels, and an application-owned native popover relationship without nesting controls. The `ListItem` route demonstrates the same multiline leading-icon anchor alongside its single-control navigation contract.
|
|
25
|
-
- The `PopupMenu` route's context example opens one app-owned menu either at the row's right-click point or below its adjacent, keyboard-operable Demand actions button.
|
|
25
|
+
- The `PopupMenu` route's context example opens one app-owned menu either at the row's right-click point or below its adjacent, keyboard-operable Demand actions button. The Nested decisions example shows the automatic phone-width submenu placement above or below its parent list, with native ArrowLeft return, Escape dismissal, and child selection.
|
|
26
26
|
- The `AppTab` and `TabBar` routes are focused component specimens: AppTab shows its selected, compact, segmented, icon-only, truncation, placeholder, pending, metadata, and close-icon axes inside the required tablist context; TabBar shows its rail, segmented, inspector, allocation, placement, and accessory-zone axes, including an adjacent action plus a separately pinned end action whose tabs overflow at narrow widths, without application readouts. The pinned leading-tab specimen uses native strip scrolling so momentum and the final tab's end position remain natural; `snapTabs` stays an opt-in for applications that want whole-tab settling. The separate `Application tabs` composition route demonstrates controlled selection, matching panel content, overflow, adding (a new tab opens `pending` with a spinner and stays selectable while its panel shows a `Skeleton` heading until a simulated load completes), closing, keyboard/pointer reordering, and application-owned ordering. The `ResizableRegion` route likewise replaces only the decorative handle glyph while retaining separator semantics and shared wiring.
|
|
27
27
|
- Every first-party visual component and every free Web Awesome 3.12 component has its own stable `?component=<id>` URL and focused demo. Composite header, menu, and feedback scenarios remain addressable alongside their subcomponents; the root opens the first Kerf component. The former aggregate Web Awesome theme specimen is intentionally omitted because its component families are covered by those focused routes.
|
|
28
28
|
- `ai/component-catalog.json` is the canonical catalog metadata. A deterministic checked-in projection supplies the demo's unique id, category, kind (`component`, `composition`, or `recipe`), source (`kerf` or `webawesome`), name, description, recommendation, direct `uses` dependencies, existing documentation path, and demo-source path; first-party, recipe, and Web Awesome renderer maps are exhaustively typed against its generated id unions. Functional groups follow their authored importance order; components precede compositions inside each group, equal-importance entries use alphabetical order, and the Recipes group comes last. Composition names stay concise and project a visible `Composition` sidebar tag. Non-supported Web Awesome recommendations project to a visible `Discouraged` tag, with Popup as the deliberate conditional exception. Demo paths resolve to the matching `ui/ux-demo/demos/<id>.tsx` or `ui/ux-demo/recipes/*.tsx` file, or to `ui/ux-demo/webawesome-demos.tsx` for Web Awesome entries. `ui/ux-demo/main.tsx` owns only the catalog shell, shared state, routing, and interaction wiring. First-party component entries additionally derive `ui/src/<public-subpath>.tsx` from their canonical `delivery.browserImport` or opt-in `delivery.moduleImport`.
|
|
29
29
|
- Every detail footer groups compact `Demo source` and `Guidance` actions; first-party component details also expose `Component source`. Web Awesome details say `Integration guidance` to distinguish the local integration contract from the upstream component implementation. Each button retains its repository-relative path in the DOM for verification and uses a deploy-safe absolute GitHub blob URL with a contextual new-tab name; the demo does not depend on a local source server or add a package runtime export. At phone widths, the resource group and related-component selector stack as full-width rows, with any resource overflow contained locally so labels cannot overlap the selector or widen the document.
|
|
30
30
|
- The master/detail shell groups first-party routes by category with production `ListHeader` and `ListItem` components. Catalog entries can supply short reusable trailing tags; the Web Awesome projection uses them to label exactly the 15 discouraged choices. The 70 ecosystem components live in a separate `Web Awesome` disclosure with seven subgroups, so they remain discoverable without being presented as first-class `@kerfjs/ui` exports. Direct Web Awesome routes open the disclosure and reveal the selected row at wide sizes; narrow layouts keep the expanded list in document flow rather than introducing a competing nested scroller.
|
|
31
|
-
- The shell itself uses `@kerfjs/ui/layout.css`: sidebars, details, previews, dialogs, and resize specimens share unpadded panes plus self-contained content items. Its sidebar identity uses the Kerf logo beside a vertically centered title, with supporting copy in its own aligned row; the current-view heading is also a toolbar. The shared Catalog sidebar includes a filter for entry and heading names that works across first-party and ecosystem sections, including collapsed sections. Collapsing the catalog removes the pane completely and moves its restore action to the main toolbar's leading edge instead of leaving an icon-only rail. The selected view description remains inset below the page toolbar. The checkerboard belongs to
|
|
31
|
+
- The shell itself uses `@kerfjs/ui/layout.css`: sidebars, details, previews, dialogs, and resize specimens share unpadded panes plus self-contained content items. Its sidebar identity uses the Kerf logo beside a vertically centered title, with supporting copy in its own aligned row; the current-view heading is also a toolbar. The shared Catalog sidebar includes a filter for entry and heading names that works across first-party and ecosystem sections, including collapsed sections. Collapsing the catalog removes the pane completely and moves its restore action to the main toolbar's leading edge instead of leaving an icon-only rail. The selected view description remains inset below the page toolbar. The preview stage sits directly in the sunken main Pane, with its canvas padded by the medium spacing token. The checkerboard belongs to that stage, rather than a viewport-height wrapper or nested card, and one explicitly repeated SVG tile keeps the pattern continuous through below-fold content. A lightly separated footer keeps live status above compact source, guidance, and relationship groups. The focused `ResizableRegion` specimen stretches its surface through the available stage height and reports committed width in that shared status footer; narrow layouts retain local horizontal access when the pane is resized wider than the stage, and the split remains readable at 200% root scaling. Geometry coverage checks wide, intermediate, narrow, and 200% zoom layouts.
|
|
32
32
|
- The composer-form recipe demonstrates one coherent visible form surface with toolbar title/supporting-copy hierarchy, fields and actions on the shared 8px control gutter, and 24px major rhythm. Its conditional error or success `StateBanner` is the only nested semantic surface. Reset synchronizes the upgraded Web Awesome fields' live value properties with their empty controlled attributes and announces `Draft reset`; browser coverage preserves behavior and verifies wide, narrow, dark, validation, success, reset, 200% zoom, and forced-colors states.
|
|
33
33
|
- The list-detail dialog recipe is laid out like a `Workbench` on roomy devices: a headerless `DialogSurface` modal whose `SplitView` holds a full-height list sidebar `Pane` with its own toolbar beside a full-height detail `Pane` that carries the primary xl title, the `data-dialog="close"` control, and the record actions in its footer. Compact devices get a full-screen sheet with its own labeled header, footer actions, and a list-to-detail drill-down. Browser geometry coverage protects those relationships at wide and narrow widths.
|
|
34
34
|
- Catalog-only brand marks, preview dimensions, checkerboard tiles, and the compound display-settings control retain local geometry because they are specimen chrome or intrinsic control anatomy rather than reusable application spacing roles.
|
|
35
35
|
- The menu composition route demonstrates toolbar/content/footer anatomy, 24px major gaps, 8/1/8 child geometry, the full-width `ListHeader` count-or-badge/action split, logical-end 44px action targets with 18px visuals, and 44px row/header/toolbar targets. Its one chevron is a production 18px `DisclosureArrow` inside a controlled Tools header that actually reveals and hides content; ordinary Projects navigation has no false disclosure affordance. Browser assertions cover stable naming, expanded/content transitions, visual rotation, node identity, reduced motion, RTL alignment and containment, and wide, narrow, dark, and 200% layouts. The focused ListHeader route separately shows the shared neutral count pill for Attachments, Notes (including zero), and Duplicates plus a non-count legacy badge, full-width logical-end actions, and no disclosure without content.
|
|
36
36
|
- The `DisclosureArrow` route renders the production 18px root-scaled default without a catalog-only size override. Its two native buttons independently control the default right-to-down chevron and a recognizable right-facing replacement arrow with left-to-up custom directions; both use shortest-path rotation and their names stay stable while `aria-expanded` communicates state. Browser coverage verifies the custom clockwise path, pointer and native keyboard activation, focus, narrow and 200%-zoom stacking with contained labels, consumer sizing, and the independent `.5` Web Awesome glyph scale retained by Kerf `Select`.
|
|
37
37
|
- A detail with relationships renders one content-fitted `Components` footer selector: `Uses` links to direct dependencies and `Used by` links to reverse dependents. Its popup owns the standard surface inset so headings and even long component names stay clear of the menu border. Choosing either navigates to that component's demo. The relationship group is absent when both sets are empty; no duplicate summary is rendered.
|
|
38
|
-
- Focused demos ship no local stylesheets, inline styles, or styling-only class names. They compose Kerf components first, Web Awesome surfaces where Kerf has no equivalent, and ordinary semantic HTML for the remainder. The catalog-owned `CatalogExample.viewport` contract supplies the few specimen constraints needed to demonstrate layout behavior. Recipes follow the same rule: they ship no stylesheets, inline styles, or styling-only classes, and the catalog frames each one in the `CatalogExample` viewport its exported `presentation` declares (full-width application arrangements span the canvas measure; narrower recipes sit in the
|
|
38
|
+
- Focused demos ship no local stylesheets, inline styles, or styling-only class names. They compose Kerf components first, Web Awesome surfaces where Kerf has no equivalent, and ordinary semantic HTML for the remainder. The catalog-owned `CatalogExample.viewport` contract supplies the few specimen constraints needed to demonstrate layout behavior. Recipes follow the same rule: they ship no stylesheets, inline styles, or styling-only classes, and the catalog frames each one in the `CatalogExample` viewport its exported `presentation` declares (full-width application arrangements span the canvas measure; narrower recipes sit in the left-aligned example column). Demo-only CSS is limited to the document-shell `style.css`, which carries no recipe rules. Decorative chrome and Web Awesome controls share the same semantic system palette instead of carrying a separate demo identity.
|
|
39
39
|
- Scenarios use deterministic data and cover real variants, long content, disabled state, selection, progress, feedback, overlays, media, helpers, and stateful interactions. Individual Web Awesome action, form, layout, navigation, feedback, media, and helper routes provide focused theme specimens without a redundant aggregate route; ListItem and ListHeader demonstrate typed domain metadata, protected native button/disclosure semantics, and a native popover trigger relationship without product props; the Headers composition demonstrates direct toolbar headings with an extra-large title, optional supporting copy, and grouped icon/actions; the Select route verifies that option icons survive unrelated Kerf rerenders and selected slot content follows value changes; the Animation route exposes preset, easing, duration, playback-rate, and transport controls with live lifecycle status; observer routes provide user-driven intersection, mutation, and resize events with live results; ToolbarControlGroup exposes all eight Hot Sheet 2 variants, loads the Web Awesome registrations needed by its popup specimen, and preserves the upgraded dropdown's managed light-DOM children across Kerf rerenders; the Toolbar route composes a collapsible TokenSearchField inside a group, animates between the empty iconic and complete field states, stays open while populated, and focuses the revealed editor; SegmentedControl covers toolbar, rounded-rectangle, pill, equal-width, and disabled presentations; TokenSearchField covers editable ordered chips, pointer and keyboard removal, caret-preserving controlled deletion, editing, clearing, free text, disabled state, and fixed first-line alignment while content wraps; StateBanner exposes every built-in tone with tone-tinted badges, a scoped override, and a placeholder with and without a badge, the badged placeholder in every tone so captures cover the solid badge skeleton; the Application tabs composition uses a Web Awesome card with semantic header/footer slots and Kerf `Text` while demonstrating overflow, edge autoscroll, and controlled pointer/keyboard reordering.
|
|
40
40
|
- The ToolbarControlGroup route also demonstrates the public compact/tight,
|
|
41
41
|
mixed-content, filled-selection, nested-dropdown, and avatar/scrim contracts
|
|
@@ -48,7 +48,7 @@ deterministic state, and semantic layout owners from the [recipe guide](./recipe
|
|
|
48
48
|
collapsible-search specimen follows the selected group shape with a
|
|
49
49
|
concentric inner radius in both collapsed and expanded states.
|
|
50
50
|
- Recipe ownership notes are hidden by default. A page-toolbar toggle reveals each recipe's note as its `CatalogExample` note above the framed specimen, so instructional copy never covers the composition.
|
|
51
|
-
- Settings exercise light/dark, increased contrast, and reduced motion without changing component code. The theme action starts from the operating system's effective color scheme, always names the appearance it will switch to, and installs an explicit light or dark override when activated; contrast and reduced motion expose their independent preferences through `aria-pressed`.
|
|
51
|
+
- Settings exercise light/dark, increased contrast, and reduced motion without changing component code. The theme action starts from the operating system's effective color scheme, always names the appearance it will switch to, and installs an explicit light or dark override when activated; contrast and reduced motion expose their independent preferences through `aria-pressed`. The display-control group wraps whole independent controls at narrow widths so all stay reachable. The adjacent Background menu chooses an 8px-square checkerboard (default), 8px vertical stripes, a layout guide with single-pixel rules at 15px, 23px, and 32px, surface, or sunken for the preview stage. The demo saves all four display choices in browser storage and restores them across entries and reloads. A saved light or dark choice remains explicit when the operating-system appearance changes; without one, the theme follows the system. Invalid or unavailable storage falls back to defaults while the controls remain usable.
|
|
52
52
|
- The footer action log is an `aria-live` region, making interactions visible to both people and browser tests.
|
|
53
53
|
- The focused Toast route calls Web Awesome's documented programmatic stack API and notes that Hot Sheet 2 still uses its own app-level toast implementation; the catalog demonstrates ecosystem support without implying current product adoption.
|
|
54
54
|
- Browser coverage walks every Kerf, composition, and Web Awesome route, checks every listed Web Awesome custom element is registered in its focused specimen, exercises the ecosystem disclosure and both relationship directions, validates offline source/guidance hrefs and accessible wide/narrow/200%-zoom wrapping, and covers tab selection/reordering/overflow, semantic palettes and scoped overrides, light/dark rendering, toolbar popup and responsive find behavior, single/multiline search alignment, menu alignment, banner urgency, keyboard resizing, live custom-element values, and representative wide/narrow captures.
|
package/docs/workbench.md
CHANGED
|
@@ -82,6 +82,8 @@ outline is hidden until it expands. `mainPane` and a panel's `pane` forward the
|
|
|
82
82
|
same options to an inner Pane when that is the intended focus owner. Give
|
|
83
83
|
the central region `mainLabel` when it needs its own named landmark; panel
|
|
84
84
|
regions use their `label`.
|
|
85
|
+
When a sole Pane fills the main region, the Workbench ring paints above opaque
|
|
86
|
+
edge-to-edge Pane content without blocking pointer input in that content.
|
|
85
87
|
|
|
86
88
|
Each static `WorkbenchPanel` takes `content`, an optional `toolbar` and `footer` (see
|
|
87
89
|
[Panel toolbars](#panel-toolbars)), an optional `collapsed`, an optional
|
package/llms.txt
CHANGED
|
@@ -12,6 +12,9 @@ npm install kerfjs @kerfjs/ui # kerfjs is a peer; Web Awesome is an optional pee
|
|
|
12
12
|
Import each component from its own subpath (`@kerfjs/ui/list-item`, `@kerfjs/ui/toolbar`,
|
|
13
13
|
…); the browser build pulls that component's reachable CSS automatically, so do not
|
|
14
14
|
maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
|
|
15
|
+
For arbitrary interactive help or popup content, use `openAnchoredSurface` or
|
|
16
|
+
`openAnchoredSurfaceAt` from `@kerfjs/ui/anchored-surface`; see
|
|
17
|
+
[AnchoredSurface](./docs/anchored-surface.md). The app owns content and actions.
|
|
15
18
|
For a public subpath containing several visual components, use a folder with one
|
|
16
19
|
component per source file and one owned stylesheet per visual component. Keep its
|
|
17
20
|
compatibility entrypoints implementation-free: JavaScript/TypeScript only re-exports
|
|
@@ -395,6 +398,9 @@ headingLevel`, `TokenSearchField clearIcon`, `ContentItem frame="framed"` for a
|
|
|
395
398
|
or explicit `scroll`, `always`, `none`), and a `NavStack` or `TabNavigator`
|
|
396
399
|
with its own `chromeDividers` (same values; a `SplitView` forwards it through
|
|
397
400
|
`compactStack`); name app-owned chrome in `targets`.
|
|
401
|
+
A `TabNavigator` also takes `separator="auto" | "hidden"`: auto draws the
|
|
402
|
+
bar's top edge beside a sunken active scene or nested Pane even when content
|
|
403
|
+
fits; hidden suppresses that edge, including scroll dividers.
|
|
398
404
|
Never add a border, or a permanent `dividerSides`, to fake one.
|
|
399
405
|
While a side overlay is open (a Workbench rail overlay, a `wireSidebar` compact
|
|
400
406
|
overlay, an overlay `CollapsiblePanel` or horizontal `ResizableRegion`), the
|
|
@@ -524,9 +530,8 @@ shell, `@kerfjs/ui/collapsible-panel` (`CollapsiblePanel`, `CollapsiblePanelTogg
|
|
|
524
530
|
- `@kerfjs/ui/wire-sidebar` (`wireSidebar`: toggle, focus, compact overlay, persistence; a compact overlay starts closed and opens only on a user action, and `inlineCollapsed` declares a panel's inline default).
|
|
525
531
|
`@kerfjs/ui/catalog` is another opt-in shell — a
|
|
526
532
|
`Catalog` component + `wireCatalog` helper for building a component-gallery catalog
|
|
527
|
-
(collapsible category sidebar + preview stage + resources footer, with
|
|
528
|
-
|
|
529
|
-
reveal for long desktop sidebars) from your own
|
|
533
|
+
(collapsible category sidebar + preview stage + resources footer, with
|
|
534
|
+
focus-preserving active-row reveal for long desktop sidebars) from your own
|
|
530
535
|
entries; controlled/stateless like the layouts. For any Catalog demo, follow
|
|
531
536
|
the [authoritative authoring contract](./docs/catalog.md#catalog-demo-authoring-contract)
|
|
532
537
|
rather than inferring structure from the shell or private CSS.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kerfjs/ui",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.84",
|
|
4
4
|
"description": "Accessible, composable UI primitives for kerf applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -317,6 +317,11 @@
|
|
|
317
317
|
"browser": "./dist/browser/popup-menu.js",
|
|
318
318
|
"import": "./dist/popup-menu.js"
|
|
319
319
|
},
|
|
320
|
+
"./anchored-surface": {
|
|
321
|
+
"types": "./dist/anchored-surface.d.ts",
|
|
322
|
+
"browser": "./dist/browser/anchored-surface.js",
|
|
323
|
+
"import": "./dist/anchored-surface.js"
|
|
324
|
+
},
|
|
320
325
|
"./popup-menu/register": {
|
|
321
326
|
"types": "./dist/popup-menu-register.d.ts",
|
|
322
327
|
"import": "./dist/popup-menu-register.js"
|
|
@@ -390,6 +395,7 @@
|
|
|
390
395
|
"./loading-spinner.css": "./dist/styles/loading-spinner.css",
|
|
391
396
|
"./select.css": "./dist/styles/select.css",
|
|
392
397
|
"./popup-menu.css": "./dist/styles/popup-menu.css",
|
|
398
|
+
"./anchored-surface.css": "./dist/styles/anchored-surface.css",
|
|
393
399
|
"./skeleton.css": "./dist/styles/skeleton.css",
|
|
394
400
|
"./sunken-panel.css": "./dist/styles/sunken-panel.css",
|
|
395
401
|
"./segmented-control.css": "./dist/styles/segmented-control.css",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/layout/row/row.tsx"],"names":[],"mappings":";;;;;AAiBA,IAAM,sBAAA,uBAA6B,GAAA,CAAI;AAAA,EACrC,gBAAA;AAAA,EACA,cAAA;AAAA,EACA,cAAA;AAAA,EACA,WAAA;AAAA,EACA,WAAA;AAAA,EACA,WAAA;AAAA,EACA,kBAAA;AAAA,EACA;AACF,CAAC,CAAA;AAeD,IAAM,UAAA,GAAqC;AAAA,EACzC,MAAA;AAAA,EACA,KAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA;AACF,CAAA;AAgCO,SAAS,GAAA,CAAI;AAAA,EAClB,QAAA;AAAA,EACA,MAAA,GAAS,MAAA;AAAA,EACT,MAAA,GAAS,MAAA;AAAA,EACT,GAAA,GAAM,IAAA;AAAA,EACN,IAAA,GAAO,KAAA;AAAA,EACP,IAAA,GAAO,KAAA;AAAA,EACP,IAAA,GAAO,KAAA;AAAA,EACP,UAAA,GAAa,EAAA;AAAA,EACb,aAAA,GAAgB,EAAA;AAAA,EAChB,SAAA,GAAY,EAAA;AAAA,EACZ,iBAAiB,EAAC;AAAA,EAClB;AACF,CAAA,EAAa;AACX,EAAA,MAAM,kBAAA,GAAqB,oBAAA;AAAA,IACzB,cAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,MAAM,WAAW,UAAA,CAAW,QAAA,CAAS,GAAkB,CAAA,GACnD,KAAA,CAAM,GAAkB,CAAA,GACxB,GAAA;AACJ,EAAA,MAAM,SAAA,GAAY,IAAA,KAAS,IAAA,GAAO,UAAA,GAAa,IAAA,IAAQ,MAAA;AACvD,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,kBAAkB,QAAQ,CAAA,CAAA;AAAA,IAC1B,SAAA,GAAY,CAAA,gBAAA,EAAmB,SAAS,CAAA,CAAA,GAAK;AAAA,GAC/C,CACG,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAEX,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACE,GAAG,kBAAA;AAAA,MACJ,KAAA,EAAO,CAAA,QAAA,EAAW,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK;AAAA,MACnC,gBAAA,EAAe,KAAA;AAAA,MACf,cAAA,EAAc,oBAAoB,MAAM,CAAA;AAAA,MACxC,cAAA,EAAc,kBAAkB,MAAM,CAAA;AAAA,MACtC,WAAA,EAAW,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAC,CAAA;AAAA,MAC/B,WAAA,EAAW,OAAO,MAAA,GAAS,MAAA;AAAA,MAC3B,WAAA,EAAW,OAAO,IAAI,CAAA;AAAA,MACtB,oBAAkB,UAAA,IAAc,MAAA;AAAA,MAChC,uBAAqB,aAAA,IAAiB,MAAA;AAAA,MACtC,KAAA;AAAA,MACA,IAAA;AAAA,MAEC;AAAA;AAAA,GACH;AAEJ","file":"chunk-5D3WDMIK.js","sourcesContent":["import type { KerfUiContent } from '../../../shared/content/semantic-content.js';\nimport { filterDataAttributes } from '../../../shared/dom/extension-attributes.js';\nimport {\n type HorizontalAlignment,\n horizontalAlignment,\n type VerticalAlignment,\n verticalAlignment,\n} from '../../../shared/layout/flex-alignment.js';\nimport type { Sides } from '../../../shared/layout/sides.js';\nimport {\n type CssFlex,\n type CssFlexKeyword,\n type CssLength,\n space,\n type UiSpaceName,\n} from '../../../shared/styles/css-values.js';\n\nconst rowProtectedAttributes = new Set([\n 'data-component',\n 'data-h-align',\n 'data-v-align',\n 'data-flex',\n 'data-fill',\n 'data-wrap',\n 'data-text-insets',\n 'data-control-insets',\n]);\n\ntype RowRootAttributes = Readonly<\n Record<`data-${string}`, string | undefined> & {\n 'data-component'?: never;\n 'data-h-align'?: never;\n 'data-v-align'?: never;\n 'data-flex'?: never;\n 'data-fill'?: never;\n 'data-wrap'?: never;\n 'data-text-insets'?: never;\n 'data-control-insets'?: never;\n }\n>;\n\nconst spaceNames: readonly UiSpaceName[] = [\n 'none',\n '2xs',\n 'xs',\n 's',\n 'm',\n 'l',\n 'xl',\n];\n\nexport interface RowProps {\n children?: KerfUiContent;\n /** Horizontal distribution. Defaults to left. */\n hAlign?: HorizontalAlignment;\n /** Vertical alignment and wrapped-line distribution. Defaults to full. */\n vAlign?: VerticalAlignment;\n /** A named UI spacing token or typed CSS length. Defaults to xs. */\n gap?: UiSpaceName | CssLength;\n /** Allow this row to grow/shrink, use a keyword, or supply a typed CSS flex shorthand. */\n flex?: boolean | CssFlexKeyword | CssFlex;\n /**\n * Fill the height of a parent with a definite height, such as an app root or\n * a fixed-height frame, when this row is that parent's layout root. Inside a\n * flex layout use `flex` instead. Defaults to false.\n */\n fill?: boolean;\n /** Allow children to wrap onto additional lines. */\n wrap?: boolean;\n /** Physical sides that receive the standard 17px text inset. */\n textInsets?: Sides;\n /** Physical sides that receive the standard 8px control inset. Text insets win on overlap. */\n controlInsets?: Sides;\n className?: string;\n /** Safe `data-*` metadata; Row-owned structural attributes remain protected. */\n rootAttributes?: RowRootAttributes;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\n/** A horizontal flex row with explicit physical-axis alignment and spacing. */\nexport function Row({\n children,\n hAlign = 'left',\n vAlign = 'full',\n gap = 'xs',\n flex = false,\n fill = false,\n wrap = false,\n textInsets = '',\n controlInsets = '',\n className = '',\n rootAttributes = {},\n slot,\n}: RowProps) {\n const safeRootAttributes = filterDataAttributes(\n rootAttributes,\n rowProtectedAttributes,\n );\n const gapValue = spaceNames.includes(gap as UiSpaceName)\n ? space(gap as UiSpaceName)\n : gap;\n const flexValue = flex === true ? '1 1 auto' : flex || undefined;\n const style = [\n `--_kui-row-gap:${gapValue}`,\n flexValue ? `--_kui-row-flex:${flexValue}` : '',\n ]\n .filter(Boolean)\n .join(';');\n\n return (\n <div\n {...safeRootAttributes}\n class={`kui-row ${className}`.trim()}\n data-component=\"row\"\n data-h-align={horizontalAlignment(hAlign)}\n data-v-align={verticalAlignment(vAlign)}\n data-flex={String(Boolean(flex))}\n data-fill={fill ? 'true' : undefined}\n data-wrap={String(wrap)}\n data-text-insets={textInsets || undefined}\n data-control-insets={controlInsets || undefined}\n style={style}\n slot={slot}\n >\n {children}\n </div>\n );\n}\n\nexport type {\n HorizontalAlignment,\n VerticalAlignment,\n} from '../../../shared/layout/flex-alignment.js';\nexport type { Sides } from '../../../shared/layout/sides.js';\nexport type {\n CssFlex,\n CssFlexKeyword,\n CssLength,\n UiSpaceName,\n} from '../../../shared/styles/css-values.js';\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/navigation/nav-stack/nav-stack.tsx"],"names":[],"mappings":"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","file":"chunk-CVKIADIS.js","sourcesContent":["import { ChevronLeft } from 'lucide';\n\nimport type { KerfUiContent } from '../../../shared/content/semantic-content.js';\nimport type { NavPane } from '../../../shared/panels/nav-pane.js';\nimport type { PanelBottomToolbar } from '../../../shared/panels/panel-toolbar.js';\nimport { Toolbar, type ToolbarConfig } from '../../actions/toolbar/toolbar.js';\nimport { ToolbarControlGroup } from '../../actions/toolbar-control-group/toolbar-control-group.js';\nimport {\n type HeadingLevel,\n ToolbarText,\n type ToolbarTextSize,\n} from '../../actions/toolbar-text/toolbar-text.js';\nimport { List, type ListConfig } from '../../collections/list/list.js';\nimport { Pane } from '../../layout/pane/pane.js';\nimport { LucideIcon } from '../../media/lucide-icon/lucide-icon.js';\n\n/**\n * One entry in a {@link NavStack}. The app owns the stack as an array (usually a\n * signal); `NavStack` renders it and `wireNavStack` animates the transitions.\n */\nexport interface NavStackView extends NavPane<\n NavStackViewToolbar,\n PanelBottomToolbar\n> {\n /** Stable identity for keyed reconcile and transition direction. */\n key: string;\n}\n\n/** Per-view top toolbar. The stack supplies its back control before `leading`. */\nexport interface NavStackViewToolbar extends NavStackToolbarConfig {\n title?: string;\n leading?: KerfUiContent;\n center?: KerfUiContent;\n trailing?: KerfUiContent;\n}\n\n/** A view's structured bottom toolbar; it cross-fades on navigation. */\nexport type NavStackViewBottomToolbar = PanelBottomToolbar;\n\n/**\n * The top toolbar's configuration. Its `ToolbarConfig` forwards to the real\n * `Toolbar` the stack renders; by default that toolbar draws no divider and\n * claims the top and side safe-area edges the stack still touches.\n */\nexport interface NavStackToolbarConfig extends ToolbarConfig {\n /** Accessible name of the top toolbar (default: none). */\n label?: string;\n /** Size of the view title's `ToolbarText` (default `large`). */\n titleSize?: ToolbarTextSize;\n /** Expose the view title as a heading at this level (default: a plain span). */\n headingLevel?: HeadingLevel;\n}\n\nexport interface NavStackProps {\n id: string;\n /** Accessible name for the stack region. */\n label: string;\n /** The stack, root first; the last entry is the active top view. */\n views: NavStackView[];\n /** Accessible label for the icon-only back control (default \"Back\"). */\n backLabel?: string;\n /** The back control's icon (default a chevron-left `LucideIcon`). */\n backIcon?: KerfUiContent;\n /**\n * Visible text beside the back icon, such as the previous view's title\n * (default: icon only). When set, the text names the control and\n * `backLabel` is not used.\n */\n backText?: string;\n /** The top toolbar's configuration, forwarded to its `Toolbar`. */\n toolbarConfig?: NavStackToolbarConfig;\n /** Persistent last group in every view's top toolbar, supplied by a hosting panel. */\n panelToggle?: KerfUiContent;\n /** Hide the top toolbar entirely (rare — a fully custom-chrome view). */\n hideToolbar?: boolean;\n /** Optional persistent bottom toolbar used when the active view does not provide one. */\n bottomToolbar?: NavStackViewBottomToolbar;\n /**\n * The line under the top chrome and over the bottom toolbar, where they\n * meet the active view. `scroll` (default) shows the chrome's line only\n * while the view's content is scrolled beneath it, and the bottom\n * toolbar's only while more content lies below — never when the content\n * fits — once `wireScrollDividers` (`@kerfjs/ui/wire-scroll-dividers`) is\n * wired above the stack; unwired, neither shows. `always` shows both\n * without the wiring; `none` neither, even when wired. The line is drawn\n * inside the chrome, so no state moves the chrome or the content. A\n * `toolbarConfig.dividerSides` edge is the Toolbar's own and is unaffected.\n */\n chromeDividers?: 'scroll' | 'always' | 'none';\n className?: string;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\nconst DEFAULT_SAFE_AREA_EDGES = [\n 'block-start',\n 'inline-start',\n 'inline-end',\n] as const;\n\nconst DEFAULT_BOTTOM_SAFE_AREA_EDGES = [\n 'block-end',\n 'inline-start',\n 'inline-end',\n] as const;\n\nfunction chromeList(content: KerfUiContent, config?: ListConfig) {\n return (\n <List\n dividerSides={config?.dividerSides}\n gap={config?.gap}\n hAlign={config?.hAlign}\n vAlign={config?.vAlign}\n textInsets={config?.textInsets}\n controlInsets={config?.controlInsets}\n >\n {content}\n </List>\n );\n}\n\n/**\n * A navigation stack (iOS-style push/pop). Renders every entry stacked, the last\n * one active; `@kerfjs/ui/wire-nav-stack`'s `wireNavStack` slides the content and\n * cross-fades the chrome across a change. Its top chrome is a real `Toolbar`: the\n * back control and title lead, the active view's `toolbar` fills the zones,\n * and `toolbarConfig` supplies defaults. A single-pane\n * layout is a `NavStack` with one entry. See `docs/23-app-layouts.md` §3.1.\n */\nexport function NavStack({\n id,\n label,\n views,\n backLabel = 'Back',\n backIcon,\n backText,\n toolbarConfig = {},\n panelToggle,\n hideToolbar = false,\n bottomToolbar,\n chromeDividers = 'scroll',\n className = '',\n slot,\n}: NavStackProps) {\n const topIndex = views.length - 1;\n const top = views[topIndex];\n const topToolbar = top?.toolbar;\n const bottom = top?.bottomToolbar ?? bottomToolbar;\n const canPop = views.length > 1;\n return (\n <section\n class={`kui-nav-stack ${className}`.trim()}\n id={id}\n data-component=\"nav-stack\"\n data-nav-stack-id={id}\n data-depth={String(views.length)}\n data-chrome-dividers={\n chromeDividers === 'always' || chromeDividers === 'none'\n ? chromeDividers\n : undefined\n }\n aria-label={label}\n slot={slot}\n >\n {!hideToolbar && (\n <div class=\"kui-nav-stack__chrome\" data-nav-stack-chrome>\n <Toolbar\n label={topToolbar?.label ?? toolbarConfig.label}\n dividerSides={\n topToolbar?.dividerSides ?? toolbarConfig.dividerSides\n }\n centerAlign={topToolbar?.centerAlign ?? toolbarConfig.centerAlign}\n responsive={topToolbar?.responsive ?? toolbarConfig.responsive}\n responsiveAt={\n topToolbar?.responsiveAt ?? toolbarConfig.responsiveAt\n }\n safeAreaEdges={\n topToolbar?.safeAreaEdges ??\n toolbarConfig.safeAreaEdges ??\n DEFAULT_SAFE_AREA_EDGES\n }\n leading={\n <>\n {canPop && (\n <ToolbarControlGroup\n appearance=\"borderless\"\n shape=\"rounded\"\n content={backText ? 'mixed' : 'icon'}\n single\n >\n <button\n type=\"button\"\n class=\"kui-nav-stack__back\"\n data-nav-back\n aria-label={backText ? undefined : backLabel}\n >\n {backIcon ?? (\n <LucideIcon\n icon={ChevronLeft}\n name=\"chevron-left\"\n className=\"kui-nav-stack__back-icon\"\n />\n )}\n {backText ? <span>{backText}</span> : null}\n </button>\n </ToolbarControlGroup>\n )}\n {topToolbar?.leading}\n <ToolbarText\n text={topToolbar?.title ?? ''}\n size={\n topToolbar?.titleSize ?? toolbarConfig.titleSize ?? 'large'\n }\n headingLevel={\n topToolbar?.headingLevel ?? toolbarConfig.headingLevel\n }\n fill\n />\n </>\n }\n center={topToolbar?.center}\n trailing={\n <>\n {topToolbar?.trailing}\n {panelToggle}\n </>\n }\n />\n </div>\n )}\n <div class=\"kui-nav-stack__viewport\" data-nav-stack-viewport>\n {views.map((view, index) => (\n <article\n class=\"kui-nav-stack__view\"\n data-key={view.key}\n data-nav-key={view.key}\n data-nav-active={String(index === topIndex)}\n aria-hidden={String(index !== topIndex)}\n >\n {view.pane ||\n view.header !== undefined ||\n view.footer !== undefined ? (\n <Pane\n appearance={view.pane?.appearance}\n deepInset={view.pane?.deepInset}\n tabIndex={view.pane?.tabIndex}\n outlined={view.pane?.outlined}\n contentElement={view.pane?.contentElement}\n contentLabel={view.pane?.contentLabel}\n separators={view.pane?.separators}\n safeAreaEdges={view.pane?.safeAreaEdges}\n chromeDividers={view.pane?.chromeDividers}\n header={\n view.header === undefined ? undefined : (\n <div data-nav-stack-header>\n {chromeList(view.header, view.headerList)}\n </div>\n )\n }\n footer={\n view.footer === undefined ? undefined : (\n <div data-nav-stack-footer>\n {chromeList(view.footer, view.footerList)}\n </div>\n )\n }\n >\n {view.content}\n </Pane>\n ) : (\n view.content\n )}\n </article>\n ))}\n </div>\n {bottom && (\n <footer class=\"kui-nav-stack__bottom\" data-nav-stack-bottom>\n <Toolbar\n position=\"footer\"\n label={bottom.label}\n dividerSides={bottom.dividerSides}\n centerAlign={bottom.centerAlign}\n responsive={bottom.responsive}\n responsiveAt={bottom.responsiveAt}\n safeAreaEdges={\n bottom.safeAreaEdges ?? DEFAULT_BOTTOM_SAFE_AREA_EDGES\n }\n leading={bottom.leading}\n center={bottom.center}\n trailing={bottom.trailing}\n />\n </footer>\n )}\n </section>\n );\n}\n"]}
|