@adia-ai/web-components 0.6.46 → 0.6.48
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/CHANGELOG.md +54 -0
- package/components/badge/badge.d.ts +14 -0
- package/components/button/button.a2ui.json +1 -4
- package/components/button/button.d.ts +1 -1
- package/components/button/button.yaml +0 -3
- package/components/calendar-grid/calendar-grid.css +20 -11
- package/components/calendar-picker/calendar-picker.css +19 -10
- package/components/card/card.a2ui.json +2 -5
- package/components/card/card.css +25 -7
- package/components/card/card.d.ts +2 -2
- package/components/card/card.yaml +7 -5
- package/components/date-range-picker/date-range-picker.css +10 -1
- package/components/heatmap/heatmap.a2ui.json +2 -0
- package/components/heatmap/heatmap.d.ts +1 -1
- package/components/heatmap/heatmap.yaml +2 -0
- package/components/index.js +1 -0
- package/components/preview/preview.a2ui.json +93 -0
- package/components/preview/preview.class.js +178 -0
- package/components/preview/preview.css +176 -0
- package/components/preview/preview.d.ts +24 -0
- package/components/preview/preview.js +22 -0
- package/components/preview/preview.yaml +100 -0
- package/components/progress/progress.a2ui.json +2 -7
- package/components/progress/progress.d.ts +2 -2
- package/components/progress/progress.yaml +3 -8
- package/components/progress-row/progress-row.a2ui.json +1 -3
- package/components/progress-row/progress-row.d.ts +1 -1
- package/components/progress-row/progress-row.yaml +0 -2
- package/components/select/select.a2ui.json +2 -4
- package/components/select/select.yaml +2 -2
- package/components/tabs/tabs.a2ui.json +1 -4
- package/components/tabs/tabs.d.ts +2 -2
- package/components/tabs/tabs.yaml +2 -2
- package/core/anchor.js +5 -1
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +75 -73
- package/index.css +6 -6
- package/package.json +1 -1
- package/styles/README.md +71 -36
- package/styles/api/layout.css +19 -0
- package/styles/api/sizing.css +225 -0
- package/styles/api/text.css +106 -0
- package/styles/colors/semantics/aliases.css +32 -0
- package/styles/colors/semantics/buckets.css +64 -0
- package/styles/colors/semantics/core.css +317 -0
- package/styles/colors/semantics/data-viz.css +129 -0
- package/styles/colors/semantics/features.css +114 -0
- package/styles/colors/semantics.css +10 -619
- package/styles/components.css +1 -0
- package/styles/foundation/elevation.css +29 -0
- package/styles/foundation/index.css +11 -0
- package/styles/foundation/motion.css +10 -0
- package/styles/foundation/radius.css +27 -0
- package/styles/foundation/size.css +33 -0
- package/styles/foundation/space.css +47 -0
- package/styles/index.css +14 -0
- package/styles/resets.css +17 -25
- package/styles/tokens.css +16 -384
- package/styles/type/elements.css +225 -0
- package/styles/type/roles.css +419 -0
- package/styles/type/scale.css +89 -0
- package/styles/typography.css +11 -809
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-side-effect class export for `<preview-ui>`.
|
|
3
|
+
*
|
|
4
|
+
* Importing this file gives you the class without auto-registering the tag.
|
|
5
|
+
* The auto-register path is `@adia-ai/web-components/components/preview`.
|
|
6
|
+
*
|
|
7
|
+
* @see ../../USAGE.md#registration--auto-vs-explicit
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* <preview-ui> — live code + render, single source of truth.
|
|
12
|
+
*
|
|
13
|
+
* Wrap any authored AdiaUI markup; the component keeps it LIVE in a render
|
|
14
|
+
* stage and shows the SAME markup as escaped, syntax-highlighted, copyable
|
|
15
|
+
* HTML beside it — so a demo's code and its rendered output can never drift
|
|
16
|
+
* (the code IS the render's source).
|
|
17
|
+
*
|
|
18
|
+
* <preview-ui>
|
|
19
|
+
* <button-ui text="Save" variant="primary"></button-ui>
|
|
20
|
+
* </preview-ui>
|
|
21
|
+
*
|
|
22
|
+
* Batteries-included + HTML-first by construction: the author writes one
|
|
23
|
+
* block of HTML, no inline styles, no JS wiring — both panes appear.
|
|
24
|
+
*
|
|
25
|
+
* Attributes:
|
|
26
|
+
* [layout="split"] — render + code side-by-side (default: stacked)
|
|
27
|
+
* [code-first] — show the code pane above/before the render
|
|
28
|
+
* [language="…"] — code-ui language hint (default: html)
|
|
29
|
+
*
|
|
30
|
+
* Light-DOM (ADR-0033): the render pane holds real, live custom elements
|
|
31
|
+
* (re-parsed from the captured source on connect), so interactive demos
|
|
32
|
+
* actually work. `slot=` here is decorative; positioning is by CSS.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { UIElement } from '../../core/element.js';
|
|
36
|
+
|
|
37
|
+
/** Prepare captured markup for display: strip shared indentation + surrounding
|
|
38
|
+
* blank lines, and collapse `attr=""` → `attr` (the DOM serializes boolean
|
|
39
|
+
* attributes like `truncate` / `disabled` with an empty value — show the
|
|
40
|
+
* cleaner boolean form a consumer would actually type). */
|
|
41
|
+
function dedent(src) {
|
|
42
|
+
let lines = src
|
|
43
|
+
.replace(/=""(?=[\s/>])/g, '') // boolean attr: truncate="" → truncate
|
|
44
|
+
.replace(/\t/g, ' ')
|
|
45
|
+
.split('\n');
|
|
46
|
+
while (lines.length && lines[0].trim() === '') lines.shift();
|
|
47
|
+
while (lines.length && lines[lines.length - 1].trim() === '') lines.pop();
|
|
48
|
+
const indentOf = (l) => l.match(/^ */)[0].length;
|
|
49
|
+
// Measure the common indent from the BODY (everything after the first line),
|
|
50
|
+
// not from every line: markup captured via outerHTML/innerHTML puts the
|
|
51
|
+
// opening tag at column 0 while its children + closing tag carry the source
|
|
52
|
+
// file's nesting depth. Including that 0-indent first line would make min=0
|
|
53
|
+
// and leave the whole body deeply indented (the original bug). Clamp each
|
|
54
|
+
// slice to the line's own indent so the 0-indent opener is never truncated.
|
|
55
|
+
const body = lines.slice(1).filter((l) => l.trim());
|
|
56
|
+
const measured = (body.length ? body : lines.filter((l) => l.trim())).map(indentOf);
|
|
57
|
+
const min = measured.length ? Math.min(...measured) : 0;
|
|
58
|
+
return lines.map((l) => l.slice(Math.min(min, indentOf(l)))).join('\n');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export class UIPreview extends UIElement {
|
|
62
|
+
static properties = {
|
|
63
|
+
layout: { type: String, default: '', reflect: true },
|
|
64
|
+
codeFirst: { type: Boolean, default: false, reflect: true, attribute: 'code-first' },
|
|
65
|
+
language: { type: String, default: 'html', reflect: true },
|
|
66
|
+
rows: { type: Boolean, default: false, reflect: true },
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
static template = () => null;
|
|
70
|
+
|
|
71
|
+
#fitObserver = null;
|
|
72
|
+
|
|
73
|
+
/** A [render-cell | code-cell] pair from one source string. */
|
|
74
|
+
#pane(source, lang, codeFirst) {
|
|
75
|
+
const render = document.createElement('div');
|
|
76
|
+
render.setAttribute('data-preview-render', '');
|
|
77
|
+
render.innerHTML = source; // re-parse → fresh, live custom elements (keeps
|
|
78
|
+
// any data-chunk corpus markers for harvest)
|
|
79
|
+
const codeWrap = document.createElement('div');
|
|
80
|
+
codeWrap.setAttribute('data-preview-code', '');
|
|
81
|
+
const code = document.createElement('code-ui');
|
|
82
|
+
code.setAttribute('language', lang);
|
|
83
|
+
// The CODE pane shows the clean consumer-facing HTML — strip doc-only
|
|
84
|
+
// corpus markers (data-chunk / -kind / -slot) that the render keeps.
|
|
85
|
+
code.textContent = source.replace(/\s+data-chunk(?:-[a-z]+)?="[^"]*"/g, '');
|
|
86
|
+
codeWrap.appendChild(code);
|
|
87
|
+
return codeFirst ? [codeWrap, render] : [render, codeWrap];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
connected() {
|
|
91
|
+
// Idempotent — never re-stamp (peer mutation / re-connect safe).
|
|
92
|
+
if (this.querySelector(':scope > [data-preview-render], :scope > [data-preview-row]')) return;
|
|
93
|
+
|
|
94
|
+
// Side-by-side (render | code) is the default presentation. `rows` mode lays
|
|
95
|
+
// out its own per-example grid, and an explicit layout="…" (e.g. "stack")
|
|
96
|
+
// opts back into the stacked render-over-code form — so only default when
|
|
97
|
+
// neither is set. Setting the attribute (not the property default) keeps
|
|
98
|
+
// rows-mode previews free of a conflicting layout="split".
|
|
99
|
+
if (!this.hasAttribute('rows') && !this.hasAttribute('layout')) {
|
|
100
|
+
this.setAttribute('layout', 'split');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const lang = this.getAttribute('language') || 'html';
|
|
104
|
+
const codeFirst = this.hasAttribute('code-first');
|
|
105
|
+
|
|
106
|
+
// ── rows mode ── each direct child is one example; lay each out as its
|
|
107
|
+
// own [render | code] row so a multi-example gallery reads as paired
|
|
108
|
+
// side-by-side rows instead of all-renders-then-one-code-dump. The code
|
|
109
|
+
// shown is the child's OWN markup (zero drift). An optional
|
|
110
|
+
// [data-preview-label] surfaces a caption per row.
|
|
111
|
+
if (this.hasAttribute('rows')) {
|
|
112
|
+
const children = [...this.children];
|
|
113
|
+
const out = [];
|
|
114
|
+
for (const child of children) {
|
|
115
|
+
const label = child.getAttribute('data-preview-label');
|
|
116
|
+
if (label != null) child.removeAttribute('data-preview-label');
|
|
117
|
+
// A synthetic wrapper (multi-element row grouped by the host) marks
|
|
118
|
+
// itself [data-preview-unwrap] so we show its children's markup, not the
|
|
119
|
+
// throwaway wrapper tag — the copied code stays exactly what the author
|
|
120
|
+
// would paste.
|
|
121
|
+
const unwrap = child.hasAttribute('data-preview-unwrap');
|
|
122
|
+
if (unwrap) child.removeAttribute('data-preview-unwrap');
|
|
123
|
+
const source = dedent(unwrap ? child.innerHTML : child.outerHTML);
|
|
124
|
+
const row = document.createElement('div');
|
|
125
|
+
row.setAttribute('data-preview-row', '');
|
|
126
|
+
const panes = this.#pane(source, lang, codeFirst);
|
|
127
|
+
// The caption pill renders via [data-preview-render]::before, whose
|
|
128
|
+
// attr() can only read its OWN element — so the label goes on the
|
|
129
|
+
// render cell, not the row.
|
|
130
|
+
if (label) {
|
|
131
|
+
const renderEl = panes.find((el) => el.hasAttribute('data-preview-render'));
|
|
132
|
+
renderEl?.setAttribute('data-preview-label', label);
|
|
133
|
+
}
|
|
134
|
+
row.append(...panes);
|
|
135
|
+
out.push(row);
|
|
136
|
+
}
|
|
137
|
+
if (out.length) { this.replaceChildren(...out); this.#observeFit(); return; }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ── single-block mode ── the whole slotted markup → one render + one code.
|
|
141
|
+
const source = dedent(this.innerHTML);
|
|
142
|
+
this.replaceChildren(...this.#pane(source, lang, codeFirst));
|
|
143
|
+
this.#observeFit();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Side-by-side clips content wider than its half-width cell (a full dashboard
|
|
147
|
+
// composite, a wide control). After layout, if a render cell overflows, fall
|
|
148
|
+
// back to the stacked full-width form so the demo is never cut off. Monotonic
|
|
149
|
+
// (split → stack, never back; a row gains [data-stack] once), so it settles in
|
|
150
|
+
// one pass and never oscillates. Runs on a rAF + a ResizeObserver so it also
|
|
151
|
+
// adapts when the viewport narrows. Self-correcting — no per-component "wide"
|
|
152
|
+
// list to maintain.
|
|
153
|
+
#fit = () => {
|
|
154
|
+
if (this.hasAttribute('rows')) {
|
|
155
|
+
for (const row of this.querySelectorAll(':scope > [data-preview-row]')) {
|
|
156
|
+
const r = row.querySelector(':scope > [data-preview-render]');
|
|
157
|
+
if (r && r.scrollWidth > r.clientWidth + 4) row.setAttribute('data-stack', '');
|
|
158
|
+
}
|
|
159
|
+
} else if (this.getAttribute('layout') === 'split') {
|
|
160
|
+
const r = this.querySelector(':scope > [data-preview-render]');
|
|
161
|
+
if (r && r.scrollWidth > r.clientWidth + 4) this.setAttribute('layout', 'stack');
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
#scheduleFit = () => requestAnimationFrame(this.#fit);
|
|
166
|
+
|
|
167
|
+
#observeFit() {
|
|
168
|
+
this.#scheduleFit();
|
|
169
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
170
|
+
this.#fitObserver = new ResizeObserver(this.#scheduleFit);
|
|
171
|
+
this.#fitObserver.observe(this);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
disconnected() {
|
|
176
|
+
this.#fitObserver?.disconnect();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
<preview-ui> — live code + render, single source of truth.
|
|
3
|
+
|
|
4
|
+
A framed demo surface: a "stage" holding the live components on top
|
|
5
|
+
(or beside) the same markup shown as escaped, highlighted, copyable
|
|
6
|
+
HTML via a nested <code-ui>. The frame owns the chrome; the inner
|
|
7
|
+
code-ui's own border/radius are flattened so the two panes read as
|
|
8
|
+
one card.
|
|
9
|
+
|
|
10
|
+
Two-block @scope per docs/specs/component-token-contract.md.
|
|
11
|
+
============================================================ */
|
|
12
|
+
|
|
13
|
+
@scope (preview-ui) {
|
|
14
|
+
/* ── Block 1 — TOKENS ── */
|
|
15
|
+
:where(:scope) {
|
|
16
|
+
--preview-border-default: 1px solid var(--a-border-subtle);
|
|
17
|
+
--preview-radius-default: var(--a-radius-lg);
|
|
18
|
+
--preview-render-bg-default: var(--a-canvas-1);
|
|
19
|
+
--preview-render-pad-default: var(--a-space-6);
|
|
20
|
+
--preview-render-gap-default: var(--a-space-3);
|
|
21
|
+
/* split column floor — below this the grid wraps to a single column
|
|
22
|
+
(responsive without container-type; see ADR note in css-patterns). */
|
|
23
|
+
--preview-split-min-default: 20rem;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* ── Block 2 — BASE ── */
|
|
27
|
+
:scope {
|
|
28
|
+
display: block;
|
|
29
|
+
border: var(--preview-border, var(--preview-border-default));
|
|
30
|
+
border-radius: var(--preview-radius, var(--preview-radius-default));
|
|
31
|
+
overflow: hidden;
|
|
32
|
+
background: var(--preview-render-bg, var(--preview-render-bg-default));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Render stage — wraps live demo nodes; flex row so multiple sibling
|
|
36
|
+
samples (e.g. button variants) sit on one line and wrap. overflow-x:auto is
|
|
37
|
+
the last-resort affordance: a single unwrappable sample wider than even the
|
|
38
|
+
full-width stage (a page-scale composite) scrolls instead of being clipped
|
|
39
|
+
by the frame's overflow:hidden. Most demos fit (the component's overflow
|
|
40
|
+
check stacks split→full-width first), so the scrollbar only appears when a
|
|
41
|
+
demo is genuinely wider than the docs column. */
|
|
42
|
+
[data-preview-render] {
|
|
43
|
+
display: flex;
|
|
44
|
+
flex-wrap: wrap;
|
|
45
|
+
align-items: center;
|
|
46
|
+
gap: var(--preview-render-gap, var(--preview-render-gap-default));
|
|
47
|
+
padding: var(--preview-render-pad, var(--preview-render-pad-default));
|
|
48
|
+
background: var(--preview-render-bg, var(--preview-render-bg-default));
|
|
49
|
+
overflow-x: auto;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* Code pane — divider line between panes; flatten the nested code-ui
|
|
53
|
+
chrome so the preview frame owns the outer border + radius. */
|
|
54
|
+
[data-preview-code] {
|
|
55
|
+
border-block-start: var(--preview-border, var(--preview-border-default));
|
|
56
|
+
min-width: 0; /* allow the code-ui to shrink in split grid */
|
|
57
|
+
}
|
|
58
|
+
[data-preview-code] code-ui {
|
|
59
|
+
display: block;
|
|
60
|
+
--code-border: transparent;
|
|
61
|
+
--code-radius: 0;
|
|
62
|
+
--code-bg: var(--a-bg);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* code-first — divider moves to the bottom of the (now-leading) code pane */
|
|
66
|
+
:scope[code-first] [data-preview-code] {
|
|
67
|
+
border-block-start: none;
|
|
68
|
+
border-block-end: var(--preview-border, var(--preview-border-default));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* Stacked layout — render above code. This is the BASE behaviour (above), so
|
|
72
|
+
[layout="stack"] just names it explicitly: it's the opt-out from the
|
|
73
|
+
side-by-side default that a bare <preview-ui> stamps on connect, and the
|
|
74
|
+
value the docs pin onto wide self-framing demos (card / shell / table). */
|
|
75
|
+
:scope[layout="stack"] {
|
|
76
|
+
display: block;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* ── Split layout — render + code side-by-side ──
|
|
80
|
+
auto-fit grid collapses to one column under --preview-split-min with
|
|
81
|
+
NO container-type (avoids the flex-child-collapse trap; see memory
|
|
82
|
+
`container_name_convention`). When wrapped, the inter-pane divider
|
|
83
|
+
reverts to the stacked top-border via the base rule above. */
|
|
84
|
+
:scope[layout="split"] {
|
|
85
|
+
display: grid;
|
|
86
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--preview-split-min, var(--preview-split-min-default))), 1fr));
|
|
87
|
+
}
|
|
88
|
+
:scope[layout="split"] [data-preview-code] {
|
|
89
|
+
border-block-start: none;
|
|
90
|
+
border-inline-start: var(--preview-border, var(--preview-border-default));
|
|
91
|
+
/* Stretch code-ui to the grid row height — the render side is often
|
|
92
|
+
taller, so a short snippet would otherwise leave an empty code surface
|
|
93
|
+
below it (same fill as rows mode). */
|
|
94
|
+
display: grid;
|
|
95
|
+
}
|
|
96
|
+
:scope[layout="split"] [data-preview-code] code-ui {
|
|
97
|
+
height: 100%;
|
|
98
|
+
}
|
|
99
|
+
/* Single-column fallback (grid wrapped): restore the horizontal divider.
|
|
100
|
+
:scope[layout="split"] with one effective column → the code pane is on
|
|
101
|
+
its own row, so the inline-start border reads as a stray left edge.
|
|
102
|
+
Guard with a width media as a pragmatic floor. */
|
|
103
|
+
@media (max-width: 32rem) {
|
|
104
|
+
:scope[layout="split"] [data-preview-code] {
|
|
105
|
+
border-inline-start: none;
|
|
106
|
+
border-block-start: var(--preview-border, var(--preview-border-default));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* ══════════ rows mode — a gallery of [render | code] rows ══════════
|
|
111
|
+
Each example is its own row: live render on the left, its source on the
|
|
112
|
+
right, paired side-by-side (no all-renders-then-one-code-dump). Rows
|
|
113
|
+
stack with a divider between them; each row is a 2-col grid that
|
|
114
|
+
collapses to stacked on narrow widths. */
|
|
115
|
+
:scope[rows] {
|
|
116
|
+
display: block;
|
|
117
|
+
}
|
|
118
|
+
:scope[rows] [data-preview-row] {
|
|
119
|
+
display: grid;
|
|
120
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
121
|
+
align-items: stretch;
|
|
122
|
+
}
|
|
123
|
+
:scope[rows] [data-preview-row] + [data-preview-row] {
|
|
124
|
+
border-block-start: var(--preview-border, var(--preview-border-default));
|
|
125
|
+
}
|
|
126
|
+
/* In rows mode the render/code panes butt horizontally — divider is the
|
|
127
|
+
inline-start border on the code cell, not the stacked top-border. The
|
|
128
|
+
cell is a grid so the nested code-ui STRETCHES to the row height (the
|
|
129
|
+
render side is often taller), filling the dark code surface instead of
|
|
130
|
+
leaving empty space below a short snippet. */
|
|
131
|
+
:scope[rows] [data-preview-row] > [data-preview-code] {
|
|
132
|
+
border-block-start: none;
|
|
133
|
+
border-inline-start: var(--preview-border, var(--preview-border-default));
|
|
134
|
+
display: grid;
|
|
135
|
+
}
|
|
136
|
+
:scope[rows] [data-preview-row] > [data-preview-code] code-ui {
|
|
137
|
+
height: 100%;
|
|
138
|
+
}
|
|
139
|
+
/* Optional per-row caption pill (from [data-preview-label] on the render
|
|
140
|
+
cell — attr() reads the element's OWN attribute). The render cell becomes
|
|
141
|
+
a column so the pill sits above the live sample. */
|
|
142
|
+
:scope[rows] [data-preview-render][data-preview-label] {
|
|
143
|
+
flex-direction: column;
|
|
144
|
+
align-items: flex-start;
|
|
145
|
+
}
|
|
146
|
+
:scope[rows] [data-preview-render][data-preview-label]::before {
|
|
147
|
+
content: attr(data-preview-label);
|
|
148
|
+
align-self: start;
|
|
149
|
+
font-family: var(--a-font-family-mono);
|
|
150
|
+
font-size: var(--a-ui-sm);
|
|
151
|
+
color: var(--a-fg-muted);
|
|
152
|
+
background: var(--a-bg-muted);
|
|
153
|
+
padding: 0.125rem 0.375rem;
|
|
154
|
+
border-radius: var(--a-radius-sm);
|
|
155
|
+
}
|
|
156
|
+
/* Narrow: collapse each row to stacked render-over-code. */
|
|
157
|
+
@media (max-width: 40rem) {
|
|
158
|
+
:scope[rows] [data-preview-row] {
|
|
159
|
+
grid-template-columns: 1fr;
|
|
160
|
+
}
|
|
161
|
+
:scope[rows] [data-preview-row] > [data-preview-code] {
|
|
162
|
+
border-inline-start: none;
|
|
163
|
+
border-block-start: var(--preview-border, var(--preview-border-default));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/* Content-driven fallback — a single row whose live sample is wider than the
|
|
167
|
+
half-width cell gets [data-stack] from the component's overflow check, so it
|
|
168
|
+
lays out render-over-code at full width (wide controls / composites). */
|
|
169
|
+
:scope[rows] [data-preview-row][data-stack] {
|
|
170
|
+
grid-template-columns: 1fr;
|
|
171
|
+
}
|
|
172
|
+
:scope[rows] [data-preview-row][data-stack] > [data-preview-code] {
|
|
173
|
+
border-inline-start: none;
|
|
174
|
+
border-block-start: var(--preview-border, var(--preview-border-default));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<preview-ui>` — Live code + render in one frame, from a single source. Wrap any authored AdiaUI markup: the same HTML renders LIVE in a stage and appears beside (or below) it as escaped, syntax-highlighted, copyable source via a nested <code-ui>. The code can never drift from the render — it IS the render's source. Batteries-included + HTML-first by construction: write one block of HTML, no inline styles, no JS wiring, and both panes appear. Use it for every component/recipe example so the primary snippet is always real, copyable HTML shown next to the working component.
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/preview
|
|
5
|
+
*
|
|
6
|
+
* Type declarations generated by scripts/build/dts-codegen.mjs from
|
|
7
|
+
* the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
|
|
8
|
+
* run `npm run build:components`, then `npm run codegen:dts` to
|
|
9
|
+
* regenerate; or hand-author this file fully if rich event types are
|
|
10
|
+
* needed beyond what the yaml `events:` block can express.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { UIElement } from '../../core/element.js';
|
|
14
|
+
|
|
15
|
+
export class UIPreview extends UIElement {
|
|
16
|
+
/** Show the code pane before (above / left of) the render pane. Default is render-first. */
|
|
17
|
+
codeFirst: boolean;
|
|
18
|
+
/** Language hint forwarded to the nested <code-ui> for syntax highlighting. Demos are HTML, so this defaults to `html`. */
|
|
19
|
+
language: string;
|
|
20
|
+
/** Pane arrangement. Defaults to `split` — render and code side-by-side in a responsive 2-column grid that collapses to stacked on narrow widths (a bare `<preview-ui>` stamps `layout="split"` on connect). Use `stack` to force the render stacked above the code; the docs auto-apply `stack` to wide self-framing demos (card / shell / table) that read cramped at half width. The `rows` attribute is a separate gallery mode and overrides this. */
|
|
21
|
+
layout: 'split' | 'stack';
|
|
22
|
+
/** Gallery mode — treat each direct child as a SEPARATE example and lay each out as its own `[render | code]` row (live sample left, its own source right), stacked with dividers. Without `rows`, the whole slotted markup is one render + one code block. An optional `data-preview-label` on a child surfaces a caption above that row's sample. */
|
|
23
|
+
rows: boolean;
|
|
24
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<preview-ui>` — auto-registers the tag on import.
|
|
3
|
+
*
|
|
4
|
+
* For non-side-effect class import (test isolation, tag override), use
|
|
5
|
+
* the `class` subpath:
|
|
6
|
+
*
|
|
7
|
+
* import { UIPreview } from '@adia-ai/web-components/components/preview/class';
|
|
8
|
+
*
|
|
9
|
+
* @see ../../USAGE.md#registration--auto-vs-explicit
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { defineIfFree } from '../../core/register.js';
|
|
13
|
+
import { UIPreview } from './preview.class.js';
|
|
14
|
+
|
|
15
|
+
// preview-ui composes <code-ui> for the source pane — pull it in so a bare
|
|
16
|
+
// `import '.../preview'` is batteries-included (the code pane works without
|
|
17
|
+
// the consumer separately importing code-ui).
|
|
18
|
+
import '../code/code.js';
|
|
19
|
+
|
|
20
|
+
defineIfFree('preview-ui', UIPreview);
|
|
21
|
+
|
|
22
|
+
export { UIPreview };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Edit this file; run `npm run build:components` to regenerate a2ui.json.
|
|
2
|
+
$schema: ../../../../scripts/schemas/component.yaml.schema.json
|
|
3
|
+
name: UIPreview
|
|
4
|
+
tag: preview-ui
|
|
5
|
+
status: stable
|
|
6
|
+
component: Preview
|
|
7
|
+
category: display
|
|
8
|
+
version: 1
|
|
9
|
+
description: >-
|
|
10
|
+
Live code + render in one frame, from a single source. Wrap any authored
|
|
11
|
+
AdiaUI markup: the same HTML renders LIVE in a stage and appears beside (or
|
|
12
|
+
below) it as escaped, syntax-highlighted, copyable source via a nested
|
|
13
|
+
<code-ui>. The code can never drift from the render — it IS the render's
|
|
14
|
+
source. Batteries-included + HTML-first by construction: write one block of
|
|
15
|
+
HTML, no inline styles, no JS wiring, and both panes appear. Use it for
|
|
16
|
+
every component/recipe example so the primary snippet is always real,
|
|
17
|
+
copyable HTML shown next to the working component.
|
|
18
|
+
props:
|
|
19
|
+
layout:
|
|
20
|
+
description: >-
|
|
21
|
+
Pane arrangement. Defaults to `split` — render and code side-by-side in a
|
|
22
|
+
responsive 2-column grid that collapses to stacked on narrow widths (a bare
|
|
23
|
+
`<preview-ui>` stamps `layout="split"` on connect). Use `stack` to force the
|
|
24
|
+
render stacked above the code; the docs auto-apply `stack` to wide
|
|
25
|
+
self-framing demos (card / shell / table) that read cramped at half width.
|
|
26
|
+
The `rows` attribute is a separate gallery mode and overrides this.
|
|
27
|
+
type: string
|
|
28
|
+
default: ""
|
|
29
|
+
reflect: true
|
|
30
|
+
enum:
|
|
31
|
+
- split
|
|
32
|
+
- stack
|
|
33
|
+
codeFirst:
|
|
34
|
+
description: >-
|
|
35
|
+
Show the code pane before (above / left of) the render pane. Default is
|
|
36
|
+
render-first.
|
|
37
|
+
type: boolean
|
|
38
|
+
default: false
|
|
39
|
+
reflect: true
|
|
40
|
+
attribute: code-first
|
|
41
|
+
language:
|
|
42
|
+
description: >-
|
|
43
|
+
Language hint forwarded to the nested <code-ui> for syntax highlighting.
|
|
44
|
+
Demos are HTML, so this defaults to `html`.
|
|
45
|
+
type: string
|
|
46
|
+
default: html
|
|
47
|
+
reflect: true
|
|
48
|
+
rows:
|
|
49
|
+
description: >-
|
|
50
|
+
Gallery mode — treat each direct child as a SEPARATE example and lay
|
|
51
|
+
each out as its own `[render | code]` row (live sample left, its own
|
|
52
|
+
source right), stacked with dividers. Without `rows`, the whole slotted
|
|
53
|
+
markup is one render + one code block. An optional `data-preview-label`
|
|
54
|
+
on a child surfaces a caption above that row's sample.
|
|
55
|
+
type: boolean
|
|
56
|
+
default: false
|
|
57
|
+
reflect: true
|
|
58
|
+
slots:
|
|
59
|
+
default:
|
|
60
|
+
description: >-
|
|
61
|
+
The authored markup to preview. Captured verbatim on connect: re-parsed
|
|
62
|
+
into the live render stage AND shown literally in the code pane. Any
|
|
63
|
+
valid AdiaUI HTML — one element or many siblings.
|
|
64
|
+
states:
|
|
65
|
+
- name: idle
|
|
66
|
+
description: Default, the only state.
|
|
67
|
+
keywords:
|
|
68
|
+
- preview
|
|
69
|
+
- example
|
|
70
|
+
- demo
|
|
71
|
+
- code-preview
|
|
72
|
+
- live-preview
|
|
73
|
+
- playground
|
|
74
|
+
- codepen
|
|
75
|
+
synonyms:
|
|
76
|
+
preview: [example, demo, sandbox, live-preview]
|
|
77
|
+
related:
|
|
78
|
+
- Code
|
|
79
|
+
- Card
|
|
80
|
+
a2ui:
|
|
81
|
+
rules:
|
|
82
|
+
- rule: >-
|
|
83
|
+
Use <preview-ui> to demonstrate ANY component or recipe — wrap the
|
|
84
|
+
example markup once; it shows the live component and its copyable HTML
|
|
85
|
+
source together. Prefer it over a bare <code-ui> snippet (which shows
|
|
86
|
+
code but no render) or a bare rendered sample (which shows output but
|
|
87
|
+
no copyable HTML).
|
|
88
|
+
reason: 'Single source of truth — code and render cannot drift.'
|
|
89
|
+
- rule: >-
|
|
90
|
+
Author the slotted markup as plain AdiaUI HTML with attributes only —
|
|
91
|
+
no inline `style=`, no `<script>` wiring. If a sample needs inline
|
|
92
|
+
styles to look right, the component is missing an attribute; fix the
|
|
93
|
+
component, not the demo.
|
|
94
|
+
reason: 'Demos are the reference the next author copies; keep them honest.'
|
|
95
|
+
- rule: >-
|
|
96
|
+
Side-by-side ([layout="split"]) is the default. Set [layout="stack"] for
|
|
97
|
+
wide self-framing examples (a full card / shell / table reads cramped at
|
|
98
|
+
half width). Use [code-first] when the code is the teaching point and the
|
|
99
|
+
render is confirmation.
|
|
100
|
+
reason: 'Layout matches the reading order of the lesson.'
|
|
@@ -22,16 +22,11 @@
|
|
|
22
22
|
"default": null
|
|
23
23
|
},
|
|
24
24
|
"variant": {
|
|
25
|
-
"description": "
|
|
25
|
+
"description": "Render mode — `bar` (default) or `spinner`. For fill color set the `--progress-fill` token (defaults to `--a-accent`); there is no variant-as-color (use the token).",
|
|
26
26
|
"type": "string",
|
|
27
27
|
"enum": [
|
|
28
28
|
"bar",
|
|
29
|
-
"spinner"
|
|
30
|
-
"primary",
|
|
31
|
-
"success",
|
|
32
|
-
"warning",
|
|
33
|
-
"danger",
|
|
34
|
-
"info"
|
|
29
|
+
"spinner"
|
|
35
30
|
],
|
|
36
31
|
"default": "bar"
|
|
37
32
|
}
|
|
@@ -15,6 +15,6 @@ import { UIElement } from '../../core/element.js';
|
|
|
15
15
|
export class UIProgress extends UIElement {
|
|
16
16
|
/** Current progress. 0..max (or 0..1 when max omitted). null = indeterminate (legacy -1 accepted for back-compat). */
|
|
17
17
|
value: number;
|
|
18
|
-
/**
|
|
19
|
-
variant: 'bar' | 'spinner'
|
|
18
|
+
/** Render mode — `bar` (default) or `spinner`. For fill color set the `--progress-fill` token (defaults to `--a-accent`); there is no variant-as-color (use the token). */
|
|
19
|
+
variant: 'bar' | 'spinner';
|
|
20
20
|
}
|
|
@@ -17,19 +17,14 @@ props:
|
|
|
17
17
|
type: number
|
|
18
18
|
default: null
|
|
19
19
|
variant:
|
|
20
|
-
description:
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
description: Render mode — `bar` (default) or `spinner`. For fill color set
|
|
21
|
+
the `--progress-fill` token (defaults to `--a-accent`); there is no
|
|
22
|
+
variant-as-color (use the token).
|
|
23
23
|
type: string
|
|
24
24
|
default: bar
|
|
25
25
|
enum:
|
|
26
26
|
- bar
|
|
27
27
|
- spinner
|
|
28
|
-
- primary
|
|
29
|
-
- success
|
|
30
|
-
- warning
|
|
31
|
-
- danger
|
|
32
|
-
- info
|
|
33
28
|
events: {}
|
|
34
29
|
slots:
|
|
35
30
|
fill:
|
|
@@ -20,5 +20,5 @@ export class UIProgressRow extends UIElement {
|
|
|
20
20
|
/** Progress value 0-100 (null = indeterminate; legacy -1 accepted for back-compat) */
|
|
21
21
|
value: number;
|
|
22
22
|
/** Color variant for the progress fill */
|
|
23
|
-
variant: 'default' | '
|
|
23
|
+
variant: 'default' | 'success' | 'warning' | 'danger';
|
|
24
24
|
}
|
|
@@ -152,13 +152,11 @@
|
|
|
152
152
|
"default": ""
|
|
153
153
|
},
|
|
154
154
|
"variant": {
|
|
155
|
-
"description": "Visual variant — default chrome
|
|
155
|
+
"description": "Visual variant — `default` chrome or `ghost` for inline.",
|
|
156
156
|
"type": "string",
|
|
157
157
|
"enum": [
|
|
158
158
|
"default",
|
|
159
|
-
"
|
|
160
|
-
"ghost",
|
|
161
|
-
"soft"
|
|
159
|
+
"ghost"
|
|
162
160
|
],
|
|
163
161
|
"default": "default"
|
|
164
162
|
}
|
|
@@ -31,10 +31,10 @@ props:
|
|
|
31
31
|
type: string
|
|
32
32
|
default: ""
|
|
33
33
|
variant:
|
|
34
|
-
description: Visual variant — default chrome
|
|
34
|
+
description: Visual variant — `default` chrome or `ghost` for inline.
|
|
35
35
|
type: string
|
|
36
36
|
default: default
|
|
37
|
-
enum: [default,
|
|
37
|
+
enum: [default, ghost]
|
|
38
38
|
size:
|
|
39
39
|
description: >-
|
|
40
40
|
Sizing scale via universal `[size]` attribute system
|
|
@@ -27,13 +27,10 @@
|
|
|
27
27
|
"default": ""
|
|
28
28
|
},
|
|
29
29
|
"variant": {
|
|
30
|
-
"description": "Visual variant — default underline strip
|
|
30
|
+
"description": "Visual variant — `default` (underline strip) or `bordered`.",
|
|
31
31
|
"type": "string",
|
|
32
32
|
"enum": [
|
|
33
33
|
"default",
|
|
34
|
-
"pills",
|
|
35
|
-
"underline",
|
|
36
|
-
"segmented",
|
|
37
34
|
"bordered"
|
|
38
35
|
],
|
|
39
36
|
"default": "default"
|
|
@@ -24,8 +24,8 @@ export class UITabs extends UIElement {
|
|
|
24
24
|
orientation: string;
|
|
25
25
|
/** Value of the currently active tab. Set programmatically or auto-assigned to the first non-disabled tab on connect. */
|
|
26
26
|
value: string;
|
|
27
|
-
/** Visual variant — default underline strip
|
|
28
|
-
variant: 'default' | '
|
|
27
|
+
/** Visual variant — `default` (underline strip) or `bordered`. */
|
|
28
|
+
variant: 'default' | 'bordered';
|
|
29
29
|
|
|
30
30
|
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
31
31
|
type: K,
|