@ashley-shrok/viewmodel-shell 1.12.0 → 2.0.0
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/dist/browser.js +0 -23
- package/dist/index.d.ts +0 -22
- package/dist/tui.js +0 -2
- package/package.json +1 -1
- package/styles/default.css +1 -52
package/dist/browser.js
CHANGED
|
@@ -368,29 +368,6 @@ export class BrowserAdapter {
|
|
|
368
368
|
parent.appendChild(el);
|
|
369
369
|
return;
|
|
370
370
|
}
|
|
371
|
-
// 1.11.0 — flyout:true overlay disclosure. The hover/focus sibling of
|
|
372
|
-
// collapsible's inline <details>: heading => focusable <button> trigger,
|
|
373
|
-
// children => an absolutely-positioned panel revealed on :hover /
|
|
374
|
-
// :focus-within (pure CSS — see default.css; no JS, no listeners, no
|
|
375
|
-
// round-tripped open state). Precedence: collapsible (checked above) wins,
|
|
376
|
-
// so reaching here means collapsible is not set; flyout in turn wins over
|
|
377
|
-
// link/action below. Omitted/false renders byte-identical to <section>.
|
|
378
|
-
if (n.flyout === true) {
|
|
379
|
-
const el = document.createElement("div");
|
|
380
|
-
el.className = `vms-section vms-section--flyout${n.variant === "card" ? " vms-section--card" : ""}${n.layout && n.layout !== "stack" ? ` vms-section--${n.layout}` : ""}${n.arrange ? ` vms-arrange--${n.arrange}` : ""}${n.align ? ` vms-align--${n.align}` : ""}${n.threshold ? ` vms-switch--${n.threshold}` : ""}${n.limit ? ` vms-switch-limit--${n.limit}` : ""}${n.minItem ? ` vms-cards-min--${n.minItem}` : ""}`;
|
|
381
|
-
const trigger = document.createElement("button");
|
|
382
|
-
trigger.type = "button";
|
|
383
|
-
trigger.className = "vms-section__trigger";
|
|
384
|
-
// Headingless fallback label — mirrors collapsible's "Show details".
|
|
385
|
-
trigger.textContent = n.heading ?? "Menu";
|
|
386
|
-
el.appendChild(trigger);
|
|
387
|
-
const panel = document.createElement("div");
|
|
388
|
-
panel.className = "vms-section__panel";
|
|
389
|
-
this.kids(n.children, panel, on);
|
|
390
|
-
el.appendChild(panel);
|
|
391
|
-
parent.appendChild(el);
|
|
392
|
-
return;
|
|
393
|
-
}
|
|
394
371
|
// 1.5.0 — SectionNode.link URL-wrapper variant (issue #21). When set,
|
|
395
372
|
// emit a wrapping <a href> element instead of <section> so every native
|
|
396
373
|
// browser link affordance works for free (middle-click / Ctrl/Cmd-click
|
package/dist/index.d.ts
CHANGED
|
@@ -106,28 +106,6 @@ export interface SectionNode {
|
|
|
106
106
|
id?: string;
|
|
107
107
|
/** When true, the section renders as a native `<details>`/`<summary>` disclosure widget (closed by default). Aesthetic, client-side primitive — the open/closed state is DOM-local and the server does NOT round-trip it (same conceptual model as draft text values in unsubmitted form inputs). The browser adapter snapshots `<details>.open` before each re-render and restores it after, keyed by `id ?? heading ?? "vms-section-anon"` (disambiguated by per-render ordinal); a re-key drops the preserved state (the documented escape hatch for rare server-driven expansion). The summary label is the section's `heading`; a headingless collapsible section uses the fallback string `"Show details"`. If a section needs to start open, do not mark it collapsible. Omitted/false = today's `<section>` rendering, byte-identical. */
|
|
108
108
|
collapsible?: boolean;
|
|
109
|
-
/** When true, the section renders as an OVERLAY disclosure ("flyout") — the
|
|
110
|
-
* hover/focus sibling of `collapsible`'s inline `<details>` reveal. The
|
|
111
|
-
* `heading` becomes a focusable `<button class="vms-section__trigger">`
|
|
112
|
-
* trigger; the `children` are wrapped in a `<div class="vms-section__panel">`
|
|
113
|
-
* that is absolutely positioned and revealed on `:hover` / `:focus-within`
|
|
114
|
-
* (pure CSS — no JavaScript, no state machine). Hover (desktop), tap-to-focus
|
|
115
|
-
* (touch), and keyboard Tab-to-trigger (a11y) all reveal it for free; it hides
|
|
116
|
-
* on blur / pointer-leave. Unlike `collapsible`, the open state is EPHEMERAL
|
|
117
|
-
* (driven by `:hover`/`:focus-within`), so it is NOT round-tripped and NOT
|
|
118
|
-
* snapshotted across re-renders — there is nothing to preserve.
|
|
119
|
-
*
|
|
120
|
-
* Use a flyout (overlay) rather than `collapsible` (inline) when the revealed
|
|
121
|
-
* content should float over siblings instead of pushing them — e.g. a menu
|
|
122
|
-
* inside a `layout:"row"` bar, where an inline disclosure would shove the bar
|
|
123
|
-
* open. A headingless flyout uses the fallback trigger label `"Menu"`.
|
|
124
|
-
*
|
|
125
|
-
* Mutually exclusive with the other section modes; the renderer resolves a
|
|
126
|
-
* fixed precedence and never combines them: `collapsible` > `flyout` > `link`
|
|
127
|
-
* > `action`. So `collapsible: true` wins if both are set, and a flyout
|
|
128
|
-
* section ignores `link`/`action`. Omitted/false = today's `<section>`
|
|
129
|
-
* rendering, byte-identical (no class drift, no extra elements). */
|
|
130
|
-
flyout?: boolean;
|
|
131
109
|
/** Click-anywhere section dispatch primitive — mirrors `TableRow.action` (1.1.0)
|
|
132
110
|
* at the section level. When set, the renderer makes the entire section
|
|
133
111
|
* clickable AND keyboard-activatable (Enter / Space — Space preventDefaults
|
package/dist/tui.js
CHANGED
|
@@ -808,8 +808,6 @@ function layoutProps(layout, _sidebarFraction) {
|
|
|
808
808
|
return { flexDirection: "row", flexWrap: "wrap", gap: 1 };
|
|
809
809
|
case "row":
|
|
810
810
|
// 1.11.0 — left-aligned wrapping horizontal row; items hug content.
|
|
811
|
-
// (Section flyout has no TUI overlay; it degrades to a plain labeled
|
|
812
|
-
// section — SectionView ignores the flag and renders children inline.)
|
|
813
811
|
return { flexDirection: "row", flexWrap: "wrap", gap: 1 };
|
|
814
812
|
case "sidebar":
|
|
815
813
|
return { flexDirection: "row", gap: 1, flexGrow: 1, flexShrink: 1 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ashley-shrok/viewmodel-shell",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "A server-driven UI framework where the wire format is structured enough that agents can build full-stack apps without ever opening a browser and all UI tests are pure unit tests with no browser runtime. Server returns a JSON tree of typed nodes; a thin TypeScript adapter renders it to DOM. Backend-agnostic — a .NET reference backend ships with the repo, but any language can produce the JSON contract.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/styles/default.css
CHANGED
|
@@ -226,7 +226,7 @@ body {
|
|
|
226
226
|
|
|
227
227
|
/* ── Layout preset: row (D-30 / 1.11.0) — left-aligned wrapping horizontal row;
|
|
228
228
|
children hug their content (no forced grow), wrap to the next line on overflow.
|
|
229
|
-
The general horizontal-row primitive — a navbar composes from this
|
|
229
|
+
The general horizontal-row primitive — a navbar composes from this.
|
|
230
230
|
Title/heading takes its own full-width row, like the other presets. gap
|
|
231
231
|
inherited from .vms-page/.vms-section — NOT redeclared. ── */
|
|
232
232
|
.vms-page--row,
|
|
@@ -373,57 +373,6 @@ body {
|
|
|
373
373
|
outline-offset: 2px;
|
|
374
374
|
}
|
|
375
375
|
|
|
376
|
-
/* ── Section: flyout (1.11.0 — overlay disclosure, the hover/focus sibling of
|
|
377
|
-
collapsible) ── The heading renders as a focusable .vms-section__trigger button;
|
|
378
|
-
the children are wrapped in an absolutely-positioned .vms-section__panel revealed
|
|
379
|
-
on :hover / :focus-within. Pure CSS — hover (desktop), tap-to-focus (touch), and
|
|
380
|
-
Tab-to-trigger (keyboard a11y) all reveal it; it hides on blur / pointer-leave.
|
|
381
|
-
No JS, no round-tripped open state. The panel overlays (out of flow) so a flyout
|
|
382
|
-
inside a .vms-section--row bar does not reflow its siblings. */
|
|
383
|
-
.vms-section--flyout {
|
|
384
|
-
position: relative;
|
|
385
|
-
flex-direction: column; /* trigger stacks above the (absolute) panel; wrapper hugs the trigger */
|
|
386
|
-
align-items: flex-start;
|
|
387
|
-
}
|
|
388
|
-
.vms-section__trigger {
|
|
389
|
-
font-size: var(--vms-text-xs);
|
|
390
|
-
letter-spacing: 0.08em;
|
|
391
|
-
text-transform: uppercase;
|
|
392
|
-
color: var(--vms-text-muted);
|
|
393
|
-
cursor: pointer;
|
|
394
|
-
background: none;
|
|
395
|
-
border: none;
|
|
396
|
-
padding: 0;
|
|
397
|
-
font-family: inherit;
|
|
398
|
-
}
|
|
399
|
-
.vms-section__trigger:focus-visible {
|
|
400
|
-
outline: 2px solid var(--vms-accent);
|
|
401
|
-
outline-offset: 2px;
|
|
402
|
-
}
|
|
403
|
-
.vms-section__panel {
|
|
404
|
-
position: absolute;
|
|
405
|
-
top: 100%;
|
|
406
|
-
left: 0;
|
|
407
|
-
z-index: 50;
|
|
408
|
-
min-width: max-content;
|
|
409
|
-
display: flex;
|
|
410
|
-
flex-direction: column;
|
|
411
|
-
gap: var(--vms-space-sm);
|
|
412
|
-
margin-top: var(--vms-space-xs);
|
|
413
|
-
padding: var(--vms-space-sm);
|
|
414
|
-
background: var(--vms-surface);
|
|
415
|
-
border: 1px solid var(--vms-border);
|
|
416
|
-
border-radius: var(--vms-radius);
|
|
417
|
-
visibility: hidden;
|
|
418
|
-
opacity: 0;
|
|
419
|
-
transition: opacity var(--vms-t);
|
|
420
|
-
}
|
|
421
|
-
.vms-section--flyout:hover > .vms-section__panel,
|
|
422
|
-
.vms-section--flyout:focus-within > .vms-section__panel {
|
|
423
|
-
visibility: visible;
|
|
424
|
-
opacity: 1;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
376
|
/* ── Stat bar ── */
|
|
428
377
|
.vms-stat-bar { display: flex; gap: var(--vms-space-lg); flex-wrap: wrap; }
|
|
429
378
|
.vms-stat-bar__item { display: flex; align-items: baseline; gap: var(--vms-space-xs); }
|