Files
WRNexusJS/examples/component-showcase/app/pages/components/layout-splitter.wrn
T
ClintchizandClaude Opus 5 c7ab605e85
Quality / quality (ubuntu-latest) (push) Failing after 12m21s
Quality / quality (windows-latest) (push) Canceled after 0s
feat(ui): migrate the page and section components, add the layout tour
Section, SectionHeader and PublicPageShell move onto wire-* classes with
local style blocks, variants as data attributes. SectionHeader loses 38
utility lines, and Section stops spending one line per variant and colour
pairing: tinted and solid now select on two attributes. PageHeader was already
on the convention and only needed the audit.

examples/basic-app/app/pages/layout.wrn composes the whole set into one page.

Building it surfaced a library-wide bug. Ten custom properties were referenced
by components and defined by nothing: --wire-color-focus, --wire-color-surface-soft,
--wire-color-on-danger, --wire-color-surface-subtle and the input-* family,
plus hover and contrast for every semantic colour except primary and
secondary. An undefined custom property does not warn, it resolves to nothing,
so focus rings drew with no colour and every soft surface rendered
transparent -- 27 components referenced surface-soft alone. They are derived
in the theme now, and a test checks every token a component references against
the rendered theme CSS rather than the source, since most are generated.

The semantic spread also had to move ahead of the primary and secondary
entries so the palette keeps winning for those two.

Known and unresolved: LayoutSplitter emits its sizeChange output and the
component does fire it, but a parent binding on the tag is not invoked. The
tour page therefore points at the handle aria-valuenow rather than wiring a
handler that would never update. Outputs work elsewhere, so this is narrower
than an outputs-are-broken problem and needs its own investigation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 15:50:33 +05:30

134 lines
12 KiB
Plaintext

