@urbicon-ui/blocks 6.40.4 → 6.43.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/components/Chat/A2UIView/A2UINode.svelte +20 -39
- package/dist/components/Chat/A2UIView/A2UIView.svelte +47 -50
- package/dist/components/Chat/A2UIView/a2ui-basic-catalog.d.ts +17 -0
- package/dist/components/Chat/A2UIView/a2ui-basic-catalog.js +69 -0
- package/dist/components/Chat/A2UIView/a2ui-catalog.d.ts +95 -0
- package/dist/components/Chat/A2UIView/a2ui-catalog.js +87 -0
- package/dist/components/Chat/A2UIView/a2ui-node-common.d.ts +40 -0
- package/dist/components/Chat/A2UIView/a2ui-node-common.js +72 -0
- package/dist/components/Chat/A2UIView/a2ui-prompt.d.ts +21 -10
- package/dist/components/Chat/A2UIView/a2ui-prompt.js +170 -24
- package/dist/components/Chat/A2UIView/a2ui-registry.d.ts +1 -1
- package/dist/components/Chat/A2UIView/a2ui-registry.js +2 -1
- package/dist/components/Chat/A2UIView/a2ui-render.d.ts +30 -0
- package/dist/components/Chat/A2UIView/a2ui-render.js +61 -23
- package/dist/components/Chat/A2UIView/a2ui-router.d.ts +136 -0
- package/dist/components/Chat/A2UIView/a2ui-router.js +264 -0
- package/dist/components/Chat/A2UIView/a2ui-schema.d.ts +45 -0
- package/dist/components/Chat/A2UIView/a2ui-schema.js +133 -0
- package/dist/components/Chat/A2UIView/a2ui-stream.d.ts +120 -0
- package/dist/components/Chat/A2UIView/a2ui-stream.js +406 -0
- package/dist/components/Chat/A2UIView/a2ui-transcript.d.ts +65 -0
- package/dist/components/Chat/A2UIView/a2ui-transcript.js +101 -0
- package/dist/components/Chat/A2UIView/a2ui-validate.d.ts +33 -1
- package/dist/components/Chat/A2UIView/a2ui-validate.js +167 -63
- package/dist/components/Chat/A2UIView/a2ui.types.d.ts +50 -0
- package/dist/components/Chat/A2UIView/a2ui.types.js +38 -1
- package/dist/components/Chat/A2UIView/index.d.ts +35 -2
- package/dist/components/Chat/A2UIView/index.js +13 -0
- package/dist/components/Chat/A2UIView/urbicon/UrbiconA2UINode.svelte +831 -0
- package/dist/components/Chat/A2UIView/urbicon/UrbiconA2UINode.svelte.d.ts +4 -0
- package/dist/components/Chat/A2UIView/urbicon/a2ui-urbicon-catalog.d.ts +14 -0
- package/dist/components/Chat/A2UIView/urbicon/a2ui-urbicon-catalog.js +87 -0
- package/dist/components/Chat/A2UIView/urbicon/a2ui-urbicon-registry.d.ts +78 -0
- package/dist/components/Chat/A2UIView/urbicon/a2ui-urbicon-registry.js +783 -0
- package/dist/components/Chat/A2UIView/urbicon/a2ui-urbicon.variants.d.ts +43 -0
- package/dist/components/Chat/A2UIView/urbicon/a2ui-urbicon.variants.js +33 -0
- package/dist/components/Chat/A2UIView/urbicon/urbicon-axes.gen.d.ts +7 -0
- package/dist/components/Chat/A2UIView/urbicon/urbicon-axes.gen.js +86 -0
- package/dist/components/Chat/ChatMessage/chat-message.variants.d.ts +19 -19
- package/dist/components/FileUpload/fileUpload.variants.js +15 -4
- package/dist/components/PinInput/PinInput.svelte +1 -0
- package/dist/components/PinInput/pin-input.variants.js +14 -1
- package/dist/components/TimeInput/time-input.variants.js +16 -1
- package/dist/internal/field-chrome.d.ts +18 -1
- package/dist/internal/field-chrome.js +18 -1
- package/dist/primitives/Avatar/avatar.variants.js +4 -1
- package/dist/primitives/Badge/badge.variants.js +11 -2
- package/dist/primitives/Button/button.variants.js +4 -1
- package/dist/primitives/Card/card.variants.js +4 -1
- package/dist/primitives/Checkbox/checkbox.variants.js +7 -1
- package/dist/primitives/Combobox/Combobox.svelte +9 -1
- package/dist/primitives/Combobox/combobox.variants.d.ts +25 -0
- package/dist/primitives/Combobox/combobox.variants.js +32 -3
- package/dist/primitives/Combobox/index.d.ts +8 -2
- package/dist/primitives/Dialog/dialog.variants.js +4 -1
- package/dist/primitives/Drawer/drawer.variants.js +4 -1
- package/dist/primitives/Input/input.variants.js +17 -1
- package/dist/primitives/Menu/menu.variants.js +5 -1
- package/dist/primitives/Progress/progress.variants.js +5 -2
- package/dist/primitives/RadioGroup/radioGroup.variants.js +68 -9
- package/dist/primitives/Select/select.variants.js +13 -4
- package/dist/primitives/Slider/Slider.svelte +4 -1
- package/dist/primitives/Slider/slider.variants.js +9 -2
- package/dist/primitives/Textarea/textarea.variants.js +15 -1
- package/dist/primitives/Toast/toast.variants.js +5 -1
- package/dist/primitives/Toggle/toggle.variants.js +92 -18
- package/dist/primitives/Tooltip/tooltip.variants.d.ts +3 -3
- package/dist/primitives/Tooltip/tooltip.variants.js +5 -1
- package/dist/utils/persistent-state.svelte.d.ts +25 -1
- package/dist/utils/persistent-state.svelte.js +98 -10
- package/docs/MIGRATION-v5.md +29 -0
- package/package.json +3 -3
|
@@ -0,0 +1,831 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
Accordion,
|
|
4
|
+
AccordionItem,
|
|
5
|
+
Alert,
|
|
6
|
+
Avatar,
|
|
7
|
+
Badge,
|
|
8
|
+
Button,
|
|
9
|
+
Card,
|
|
10
|
+
Checkbox,
|
|
11
|
+
Input,
|
|
12
|
+
Progress,
|
|
13
|
+
RadioGroup,
|
|
14
|
+
RadioItem,
|
|
15
|
+
Select,
|
|
16
|
+
Separator,
|
|
17
|
+
Skeleton,
|
|
18
|
+
Slider,
|
|
19
|
+
Stepper,
|
|
20
|
+
StepperStep,
|
|
21
|
+
Textarea,
|
|
22
|
+
Toggle
|
|
23
|
+
} from '../../../../primitives';
|
|
24
|
+
import { resolveIcon } from '../../../../icons';
|
|
25
|
+
import DangerCircleIconDefault from '../../../../icons/DangerCircleIcon.svelte';
|
|
26
|
+
import { fromDateInputValue, toDateInputValue } from '../../../../utils/date';
|
|
27
|
+
import DatePicker from '../../../DatePicker/DatePicker.svelte';
|
|
28
|
+
import EmptyState from '../../../EmptyState/EmptyState.svelte';
|
|
29
|
+
import TimeInput from '../../../TimeInput/TimeInput.svelte';
|
|
30
|
+
import StreamingMarkdown from '../../StreamingMarkdown/StreamingMarkdown.svelte';
|
|
31
|
+
import { checkImageUrl, checkLinkUrl } from '../../markdown/url-policy.js';
|
|
32
|
+
import type { A2uiActionEvent } from '../a2ui.types';
|
|
33
|
+
import { coerceText, dedupeOptions, splitDateTime } from '../a2ui-node-common';
|
|
34
|
+
import type { A2uiNodeProps, A2uiRenderNode } from '../a2ui-render';
|
|
35
|
+
import { bindingPointer, toInputString, toStringArray } from '../a2ui-render';
|
|
36
|
+
import { A2UI_SVG_PATH_RE } from '../a2ui-registry';
|
|
37
|
+
import { URBICON_A2UI_REGISTRY } from './a2ui-urbicon-registry';
|
|
38
|
+
import { a2uiUrbiconVariants } from './a2ui-urbicon.variants';
|
|
39
|
+
|
|
40
|
+
// Recursive dispatcher for the Urbicon A2UI catalog — the sibling of A2UINode,
|
|
41
|
+
// mapping the richer Urbicon vocabulary (real intents/variants/sizes, Section,
|
|
42
|
+
// RichText, Accordion) onto real Urbicon components. Same contract as A2UINode:
|
|
43
|
+
// children come pre-expanded on `node.children`; recursion runs through the
|
|
44
|
+
// `renderChild` snippet A2UIView threads down; the render-time plumbing is
|
|
45
|
+
// deliberately duplicated (not shared) so this stays a self-contained renderer.
|
|
46
|
+
let { node, context, renderChild }: A2uiNodeProps = $props();
|
|
47
|
+
|
|
48
|
+
const styles = a2uiUrbiconVariants();
|
|
49
|
+
const DangerIcon = resolveIcon('danger', DangerCircleIconDefault);
|
|
50
|
+
|
|
51
|
+
// Narrow a validated enum string to the exact component union it feeds. Safe:
|
|
52
|
+
// every value is registry-validated to be a subset of the real component axis
|
|
53
|
+
// (the drift test enforces `values ⊆ axis`), so this cast can never widen past
|
|
54
|
+
// what the component accepts.
|
|
55
|
+
function u<T extends string>(value: string): T {
|
|
56
|
+
return value as T;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Local fallback state for inputs whose `value`/`current` is a literal (no
|
|
60
|
+
// data-model path to write back into). Bound inputs bypass these entirely.
|
|
61
|
+
let localText = $state<string | undefined>(undefined);
|
|
62
|
+
let localBool = $state<boolean | undefined>(undefined);
|
|
63
|
+
let localNum = $state<number | undefined>(undefined);
|
|
64
|
+
let localStr = $state<string | undefined>(undefined);
|
|
65
|
+
let localList = $state<string[] | undefined>(undefined);
|
|
66
|
+
|
|
67
|
+
const instance = $derived(node.instance);
|
|
68
|
+
const nodeProps = $derived(instance?.props);
|
|
69
|
+
const component = $derived(instance?.component ?? '');
|
|
70
|
+
const spec = $derived(component ? URBICON_A2UI_REGISTRY[component] : undefined);
|
|
71
|
+
|
|
72
|
+
const missingRequired = $derived.by(() => {
|
|
73
|
+
if (!spec || !instance) return false;
|
|
74
|
+
for (const [key, propSpec] of Object.entries(spec.props)) {
|
|
75
|
+
if (propSpec.required && !instance.props.has(key)) return true;
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
function raw(key: string): unknown {
|
|
81
|
+
return nodeProps?.get(key);
|
|
82
|
+
}
|
|
83
|
+
/** The resolved child node that came from a named childId slot (Card header/footer/child, …). */
|
|
84
|
+
function childByKey(slot: string): A2uiRenderNode | undefined {
|
|
85
|
+
return node.children.find((child) => child.slot === slot);
|
|
86
|
+
}
|
|
87
|
+
function enumOr(key: string, fallback: string): string {
|
|
88
|
+
const value = nodeProps?.get(key);
|
|
89
|
+
return typeof value === 'string' ? value : fallback;
|
|
90
|
+
}
|
|
91
|
+
function numOr(value: unknown, fallback: number): number {
|
|
92
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
|
|
93
|
+
}
|
|
94
|
+
function resolved(key: string): unknown {
|
|
95
|
+
return context.resolve(nodeProps?.get(key), node.scopePrefix).value;
|
|
96
|
+
}
|
|
97
|
+
function resolvedText(key: string): string {
|
|
98
|
+
return coerceText(resolved(key));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const ariaLabel = $derived.by(() => {
|
|
102
|
+
const a11y = nodeProps?.get('accessibility');
|
|
103
|
+
if (a11y !== null && typeof a11y === 'object') {
|
|
104
|
+
const label = (a11y as Record<string, unknown>).label;
|
|
105
|
+
if (typeof label === 'string' && label.length > 0) return label;
|
|
106
|
+
}
|
|
107
|
+
return undefined;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const weightStyle = $derived(node.weight != null ? `flex-grow:${node.weight}` : undefined);
|
|
111
|
+
|
|
112
|
+
const blockCtx = $derived(context.inline ? { ...context, inline: false } : context);
|
|
113
|
+
const inlineCtx = $derived({ ...context, inline: true });
|
|
114
|
+
|
|
115
|
+
// ── Enum → complete class-string lookups (literals for Tailwind's scanner) ──
|
|
116
|
+
const JUSTIFY: Record<string, string> = {
|
|
117
|
+
start: 'justify-start',
|
|
118
|
+
center: 'justify-center',
|
|
119
|
+
end: 'justify-end',
|
|
120
|
+
spaceBetween: 'justify-between',
|
|
121
|
+
spaceAround: 'justify-around',
|
|
122
|
+
spaceEvenly: 'justify-evenly',
|
|
123
|
+
stretch: 'justify-stretch'
|
|
124
|
+
};
|
|
125
|
+
const ALIGN: Record<string, string> = {
|
|
126
|
+
start: 'items-start',
|
|
127
|
+
center: 'items-center',
|
|
128
|
+
end: 'items-end',
|
|
129
|
+
stretch: 'items-stretch'
|
|
130
|
+
};
|
|
131
|
+
const DIRECTION: Record<string, string> = { vertical: 'flex-col', horizontal: 'flex-row' };
|
|
132
|
+
const FIT: Record<string, string> = {
|
|
133
|
+
contain: 'object-contain',
|
|
134
|
+
cover: 'object-cover',
|
|
135
|
+
fill: 'object-fill',
|
|
136
|
+
none: 'object-none',
|
|
137
|
+
scaleDown: 'object-scale-down'
|
|
138
|
+
};
|
|
139
|
+
const IMAGE_SIZE: Record<string, string> = {
|
|
140
|
+
icon: 'h-6 w-6',
|
|
141
|
+
avatar: 'h-10 w-10 rounded-full',
|
|
142
|
+
smallFeature: 'h-24 w-auto',
|
|
143
|
+
mediumFeature: 'h-40 w-auto',
|
|
144
|
+
largeFeature: 'h-64 w-auto',
|
|
145
|
+
header: 'h-48 w-full'
|
|
146
|
+
};
|
|
147
|
+
const HEADING_SIZE: Record<string, string> = {
|
|
148
|
+
h1: 'text-2xl',
|
|
149
|
+
h2: 'text-xl',
|
|
150
|
+
h3: 'text-lg',
|
|
151
|
+
h4: 'text-base',
|
|
152
|
+
h5: 'text-sm'
|
|
153
|
+
};
|
|
154
|
+
const HEADING_TAG: Record<string, string> = { h1: 'h1', h2: 'h2', h3: 'h3', h4: 'h4', h5: 'h5' };
|
|
155
|
+
|
|
156
|
+
// ── Icon resolution (Icon.name and EmptyState.icon) ────────────────────────
|
|
157
|
+
function iconNameFrom(key: string): string | undefined {
|
|
158
|
+
const value = raw(key);
|
|
159
|
+
if (typeof value === 'string') return value;
|
|
160
|
+
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
161
|
+
const object = value as Record<string, unknown>;
|
|
162
|
+
if (typeof object.svgPath === 'string') return undefined;
|
|
163
|
+
if (typeof object.path === 'string') {
|
|
164
|
+
const r = context.resolve(value, node.scopePrefix).value;
|
|
165
|
+
return typeof r === 'string' ? r : undefined;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
const iconName = $derived(iconNameFrom('name'));
|
|
171
|
+
const iconSvgPath = $derived.by<string | undefined>(() => {
|
|
172
|
+
const value = raw('name');
|
|
173
|
+
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
174
|
+
const svgPath = (value as Record<string, unknown>).svgPath;
|
|
175
|
+
if (typeof svgPath === 'string' && A2UI_SVG_PATH_RE.test(svgPath)) return svgPath;
|
|
176
|
+
}
|
|
177
|
+
return undefined;
|
|
178
|
+
});
|
|
179
|
+
const IconComp = $derived((iconName && context.icons[iconName]) || context.fallbackIcon);
|
|
180
|
+
const emptyStateIcon = $derived.by(() => {
|
|
181
|
+
const name = iconNameFrom('icon');
|
|
182
|
+
return name ? context.icons[name] : undefined;
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// ── Action (Button) ─────────────────────────────────────────────────────────
|
|
186
|
+
const actionEvent = $derived.by(() => {
|
|
187
|
+
const action = raw('action');
|
|
188
|
+
if (action === null || typeof action !== 'object') return undefined;
|
|
189
|
+
const event = (action as Record<string, unknown>).event;
|
|
190
|
+
if (event === null || typeof event !== 'object') return undefined;
|
|
191
|
+
const name = (event as Record<string, unknown>).name;
|
|
192
|
+
return typeof name === 'string' ? (event as Record<string, unknown>) : undefined;
|
|
193
|
+
});
|
|
194
|
+
const openUrlArg = $derived.by<string | undefined>(() => {
|
|
195
|
+
const action = raw('action');
|
|
196
|
+
if (action === null || typeof action !== 'object') return undefined;
|
|
197
|
+
const fc = (action as Record<string, unknown>).functionCall;
|
|
198
|
+
if (fc === null || typeof fc !== 'object') return undefined;
|
|
199
|
+
if ((fc as Record<string, unknown>).call !== 'openUrl') return undefined;
|
|
200
|
+
const args = (fc as Record<string, unknown>).args;
|
|
201
|
+
const url =
|
|
202
|
+
args !== null && typeof args === 'object' ? (args as Record<string, unknown>).url : undefined;
|
|
203
|
+
const resolvedUrl = context.resolve(url, node.scopePrefix).value;
|
|
204
|
+
return typeof resolvedUrl === 'string' ? resolvedUrl : undefined;
|
|
205
|
+
});
|
|
206
|
+
const buttonUsable = $derived(actionEvent !== undefined || openUrlArg !== undefined);
|
|
207
|
+
const buttonDisabled = $derived(!buttonUsable || Boolean(resolved('disabled')));
|
|
208
|
+
|
|
209
|
+
function dispatchButton() {
|
|
210
|
+
if (actionEvent !== undefined) {
|
|
211
|
+
dispatchAction();
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (openUrlArg !== undefined) {
|
|
215
|
+
const check = checkLinkUrl(openUrlArg, context.urlPolicy);
|
|
216
|
+
if (check.ok) window.open(check.href, '_blank', 'noopener,noreferrer');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function dispatchAction() {
|
|
220
|
+
const event = actionEvent;
|
|
221
|
+
if (!event || !instance) return;
|
|
222
|
+
const resolvedContext: Record<string, unknown> = {};
|
|
223
|
+
const rawContext = event.context;
|
|
224
|
+
if (rawContext !== null && typeof rawContext === 'object' && !Array.isArray(rawContext)) {
|
|
225
|
+
for (const key of Object.keys(rawContext as Record<string, unknown>)) {
|
|
226
|
+
if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue;
|
|
227
|
+
resolvedContext[key] = context.resolve(
|
|
228
|
+
(rawContext as Record<string, unknown>)[key],
|
|
229
|
+
node.scopePrefix
|
|
230
|
+
).value;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// `sendDataModel` surfaces ship the whole model alongside the context, so a
|
|
234
|
+
// context the agent under-specified still tells it what the user entered.
|
|
235
|
+
const dataModel = context.actionDataModel?.();
|
|
236
|
+
const payload: A2uiActionEvent = {
|
|
237
|
+
...(dataModel === undefined ? {} : { dataModel }),
|
|
238
|
+
name: event.name as string,
|
|
239
|
+
surfaceId: context.surfaceId,
|
|
240
|
+
sourceComponentId: instance.id,
|
|
241
|
+
timestamp: new Date().toISOString(),
|
|
242
|
+
context: resolvedContext
|
|
243
|
+
};
|
|
244
|
+
context.onAction?.(payload);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ── Two-way inputs ──────────────────────────────────────────────────────────
|
|
248
|
+
function valuePointer(): string | undefined {
|
|
249
|
+
return bindingPointer(raw('value'), node.scopePrefix);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const textValue = $derived.by(() => {
|
|
253
|
+
const pointer = valuePointer();
|
|
254
|
+
if (pointer) return toInputString(resolved('value'));
|
|
255
|
+
return localText ?? toInputString(raw('value'));
|
|
256
|
+
});
|
|
257
|
+
function onTextInput(event: Event) {
|
|
258
|
+
const target = event.currentTarget as HTMLInputElement | HTMLTextAreaElement;
|
|
259
|
+
const pointer = valuePointer();
|
|
260
|
+
if (pointer) context.write(pointer, target.value);
|
|
261
|
+
else localText = target.value;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const boolValue = $derived.by(() => {
|
|
265
|
+
const pointer = valuePointer();
|
|
266
|
+
if (pointer) return Boolean(resolved('value'));
|
|
267
|
+
return localBool ?? (typeof raw('value') === 'boolean' ? (raw('value') as boolean) : false);
|
|
268
|
+
});
|
|
269
|
+
function onBoolChange(checked: boolean) {
|
|
270
|
+
const pointer = valuePointer();
|
|
271
|
+
if (pointer) context.write(pointer, checked);
|
|
272
|
+
else localBool = checked;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const sliderMin = $derived(numOr(raw('min'), 0));
|
|
276
|
+
const sliderMax = $derived(numOr(raw('max'), 100));
|
|
277
|
+
const sliderValue = $derived.by(() => {
|
|
278
|
+
const pointer = valuePointer();
|
|
279
|
+
if (pointer) {
|
|
280
|
+
const value = resolved('value');
|
|
281
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : sliderMin;
|
|
282
|
+
}
|
|
283
|
+
return localNum ?? (typeof raw('value') === 'number' ? (raw('value') as number) : sliderMin);
|
|
284
|
+
});
|
|
285
|
+
function onSliderChange(value: number | [number, number]) {
|
|
286
|
+
const scalar = Array.isArray(value) ? value[0] : value;
|
|
287
|
+
const pointer = valuePointer();
|
|
288
|
+
if (pointer) context.write(pointer, scalar);
|
|
289
|
+
else localNum = scalar;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// RadioGroup: value is a single string in the data model.
|
|
293
|
+
const radioValue = $derived.by(() => {
|
|
294
|
+
const pointer = valuePointer();
|
|
295
|
+
if (pointer) {
|
|
296
|
+
const v = resolved('value');
|
|
297
|
+
return typeof v === 'string' ? v : undefined;
|
|
298
|
+
}
|
|
299
|
+
return localStr ?? (typeof raw('value') === 'string' ? (raw('value') as string) : undefined);
|
|
300
|
+
});
|
|
301
|
+
function onRadioChange(value: string) {
|
|
302
|
+
const pointer = valuePointer();
|
|
303
|
+
if (pointer) context.write(pointer, value);
|
|
304
|
+
else localStr = value;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Select: value is ALWAYS a string array in the data model. Single-select
|
|
308
|
+
// adapts to Select's single-value shape (value = first, write a one-element
|
|
309
|
+
// array); multi-select maps straight through.
|
|
310
|
+
const selectValues = $derived.by(() => {
|
|
311
|
+
const pointer = valuePointer();
|
|
312
|
+
if (pointer) return toStringArray(resolved('value'));
|
|
313
|
+
return localList ?? toStringArray(raw('value'));
|
|
314
|
+
});
|
|
315
|
+
const selectMultiple = $derived(raw('multiple') === true);
|
|
316
|
+
function writeSelect(next: string[]) {
|
|
317
|
+
const pointer = valuePointer();
|
|
318
|
+
if (pointer) context.write(pointer, next);
|
|
319
|
+
else localList = next;
|
|
320
|
+
}
|
|
321
|
+
// Select is generic over string | number | boolean; our option values are
|
|
322
|
+
// always strings, so coerce on write-back to keep the model a string array.
|
|
323
|
+
function onSelectSingle(value: string | number | boolean | null) {
|
|
324
|
+
// Only null clears; an empty-string option value is a real (if degenerate)
|
|
325
|
+
// selection and must round-trip as [''], not [] (which would deselect it).
|
|
326
|
+
writeSelect(value === null ? [] : [String(value)]);
|
|
327
|
+
}
|
|
328
|
+
function onSelectMultiple(value: Array<string | number | boolean>) {
|
|
329
|
+
writeSelect(value.map((item) => String(item)));
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const choiceOptions = $derived(
|
|
333
|
+
// `options` may itself be a { path } binding — that is how an agent shows a
|
|
334
|
+
// list it fetched mid-conversation (free slots, search hits) without
|
|
335
|
+
// rewriting the component. Resolve it first, then the per-option labels.
|
|
336
|
+
dedupeOptions(resolved('options'), (label) =>
|
|
337
|
+
coerceText(context.resolve(label, node.scopePrefix).value)
|
|
338
|
+
)
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
// ── DateTime (separate DatePicker / TimeInput) ─────────────────────────────
|
|
342
|
+
const dtValue = $derived.by(() => {
|
|
343
|
+
const pointer = valuePointer();
|
|
344
|
+
if (pointer) return toInputString(resolved('value'));
|
|
345
|
+
return localText ?? toInputString(raw('value'));
|
|
346
|
+
});
|
|
347
|
+
const dtParts = $derived(splitDateTime(dtValue));
|
|
348
|
+
const dtMinParts = $derived(splitDateTime(resolvedText('min')));
|
|
349
|
+
const dtMaxParts = $derived(splitDateTime(resolvedText('max')));
|
|
350
|
+
const dtMinDate = $derived(
|
|
351
|
+
dtMinParts.date ? (fromDateInputValue(dtMinParts.date) ?? undefined) : undefined
|
|
352
|
+
);
|
|
353
|
+
const dtMaxDate = $derived(
|
|
354
|
+
dtMaxParts.date ? (fromDateInputValue(dtMaxParts.date) ?? undefined) : undefined
|
|
355
|
+
);
|
|
356
|
+
function writeDateTime(next: string): void {
|
|
357
|
+
const pointer = valuePointer();
|
|
358
|
+
if (pointer) context.write(pointer, next);
|
|
359
|
+
else localText = next;
|
|
360
|
+
}
|
|
361
|
+
function onDateChange(picked: Date | undefined): void {
|
|
362
|
+
writeDateTime(picked ? toDateInputValue(picked) : '');
|
|
363
|
+
}
|
|
364
|
+
function onTimeChange(next: string | null): void {
|
|
365
|
+
writeDateTime(next ?? '');
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ── Stepper / Accordion display data ───────────────────────────────────────
|
|
369
|
+
const stepperSteps = $derived.by(() => {
|
|
370
|
+
const steps = raw('steps');
|
|
371
|
+
if (!Array.isArray(steps)) return [] as string[];
|
|
372
|
+
return steps.filter((s): s is string => typeof s === 'string');
|
|
373
|
+
});
|
|
374
|
+
const stepperCurrent = $derived.by(() => {
|
|
375
|
+
const v = resolved('current');
|
|
376
|
+
return typeof v === 'number' && Number.isFinite(v) ? Math.max(0, Math.floor(v)) : 0;
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// Accordion: pair each labeledChildren item's label with the resolved child
|
|
380
|
+
// node (aligned by index — childRefs pushes one child per item). Each item
|
|
381
|
+
// gets a stable, unique AccordionItem value so open-state and the keyed each
|
|
382
|
+
// never collide, even on duplicate/dangling child ids.
|
|
383
|
+
const accordionItems = $derived.by(() => {
|
|
384
|
+
const items = raw('items');
|
|
385
|
+
if (!Array.isArray(items))
|
|
386
|
+
return [] as Array<{ value: string; label: string; child: A2uiRenderNode | undefined }>;
|
|
387
|
+
// Pair each item's label with its resolved panel by consuming child nodes by
|
|
388
|
+
// id (in order), NOT by raw index: a cyclic item.child is dropped from
|
|
389
|
+
// node.children entirely (no placeholder, unlike a dangling ref), which would
|
|
390
|
+
// shift every later panel up by one under a positional zip. Consume-by-id
|
|
391
|
+
// keeps labels aligned and leaves a cyclic/missing panel empty.
|
|
392
|
+
const pool = node.children.filter((child) => child.slot === 'items');
|
|
393
|
+
const used = new Set<number>();
|
|
394
|
+
return items.map((item, i) => {
|
|
395
|
+
const childId =
|
|
396
|
+
item !== null && typeof item === 'object' ? (item as { child?: unknown }).child : undefined;
|
|
397
|
+
let matched: A2uiRenderNode | undefined;
|
|
398
|
+
if (typeof childId === 'string') {
|
|
399
|
+
for (let j = 0; j < pool.length; j++) {
|
|
400
|
+
if (!used.has(j) && pool[j].id === childId) {
|
|
401
|
+
matched = pool[j];
|
|
402
|
+
used.add(j);
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return {
|
|
408
|
+
value: `item-${i}`,
|
|
409
|
+
label:
|
|
410
|
+
item !== null && typeof item === 'object'
|
|
411
|
+
? coerceText(
|
|
412
|
+
context.resolve((item as { label?: unknown }).label, node.scopePrefix).value
|
|
413
|
+
)
|
|
414
|
+
: '',
|
|
415
|
+
child: matched
|
|
416
|
+
};
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
const accordionMultiple = $derived(raw('multiple') === true);
|
|
420
|
+
|
|
421
|
+
// ── Field-error (Input/Textarea use the native prop; date/time show a line) ──
|
|
422
|
+
const fieldError = $derived(resolvedText('error'));
|
|
423
|
+
|
|
424
|
+
// ── Image ──────────────────────────────────────────────────────────────────
|
|
425
|
+
const imageCheck = $derived(checkImageUrl(resolvedText('url'), context.urlPolicy));
|
|
426
|
+
// Avatar src is gated through the same policy; a blocked URL falls back to initials.
|
|
427
|
+
const avatarSrc = $derived.by(() => {
|
|
428
|
+
const src = resolvedText('src');
|
|
429
|
+
if (!src) return undefined;
|
|
430
|
+
const check = checkImageUrl(src, context.urlPolicy);
|
|
431
|
+
return check.ok ? check.href : undefined;
|
|
432
|
+
});
|
|
433
|
+
</script>
|
|
434
|
+
|
|
435
|
+
{#snippet faultChip(label: string)}
|
|
436
|
+
<span class={context.classes.errorChip}>
|
|
437
|
+
<DangerIcon size={14} class={context.classes.errorIcon} />
|
|
438
|
+
{label}
|
|
439
|
+
</span>
|
|
440
|
+
{/snippet}
|
|
441
|
+
|
|
442
|
+
{#if instance === null}
|
|
443
|
+
{#if context.streaming}
|
|
444
|
+
<span class={context.classes.pending}>
|
|
445
|
+
<Skeleton variant="text" width="6rem" />
|
|
446
|
+
<span class="sr-only">{context.labels.pending}</span>
|
|
447
|
+
</span>
|
|
448
|
+
{:else}
|
|
449
|
+
{@render faultChip(`${context.labels.unsupported}: ${node.id}`)}
|
|
450
|
+
{/if}
|
|
451
|
+
{:else if !spec || missingRequired}
|
|
452
|
+
{@render faultChip(`${context.labels.unsupported}: ${component || node.id}`)}
|
|
453
|
+
{:else if component === 'Column'}
|
|
454
|
+
<div
|
|
455
|
+
class={[
|
|
456
|
+
context.classes.column,
|
|
457
|
+
JUSTIFY[enumOr('justify', 'start')],
|
|
458
|
+
ALIGN[enumOr('align', 'stretch')]
|
|
459
|
+
]}
|
|
460
|
+
style={weightStyle}
|
|
461
|
+
role={ariaLabel ? 'group' : undefined}
|
|
462
|
+
aria-label={ariaLabel}
|
|
463
|
+
>
|
|
464
|
+
{#each node.children as child (child.key)}
|
|
465
|
+
{@render renderChild(child, blockCtx)}
|
|
466
|
+
{/each}
|
|
467
|
+
</div>
|
|
468
|
+
{:else if component === 'Row'}
|
|
469
|
+
<div
|
|
470
|
+
class={[
|
|
471
|
+
context.classes.row,
|
|
472
|
+
JUSTIFY[enumOr('justify', 'start')],
|
|
473
|
+
ALIGN[enumOr('align', 'center')]
|
|
474
|
+
]}
|
|
475
|
+
style={weightStyle}
|
|
476
|
+
role={ariaLabel ? 'group' : undefined}
|
|
477
|
+
aria-label={ariaLabel}
|
|
478
|
+
>
|
|
479
|
+
{#each node.children as child (child.key)}
|
|
480
|
+
{@render renderChild(child, blockCtx)}
|
|
481
|
+
{/each}
|
|
482
|
+
</div>
|
|
483
|
+
{:else if component === 'List'}
|
|
484
|
+
<ul
|
|
485
|
+
class={[context.classes.list, DIRECTION[enumOr('direction', 'vertical')]]}
|
|
486
|
+
style={weightStyle}
|
|
487
|
+
aria-label={ariaLabel}
|
|
488
|
+
>
|
|
489
|
+
{#each node.children as child (child.key)}
|
|
490
|
+
<li class={context.classes.listItem}>
|
|
491
|
+
{@render renderChild(child, blockCtx)}
|
|
492
|
+
</li>
|
|
493
|
+
{/each}
|
|
494
|
+
</ul>
|
|
495
|
+
{:else if component === 'Card'}
|
|
496
|
+
{@const headerNode = childByKey('header')}
|
|
497
|
+
{@const footerNode = childByKey('footer')}
|
|
498
|
+
{@const bodyNode = childByKey('child')}
|
|
499
|
+
{#snippet cardHeader()}
|
|
500
|
+
{#if headerNode}{@render renderChild(headerNode, blockCtx)}{/if}
|
|
501
|
+
{/snippet}
|
|
502
|
+
{#snippet cardFooter()}
|
|
503
|
+
{#if footerNode}{@render renderChild(footerNode, blockCtx)}{/if}
|
|
504
|
+
{/snippet}
|
|
505
|
+
<Card
|
|
506
|
+
variant={u(enumOr('variant', 'quiet'))}
|
|
507
|
+
aria-label={ariaLabel}
|
|
508
|
+
header={headerNode ? cardHeader : undefined}
|
|
509
|
+
footer={footerNode ? cardFooter : undefined}
|
|
510
|
+
>
|
|
511
|
+
{#if bodyNode}{@render renderChild(bodyNode, blockCtx)}{/if}
|
|
512
|
+
</Card>
|
|
513
|
+
{:else if component === 'Separator'}
|
|
514
|
+
<Separator
|
|
515
|
+
orientation={enumOr('orientation', 'horizontal') === 'vertical' ? 'vertical' : 'horizontal'}
|
|
516
|
+
style={weightStyle}
|
|
517
|
+
/>
|
|
518
|
+
{:else if component === 'Section'}
|
|
519
|
+
<section class={styles.section()} style={weightStyle} aria-label={ariaLabel}>
|
|
520
|
+
<div class={styles.sectionHeader()}>
|
|
521
|
+
<h3 class={styles.sectionTitle()}>{resolvedText('title')}</h3>
|
|
522
|
+
{#if resolvedText('description')}
|
|
523
|
+
<p class={styles.sectionDescription()}>{resolvedText('description')}</p>
|
|
524
|
+
{/if}
|
|
525
|
+
</div>
|
|
526
|
+
{#if childByKey('child')}
|
|
527
|
+
{@render renderChild(childByKey('child')!, blockCtx)}
|
|
528
|
+
{/if}
|
|
529
|
+
</section>
|
|
530
|
+
{:else if component === 'EmptyState'}
|
|
531
|
+
{@const ctaNode = childByKey('cta')}
|
|
532
|
+
{#snippet emptyCta()}
|
|
533
|
+
{#if ctaNode}{@render renderChild(ctaNode, blockCtx)}{/if}
|
|
534
|
+
{/snippet}
|
|
535
|
+
<EmptyState
|
|
536
|
+
title={resolvedText('title')}
|
|
537
|
+
description={resolvedText('description') || undefined}
|
|
538
|
+
icon={emptyStateIcon}
|
|
539
|
+
cta={ctaNode ? emptyCta : undefined}
|
|
540
|
+
/>
|
|
541
|
+
{:else if component === 'Text'}
|
|
542
|
+
{@const value = resolvedText('text')}
|
|
543
|
+
{@const variant = enumOr('variant', 'body')}
|
|
544
|
+
{#if context.inline}
|
|
545
|
+
<span class={context.classes.inlineText} style={weightStyle}>{value}</span>
|
|
546
|
+
{:else if variant === 'caption'}
|
|
547
|
+
<span class={context.classes.caption} style={weightStyle}>{value}</span>
|
|
548
|
+
{:else if variant === 'body'}
|
|
549
|
+
<p class={styles.body()} style={weightStyle}>{value}</p>
|
|
550
|
+
{:else}
|
|
551
|
+
<svelte:element
|
|
552
|
+
this={HEADING_TAG[variant] ?? 'h3'}
|
|
553
|
+
class={[context.classes.heading, HEADING_SIZE[variant]]}
|
|
554
|
+
style={weightStyle}
|
|
555
|
+
>
|
|
556
|
+
{value}
|
|
557
|
+
</svelte:element>
|
|
558
|
+
{/if}
|
|
559
|
+
{:else if component === 'RichText'}
|
|
560
|
+
<StreamingMarkdown
|
|
561
|
+
content={resolvedText('content')}
|
|
562
|
+
size="sm"
|
|
563
|
+
urlPolicy={context.urlPolicy}
|
|
564
|
+
streaming={false}
|
|
565
|
+
style={weightStyle}
|
|
566
|
+
/>
|
|
567
|
+
{:else if component === 'Button'}
|
|
568
|
+
<Button
|
|
569
|
+
intent={u(enumOr('intent', 'neutral'))}
|
|
570
|
+
variant={u(enumOr('variant', 'filled'))}
|
|
571
|
+
size={u(enumOr('size', 'md'))}
|
|
572
|
+
style={weightStyle}
|
|
573
|
+
aria-label={ariaLabel}
|
|
574
|
+
disabled={buttonDisabled}
|
|
575
|
+
title={buttonUsable ? undefined : 'This action is not supported'}
|
|
576
|
+
onclick={buttonUsable ? dispatchButton : undefined}
|
|
577
|
+
>
|
|
578
|
+
{#if node.children[0]}
|
|
579
|
+
{@render renderChild(node.children[0], inlineCtx)}
|
|
580
|
+
{/if}
|
|
581
|
+
</Button>
|
|
582
|
+
{#if !buttonUsable}
|
|
583
|
+
<span class="sr-only">This action is not supported</span>
|
|
584
|
+
{/if}
|
|
585
|
+
{:else if component === 'Input'}
|
|
586
|
+
<Input
|
|
587
|
+
label={resolvedText('label')}
|
|
588
|
+
type={u(enumOr('inputType', 'text'))}
|
|
589
|
+
value={textValue}
|
|
590
|
+
placeholder={resolvedText('placeholder') || undefined}
|
|
591
|
+
error={fieldError || undefined}
|
|
592
|
+
oninput={onTextInput}
|
|
593
|
+
style={weightStyle}
|
|
594
|
+
aria-label={ariaLabel}
|
|
595
|
+
/>
|
|
596
|
+
{:else if component === 'Textarea'}
|
|
597
|
+
<Textarea
|
|
598
|
+
label={resolvedText('label')}
|
|
599
|
+
value={textValue}
|
|
600
|
+
placeholder={resolvedText('placeholder') || undefined}
|
|
601
|
+
error={fieldError || undefined}
|
|
602
|
+
rows={typeof raw('rows') === 'number' ? (raw('rows') as number) : undefined}
|
|
603
|
+
oninput={onTextInput}
|
|
604
|
+
style={weightStyle}
|
|
605
|
+
aria-label={ariaLabel}
|
|
606
|
+
/>
|
|
607
|
+
{:else if component === 'Checkbox'}
|
|
608
|
+
<Checkbox
|
|
609
|
+
label={resolvedText('label')}
|
|
610
|
+
checked={boolValue}
|
|
611
|
+
onCheckedChange={onBoolChange}
|
|
612
|
+
style={weightStyle}
|
|
613
|
+
aria-label={ariaLabel}
|
|
614
|
+
/>
|
|
615
|
+
{:else if component === 'RadioGroup'}
|
|
616
|
+
<RadioGroup
|
|
617
|
+
label={resolvedText('label') || ariaLabel || undefined}
|
|
618
|
+
value={radioValue}
|
|
619
|
+
onValueChange={onRadioChange}
|
|
620
|
+
orientation={enumOr('orientation', 'vertical') === 'horizontal' ? 'horizontal' : 'vertical'}
|
|
621
|
+
style={weightStyle}
|
|
622
|
+
>
|
|
623
|
+
{#each choiceOptions as option (option.value)}
|
|
624
|
+
<RadioItem value={option.value} label={option.label} />
|
|
625
|
+
{/each}
|
|
626
|
+
</RadioGroup>
|
|
627
|
+
{:else if component === 'Select'}
|
|
628
|
+
{#if selectMultiple}
|
|
629
|
+
<Select
|
|
630
|
+
multiple
|
|
631
|
+
label={resolvedText('label') || undefined}
|
|
632
|
+
placeholder={resolvedText('placeholder') || undefined}
|
|
633
|
+
options={choiceOptions}
|
|
634
|
+
value={selectValues}
|
|
635
|
+
onValueChange={onSelectMultiple}
|
|
636
|
+
style={weightStyle}
|
|
637
|
+
aria-label={ariaLabel}
|
|
638
|
+
/>
|
|
639
|
+
{:else}
|
|
640
|
+
<Select
|
|
641
|
+
label={resolvedText('label') || undefined}
|
|
642
|
+
placeholder={resolvedText('placeholder') || undefined}
|
|
643
|
+
options={choiceOptions}
|
|
644
|
+
value={selectValues[0] ?? null}
|
|
645
|
+
onValueChange={onSelectSingle}
|
|
646
|
+
style={weightStyle}
|
|
647
|
+
aria-label={ariaLabel}
|
|
648
|
+
/>
|
|
649
|
+
{/if}
|
|
650
|
+
{:else if component === 'Slider'}
|
|
651
|
+
<!-- showValue is forced on: a generated surface has no other place to state
|
|
652
|
+
the current number, and the agent cannot ask for it (not a catalog prop). -->
|
|
653
|
+
<Slider
|
|
654
|
+
label={resolvedText('label') || undefined}
|
|
655
|
+
value={sliderValue}
|
|
656
|
+
min={sliderMin}
|
|
657
|
+
max={sliderMax}
|
|
658
|
+
step={typeof raw('step') === 'number' ? (raw('step') as number) : undefined}
|
|
659
|
+
showValue
|
|
660
|
+
onValueChange={onSliderChange}
|
|
661
|
+
style={weightStyle}
|
|
662
|
+
aria-label={ariaLabel}
|
|
663
|
+
/>
|
|
664
|
+
{:else if component === 'Toggle'}
|
|
665
|
+
<Toggle
|
|
666
|
+
label={resolvedText('label')}
|
|
667
|
+
checked={boolValue}
|
|
668
|
+
onCheckedChange={onBoolChange}
|
|
669
|
+
style={weightStyle}
|
|
670
|
+
aria-label={ariaLabel}
|
|
671
|
+
/>
|
|
672
|
+
{:else if component === 'DatePicker'}
|
|
673
|
+
{@const label = resolvedText('label')}
|
|
674
|
+
<div class={styles.field()} style={weightStyle}>
|
|
675
|
+
<DatePicker
|
|
676
|
+
label={label || ariaLabel || undefined}
|
|
677
|
+
value={dtParts.date || null}
|
|
678
|
+
onValueChange={onDateChange}
|
|
679
|
+
minDate={dtMinDate}
|
|
680
|
+
maxDate={dtMaxDate}
|
|
681
|
+
/>
|
|
682
|
+
{#if fieldError}
|
|
683
|
+
<span class={styles.fieldError()}>{fieldError}</span>
|
|
684
|
+
{/if}
|
|
685
|
+
</div>
|
|
686
|
+
{:else if component === 'TimeInput'}
|
|
687
|
+
{@const label = resolvedText('label')}
|
|
688
|
+
<div
|
|
689
|
+
class={styles.field()}
|
|
690
|
+
style={weightStyle}
|
|
691
|
+
role={ariaLabel ? 'group' : undefined}
|
|
692
|
+
aria-label={ariaLabel}
|
|
693
|
+
>
|
|
694
|
+
<TimeInput
|
|
695
|
+
label={label || undefined}
|
|
696
|
+
value={dtParts.time || null}
|
|
697
|
+
onValueChange={onTimeChange}
|
|
698
|
+
withSeconds={dtParts.time.length > 5}
|
|
699
|
+
min={dtMinParts.time && !dtMinParts.date ? dtMinParts.time : undefined}
|
|
700
|
+
max={dtMaxParts.time && !dtMaxParts.date ? dtMaxParts.time : undefined}
|
|
701
|
+
/>
|
|
702
|
+
{#if fieldError}
|
|
703
|
+
<span class={styles.fieldError()}>{fieldError}</span>
|
|
704
|
+
{/if}
|
|
705
|
+
</div>
|
|
706
|
+
{:else if component === 'Badge'}
|
|
707
|
+
{@const variant = enumOr('variant', 'filled')}
|
|
708
|
+
{#if variant === 'dot'}
|
|
709
|
+
<Badge
|
|
710
|
+
variant="dot"
|
|
711
|
+
intent={u(enumOr('intent', 'primary'))}
|
|
712
|
+
aria-label={ariaLabel || resolvedText('text') || undefined}
|
|
713
|
+
/>
|
|
714
|
+
{:else}
|
|
715
|
+
<Badge
|
|
716
|
+
variant={variant as 'filled' | 'soft' | 'outlined'}
|
|
717
|
+
intent={u(enumOr('intent', 'primary'))}
|
|
718
|
+
>
|
|
719
|
+
{resolvedText('text')}
|
|
720
|
+
</Badge>
|
|
721
|
+
{/if}
|
|
722
|
+
{:else if component === 'Alert'}
|
|
723
|
+
{@const childNode = childByKey('child')}
|
|
724
|
+
<Alert
|
|
725
|
+
title={resolvedText('title')}
|
|
726
|
+
intent={u(enumOr('intent', 'primary'))}
|
|
727
|
+
variant={u(enumOr('variant', 'soft'))}
|
|
728
|
+
style={weightStyle}
|
|
729
|
+
>
|
|
730
|
+
{#if resolvedText('description')}
|
|
731
|
+
<span>{resolvedText('description')}</span>
|
|
732
|
+
{/if}
|
|
733
|
+
{#if childNode}
|
|
734
|
+
{@render renderChild(childNode, blockCtx)}
|
|
735
|
+
{/if}
|
|
736
|
+
</Alert>
|
|
737
|
+
{:else if component === 'Progress'}
|
|
738
|
+
{@const indeterminate = raw('indeterminate') === true}
|
|
739
|
+
{@const progressValue = resolved('value')}
|
|
740
|
+
<Progress
|
|
741
|
+
value={indeterminate || typeof progressValue !== 'number' || !Number.isFinite(progressValue)
|
|
742
|
+
? undefined
|
|
743
|
+
: progressValue}
|
|
744
|
+
max={numOr(raw('max'), 100)}
|
|
745
|
+
intent={u(enumOr('intent', 'primary'))}
|
|
746
|
+
label={resolvedText('label') || undefined}
|
|
747
|
+
style={weightStyle}
|
|
748
|
+
aria-label={ariaLabel}
|
|
749
|
+
/>
|
|
750
|
+
{:else if component === 'Stepper'}
|
|
751
|
+
<Stepper
|
|
752
|
+
activeStep={stepperCurrent}
|
|
753
|
+
orientation={enumOr('orientation', 'horizontal') === 'vertical' ? 'vertical' : 'horizontal'}
|
|
754
|
+
variant={u(enumOr('variant', 'default'))}
|
|
755
|
+
style={weightStyle}
|
|
756
|
+
aria-label={ariaLabel}
|
|
757
|
+
>
|
|
758
|
+
{#each stepperSteps as step, i (`${step}#${i}`)}
|
|
759
|
+
<StepperStep label={step} />
|
|
760
|
+
{/each}
|
|
761
|
+
</Stepper>
|
|
762
|
+
{:else if component === 'Accordion'}
|
|
763
|
+
<Accordion
|
|
764
|
+
type={accordionMultiple ? 'multiple' : 'single'}
|
|
765
|
+
variant={u(enumOr('variant', 'default'))}
|
|
766
|
+
style={weightStyle}
|
|
767
|
+
aria-label={ariaLabel}
|
|
768
|
+
>
|
|
769
|
+
{#each accordionItems as item (item.value)}
|
|
770
|
+
<AccordionItem value={item.value} title={item.label}>
|
|
771
|
+
{#if item.child}
|
|
772
|
+
{@render renderChild(item.child, blockCtx)}
|
|
773
|
+
{/if}
|
|
774
|
+
</AccordionItem>
|
|
775
|
+
{/each}
|
|
776
|
+
</Accordion>
|
|
777
|
+
{:else if component === 'Avatar'}
|
|
778
|
+
<Avatar
|
|
779
|
+
src={avatarSrc}
|
|
780
|
+
name={resolvedText('name') || undefined}
|
|
781
|
+
alt={resolvedText('name') || ariaLabel || undefined}
|
|
782
|
+
variant={u(enumOr('variant', 'circle'))}
|
|
783
|
+
size={u(enumOr('size', 'md'))}
|
|
784
|
+
/>
|
|
785
|
+
{:else if component === 'Icon'}
|
|
786
|
+
{#if iconSvgPath}
|
|
787
|
+
<svg
|
|
788
|
+
viewBox="0 0 24 24"
|
|
789
|
+
width="20"
|
|
790
|
+
height="20"
|
|
791
|
+
class={context.classes.svgIcon}
|
|
792
|
+
style={weightStyle}
|
|
793
|
+
stroke-width="2"
|
|
794
|
+
stroke-linecap="round"
|
|
795
|
+
stroke-linejoin="round"
|
|
796
|
+
role={ariaLabel ? 'img' : undefined}
|
|
797
|
+
aria-label={ariaLabel}
|
|
798
|
+
aria-hidden={ariaLabel ? undefined : 'true'}
|
|
799
|
+
>
|
|
800
|
+
<path d={iconSvgPath} />
|
|
801
|
+
</svg>
|
|
802
|
+
{:else}
|
|
803
|
+
<span
|
|
804
|
+
class={context.classes.icon}
|
|
805
|
+
style={weightStyle}
|
|
806
|
+
role={ariaLabel ? 'img' : undefined}
|
|
807
|
+
aria-label={ariaLabel}
|
|
808
|
+
aria-hidden={ariaLabel ? undefined : 'true'}
|
|
809
|
+
>
|
|
810
|
+
<IconComp size={20} />
|
|
811
|
+
</span>
|
|
812
|
+
{/if}
|
|
813
|
+
{:else if component === 'Image'}
|
|
814
|
+
{@const description = resolvedText('description')}
|
|
815
|
+
{#if imageCheck.ok}
|
|
816
|
+
<img
|
|
817
|
+
src={imageCheck.href}
|
|
818
|
+
alt={description}
|
|
819
|
+
class={[
|
|
820
|
+
context.classes.image,
|
|
821
|
+
FIT[enumOr('fit', 'cover')],
|
|
822
|
+
IMAGE_SIZE[enumOr('variant', 'mediumFeature')]
|
|
823
|
+
]}
|
|
824
|
+
style={weightStyle}
|
|
825
|
+
/>
|
|
826
|
+
{:else}
|
|
827
|
+
<span class={context.classes.blockedChip} style={weightStyle}>
|
|
828
|
+
{context.labels.blockedImage}{description ? `: ${description}` : ''}
|
|
829
|
+
</span>
|
|
830
|
+
{/if}
|
|
831
|
+
{/if}
|