// Generated by scripts/generate-showcase.mjs. Do not edit directly.
page LayoutSplitterDetail {
layout = "showcase"
state playground_color = ctx.url.searchParams.get("pg_color") ?? "primary"
state playground_size = Number(ctx.url.searchParams.get("pg_size") ?? "50")
state playground_orientation = ctx.url.searchParams.get("pg_orientation") ?? "horizontal"
state playground_minSize = Number(ctx.url.searchParams.get("pg_minSize") ?? "15")
state playground_step = Number(ctx.url.searchParams.get("pg_step") ?? "5")
state playground_label = ctx.url.searchParams.get("pg_label") ?? "Layout Splitter"
state playground_class = ctx.url.searchParams.get("pg_class") ?? "showcase-instance showcase-instance--1"
seo {
title = "Layout Splitter"
description = "Theme-aware, responsive layout splitter component."
}
view {
<nav class="docs-breadcrumbs" aria-label="Breadcrumb">
<a href="/">Components</a><span aria-hidden="true">/</span><a href="/layout">Layout</a><span aria-hidden="true">/</span><span>Layout Splitter</span>
</nav>
<header class="detail-hero">
<div class="detail-hero-copy">
<span class="showcase-eyebrow">Layout component</span>
<h1>Layout Splitter</h1>
<p>Theme-aware, responsive layout splitter component.</p>
<div class="detail-badges"><span>7 props</span><span>3 slots</span><span>1 outputs</span><span>Theme ready</span><span>Responsive</span></div>
</div>
<div class="detail-hero-icon"><span class="icon-[lucide--component] size-10" aria-hidden="true"></span></div>
</header>
<section id="playground" class="detail-section playground-section" data-playground data-playground-component="LayoutSplitter" data-playground-public-tag="true" data-playground-has-slot="true" data-playground-events="sizeChange">
<div class="detail-section-heading"><span>Interactive playground</span><h2>Configure Layout Splitter</h2><p>Change any prop and inspect the server-rendered component immediately.</p></div>
<div class="playground-workbench">
<div class="playground-preview">
<div class="demo-browser-chrome"><span></span><span></span><span></span><small>Live preview</small></div>
<div class="playground-preview-source" data-playground-preview><LayoutSplitter color='{playground_color}' size='{playground_size}' orientation='{playground_orientation}' minSize='{playground_minSize}' step='{playground_step}' label='{playground_label}' class='{playground_class}' /></div>
<section class="playground-code" aria-label="Current component code">
<header><span><span class="icon-[lucide--code-2] size-4" aria-hidden="true"></span>Component code</span><button type="button" data-playground-copy><span class="icon-[lucide--copy] size-4" aria-hidden="true"></span>Copy</button></header>
<pre><code data-playground-code>&lt;LayoutSplitter
label="Layout Splitter"&gt;
&lt;div data-slot="start"&gt;
&lt;div class="showcase-slot"&gt;start slot&lt;/div&gt;
&lt;/div&gt;
&lt;div data-slot="end"&gt;
&lt;div class="showcase-slot"&gt;end slot&lt;/div&gt;
&lt;/div&gt;
&lt;/LayoutSplitter&gt;</code></pre>
</section>
<div class="playground-status" data-playground-status aria-live="polite"></div>
<div class="playground-event-log" data-playground-event-log aria-live="polite"><strong>Event output</strong><span>Interact with the preview to inspect the typed <code>payload</code>.</span></div>
</div>
<form class="playground-controls" data-playground-form>
<header><div><span>Component props</span><strong>7 controls</strong></div><button type="reset"><span class="icon-[lucide--rotate-ccw] size-4" aria-hidden="true"></span>Reset</button></header>
<div class="playground-fields"><label class="playground-field" for="playground-layout-splitter-color"><span><strong>color</strong><small>string</small></span><select id="playground-layout-splitter-color" name="pg_color"><option value="primary" selected>primary</option><option value="secondary">secondary</option><option value="success">success</option><option value="warning">warning</option><option value="danger">danger</option><option value="info">info</option></select></label><label class="playground-field" for="playground-layout-splitter-size"><span><strong>size</strong><small>number</small></span><input id="playground-layout-splitter-size" name="pg_size" type="number" value="50" /></label><label class="playground-field" for="playground-layout-splitter-orientation"><span><strong>orientation</strong><small>string</small></span><select id="playground-layout-splitter-orientation" name="pg_orientation"><option value="horizontal" selected>horizontal</option><option value="vertical">vertical</option></select></label><label class="playground-field" for="playground-layout-splitter-minSize"><span><strong>min Size</strong><small>number</small></span><input id="playground-layout-splitter-minSize" name="pg_minSize" type="number" value="15" /></label><label class="playground-field" for="playground-layout-splitter-step"><span><strong>step</strong><small>number</small></span><input id="playground-layout-splitter-step" name="pg_step" type="number" value="5" /></label><label class="playground-field" for="playground-layout-splitter-label"><span><strong>label</strong><small>string</small></span><input id="playground-layout-splitter-label" name="pg_label" type="text" value="Layout Splitter" /></label><label class="playground-field" for="playground-layout-splitter-class"><span><strong>class</strong><small>string</small></span><input id="playground-layout-splitter-class" name="pg_class" type="text" value="showcase-instance showcase-instance--1" /></label></div>
</form>
</div>
</section>
<div class="detail-layout">
<main class="detail-content">
<section class="detail-section"><div class="detail-section-heading"><span>Live examples</span><h2>Designed for real product surfaces</h2><p>Compare configurations and resize the browser to check responsive behavior.</p></div><div class="demo-list">
<article class="demo-case">
<header class="demo-case-header">
<div><span class="demo-case-eyebrow">Recommended</span><h2>Drag or arrow the divider</h2><p>Two panes with a movable divider. Drag it, or focus it and use the arrow keys; Home and End go to the bounds. The size is resolved in the runtime and written onto the container as a custom property, because a pointermove fires far too often to route through a client function.</p></div>
<span class="demo-case-number">01</span>
</header>
<div class="demo-workbench">
<div id="layout-splitter-demo-1" class="demo-canvas demo-canvas--1">
<div class="demo-browser-chrome"><span></span><span></span><span></span><small>Live preview</small></div>
<div class="demo-render"><LayoutSplitter size="40" orientation="horizontal" minSize="20" step="5" label="Resize panels" class="showcase-instance showcase-instance--1" /></div>
</div>
<section class="demo-code" aria-label="Drag or arrow the divider usage">
<header><span><span class="icon-[lucide--code-2] size-4" aria-hidden="true"></span>Component usage</span><small>.wrn</small></header>
<pre><code>&lt;LayoutSplitter
size="40"
minSize="20"&gt;
&lt;div data-slot="start"&gt;
&lt;div class="showcase-slot"&gt;start slot&lt;/div&gt;
&lt;/div&gt;
&lt;div data-slot="end"&gt;
&lt;div class="showcase-slot"&gt;end slot&lt;/div&gt;
&lt;/div&gt;
&lt;/LayoutSplitter&gt;</code></pre>
</section>
</div>
</article>
<article class="demo-case">
<header class="demo-case-header">
<div><span class="demo-case-eyebrow">Advanced</span><h2>Stacked panes</h2><p>Vertical orientation splits top from bottom and switches the arrow keys to up and down. Below the small breakpoint both orientations stack, because two panes side by side stop making sense on a phone.</p></div>
<span class="demo-case-number">02</span>
</header>
<div class="demo-workbench">
<div id="layout-splitter-demo-2" class="demo-canvas demo-canvas--2">
<div class="demo-browser-chrome"><span></span><span></span><span></span><small>Live preview</small></div>
<div class="demo-render"><LayoutSplitter size="60" orientation="vertical" minSize="25" step="10" label="Resize rows" class="showcase-instance showcase-instance--2" /></div>
</div>
<section class="demo-code" aria-label="Stacked panes usage">
<header><span><span class="icon-[lucide--code-2] size-4" aria-hidden="true"></span>Component usage</span><small>.wrn</small></header>
<pre><code>&lt;LayoutSplitter
size="60"
orientation="vertical"
minSize="25"
step="10"
label="Resize rows"&gt;
&lt;div data-slot="start"&gt;
&lt;div class="showcase-slot"&gt;start slot&lt;/div&gt;
&lt;/div&gt;
&lt;div data-slot="end"&gt;
&lt;div class="showcase-slot"&gt;end slot&lt;/div&gt;
&lt;/div&gt;
&lt;/LayoutSplitter&gt;</code></pre>
</section>
</div>
</article></div></section>
<section id="events" class="detail-section"><div class="detail-section-heading"><span>Component outputs</span><h2>Receive every typed component output</h2><p>Use declarative output handlers in <code>.wrn</code> files or register a direct output handler from JavaScript. The canonical API exposes <code>payload</code> and does not require <code>event.detail</code>.</p></div><div class="detail-events-grid"><div class="detail-events-list"><div><code>@sizeChange</code><span>Fires when the component emits the sizeChange event.</span></div></div><div class="detail-event-examples"><section class="demo-code"><header><span><span class="icon-[lucide--braces] size-4" aria-hidden="true"></span>Declarative handlers</span><small>.wrn</small></header><pre><code>&lt;LayoutSplitter
@sizeChange='console.log(payload)'
/&gt;</code></pre></section><section class="demo-code"><header><span><span class="icon-[lucide--radio] size-4" aria-hidden="true"></span>Direct output handlers</span><small>.js</small></header><pre><code>import <span>&#123;</span> registerOutputHandler <span>&#125;</span> from "@wrnexus/csr/outputs"
const component = document.querySelector("[data-ui-component=\"LayoutSplitter\"], [data-component=\"LayoutSplitter\"]")
if (component) registerOutputHandler(component, "sizeChange", (payload) =&gt; <span>&#123;</span>
console.log("sizeChange", payload)
<span>&#125;</span>)</code></pre></section></div></div></section>
<section id="api" class="detail-section"><div class="detail-section-heading"><span>Component API</span><h2>Props and configuration</h2><p>All content and behavior shown above is supplied through these props and slots.</p></div><div class="docs-table-wrap"><table class="docs-table"><thead><tr><th>Prop</th><th>Type</th><th>Default</th><th>Required</th></tr></thead><tbody><tr><td><code>color</code></td><td>string</td><td><code>"primary"</code></td><td>No</td></tr><tr><td><code>size</code></td><td>number</td><td><code>50</code></td><td>No</td></tr><tr><td><code>orientation</code></td><td>string</td><td><code>"horizontal"</code></td><td>No</td></tr><tr><td><code>minSize</code></td><td>number</td><td><code>15</code></td><td>No</td></tr><tr><td><code>step</code></td><td>number</td><td><code>5</code></td><td>No</td></tr><tr><td><code>label</code></td><td>string</td><td><code>"Resize panels"</code></td><td>No</td></tr><tr><td><code>class</code></td><td>string</td><td><code>""</code></td><td>No</td></tr></tbody></table></div></section>
</main>
<aside class="detail-aside">
<div class="detail-toc"><strong>On this page</strong><a href="#playground">Playground</a><a href="#layout-splitter-demo-1">Drag or arrow the divider</a><a href="#layout-splitter-demo-2">Stacked panes</a><a href="#events">Outputs</a><a href="#api">Props API</a></div>
</aside>
</div>
<nav class="detail-pagination">
<a href="/components/kbd"><small>Previous</small><strong>← Kbd</strong></a>
<a href="/components/link"><small>Next</small><strong>Link →</strong></a>
</nav>
}
}