@judarn/ui 0.0.0-stage → 0.7.2

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.
@@ -0,0 +1,540 @@
1
+ // src/sdui/serializer.js
2
+ import * as React2 from "react";
3
+
4
+ // src/internal/cx.js
5
+ function cx(...parts) {
6
+ return parts.filter(Boolean).join(" ");
7
+ }
8
+
9
+ // src/Button/Button.jsx
10
+ import { jsx } from "react/jsx-runtime";
11
+ function Button({
12
+ children,
13
+ variant = "primary",
14
+ size = "md",
15
+ block = false,
16
+ disabled = false,
17
+ type = "button",
18
+ onClick,
19
+ className,
20
+ ...rest
21
+ }) {
22
+ const cls = cx(
23
+ "btn",
24
+ variant === "outline" && "btn--outline",
25
+ variant === "secondary" && "btn--secondary",
26
+ variant === "ghost" && "btn--ghost",
27
+ variant === "accent" && "btn--accent",
28
+ size === "sm" && "btn--sm",
29
+ size === "lg" && "btn--lg",
30
+ block && "btn--block",
31
+ className
32
+ );
33
+ return /* @__PURE__ */ jsx("button", { ...rest, className: cls, type, disabled, onClick, children });
34
+ }
35
+
36
+ // src/Badge/Badge.jsx
37
+ import { jsx as jsx2 } from "react/jsx-runtime";
38
+ var linearize = (c) => c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
39
+ function oklchToLinearSrgb(l, c, h, clip) {
40
+ const hr = h * Math.PI / 180;
41
+ const a = c * Math.cos(hr);
42
+ const b = c * Math.sin(hr);
43
+ const l_ = Math.pow(l + 0.3963377774 * a + 0.2158037573 * b, 3);
44
+ const m_ = Math.pow(l - 0.1055613458 * a - 0.0638541728 * b, 3);
45
+ const s_ = Math.pow(l - 0.0894841775 * a - 1.291485548 * b, 3);
46
+ const rgb = [
47
+ 4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_,
48
+ -1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_,
49
+ -0.0041960863 * l_ - 0.7034186147 * m_ + 1.707614701 * s_
50
+ ];
51
+ return clip ? rgb.map((v) => Math.min(1, Math.max(0, v))) : rgb;
52
+ }
53
+ function oklchToLuminance(l, c, h) {
54
+ if (l <= 0) return 0;
55
+ if (l >= 1) return 1;
56
+ const inGamut = (rgb2) => rgb2.every((v) => v >= -1e-6 && v <= 1 + 1e-6);
57
+ let rgb = oklchToLinearSrgb(l, c, h, false);
58
+ if (!inGamut(rgb)) {
59
+ let low = 0;
60
+ let high = c;
61
+ for (let i = 0; i < 24; i++) {
62
+ const mid = 0.5 * (low + high);
63
+ if (inGamut(oklchToLinearSrgb(l, mid, h, false))) low = mid;
64
+ else high = mid;
65
+ }
66
+ rgb = oklchToLinearSrgb(l, low, h, false);
67
+ }
68
+ const [r, g, b] = rgb.map((v) => Math.min(1, Math.max(0, v)));
69
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
70
+ }
71
+ function parseLuminance(input) {
72
+ const value = input.trim().toLowerCase();
73
+ if (value.startsWith("#")) {
74
+ const hex = value.slice(1);
75
+ if (!/^([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/.test(hex)) return null;
76
+ const full = hex.length <= 4 ? [...hex].map((ch) => ch + ch).join("") : hex;
77
+ const [r, g, b] = [0, 2, 4].map((i) => linearize(parseInt(full.slice(i, i + 2), 16) / 255));
78
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
79
+ }
80
+ if (value.startsWith("rgb")) {
81
+ const nums = value.replace(/\/.*$/, "").match(/\d+(\.\d+)?/g);
82
+ if (!nums || nums.length < 3) return null;
83
+ const [r, g, b] = nums.slice(0, 3).map((n) => linearize(parseFloat(n) / 255));
84
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
85
+ }
86
+ if (value.startsWith("oklch(")) {
87
+ const m = value.match(/^oklch\(\s*([\d.]+)(%?)\s+([\d.]+)\s+([\d.]+)(?:deg)?\s*(?:\/[^)]*)?\)$/);
88
+ if (!m) return null;
89
+ const l = parseFloat(m[1]) / (m[2] ? 100 : 1);
90
+ return oklchToLuminance(l, parseFloat(m[3]), parseFloat(m[4]));
91
+ }
92
+ return null;
93
+ }
94
+ function getContrastingInk(color) {
95
+ const lum = typeof color === "string" ? parseLuminance(color) : null;
96
+ if (lum === null) {
97
+ throw new TypeError(`getContrastingInk: unsupported colour "${String(color)}". Use #hex, rgb() or oklch().`);
98
+ }
99
+ const perceptualCrossover = 0.5;
100
+ return lum > perceptualCrossover ? "#000000" : "#ffffff";
101
+ }
102
+ function Badge({ children, variant = "outline", accentColor, style, className, ...rest }) {
103
+ const cls = cx(
104
+ "badge",
105
+ variant === "solid" && "badge--solid",
106
+ variant === "accent" && "badge--accent",
107
+ variant === "accent-outline" && "badge--accent-outline",
108
+ className
109
+ );
110
+ const inlineStyle = { ...style };
111
+ if (accentColor) {
112
+ if (variant === "accent") {
113
+ inlineStyle.backgroundColor = accentColor;
114
+ inlineStyle.borderColor = accentColor;
115
+ inlineStyle.color = getContrastingInk(accentColor);
116
+ } else if (variant === "accent-outline") {
117
+ inlineStyle.borderColor = accentColor;
118
+ inlineStyle.color = accentColor;
119
+ }
120
+ }
121
+ return /* @__PURE__ */ jsx2("span", { ...rest, className: cls, style: Object.keys(inlineStyle).length > 0 ? inlineStyle : void 0, children });
122
+ }
123
+ function Tag({ children, className, ...rest }) {
124
+ return /* @__PURE__ */ jsx2("span", { ...rest, className: cx("tag", className), children });
125
+ }
126
+
127
+ // src/Card/Card.jsx
128
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
129
+ function Card({
130
+ eyebrow,
131
+ eyebrowCasing = "uppercase",
132
+ title,
133
+ children,
134
+ media,
135
+ actions,
136
+ flat = false,
137
+ compact = false,
138
+ className,
139
+ ...rest
140
+ }) {
141
+ const cls = cx("card", flat && "card--flat", compact && "card--compact", className);
142
+ return /* @__PURE__ */ jsxs("div", { ...rest, className: cls, children: [
143
+ media ? /* @__PURE__ */ jsx3("div", { className: "card__media", children: media }) : null,
144
+ eyebrow ? /* @__PURE__ */ jsx3("span", { className: cx("card__eyebrow", "t-label", eyebrowCasing === "asWritten" && "t-as-written"), children: eyebrow }) : null,
145
+ title ? /* @__PURE__ */ jsx3("div", { className: "card__title", children: title }) : null,
146
+ children ? /* @__PURE__ */ jsx3("div", { className: "card__body", children }) : null,
147
+ actions ? /* @__PURE__ */ jsx3("div", { style: { display: "flex", gap: "var(--gap-tight)" }, children: actions }) : null
148
+ ] });
149
+ }
150
+
151
+ // src/DataMatrix/DataMatrix.jsx
152
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
153
+ function DataMatrix({ columns: columns2 = [], rows: rows2 = [], label = "Data Matrix", className, ...rest }) {
154
+ const cols = columns2.map((c) => typeof c === "string" ? { key: c, label: c } : c);
155
+ const totalColumns = cols.length;
156
+ const totalRows = rows2.length;
157
+ return /* @__PURE__ */ jsxs2(
158
+ "table",
159
+ {
160
+ ...rest,
161
+ className: cx("matrix", className),
162
+ role: "table",
163
+ "aria-label": label,
164
+ "aria-colcount": totalColumns,
165
+ "aria-rowcount": totalRows + 1,
166
+ children: [
167
+ /* @__PURE__ */ jsx4("thead", { role: "rowgroup", children: /* @__PURE__ */ jsx4("tr", { role: "row", "aria-rowindex": 1, children: cols.map((c, colIndex) => {
168
+ const widthStyle = c.width != null ? {
169
+ width: typeof c.width === "number" ? `${c.width}px` : c.width,
170
+ minWidth: typeof c.width === "number" ? `${c.width}px` : c.width,
171
+ maxWidth: typeof c.width === "number" ? `${c.width}px` : c.width
172
+ } : void 0;
173
+ return /* @__PURE__ */ jsx4(
174
+ "th",
175
+ {
176
+ scope: "col",
177
+ role: "columnheader",
178
+ "aria-colindex": colIndex + 1,
179
+ "aria-label": c.label,
180
+ className: [c.numeric && "num", c.casing === "asWritten" && "t-as-written"].filter(Boolean).join(" ") || void 0,
181
+ style: widthStyle,
182
+ children: c.label
183
+ },
184
+ c.key
185
+ );
186
+ }) }) }),
187
+ /* @__PURE__ */ jsx4("tbody", { role: "rowgroup", children: rows2.map((row, rowIndex) => {
188
+ const rowNum = rowIndex + 2;
189
+ return /* @__PURE__ */ jsx4(
190
+ "tr",
191
+ {
192
+ role: "row",
193
+ "aria-rowindex": rowNum,
194
+ "aria-label": `Row ${rowIndex + 1} of ${totalRows}`,
195
+ children: cols.map((c, colIndex) => {
196
+ const rawVal = row[c.key];
197
+ const isEmpty = rawVal == null || rawVal === "";
198
+ const displayVal = isEmpty ? "\u200B" : rawVal;
199
+ const widthStyle = c.width != null ? {
200
+ width: typeof c.width === "number" ? `${c.width}px` : c.width,
201
+ minWidth: typeof c.width === "number" ? `${c.width}px` : c.width,
202
+ maxWidth: typeof c.width === "number" ? `${c.width}px` : c.width
203
+ } : void 0;
204
+ return /* @__PURE__ */ jsx4(
205
+ "td",
206
+ {
207
+ role: "cell",
208
+ "aria-colindex": colIndex + 1,
209
+ "aria-label": isEmpty ? `${c.label}: Empty` : void 0,
210
+ className: c.numeric ? "num" : void 0,
211
+ style: widthStyle,
212
+ children: displayVal
213
+ },
214
+ c.key
215
+ );
216
+ })
217
+ },
218
+ rowIndex
219
+ );
220
+ }) })
221
+ ]
222
+ }
223
+ );
224
+ }
225
+
226
+ // src/DataTable/DataTable.jsx
227
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
228
+ var headerClass = (c) => [c.numeric && "num", c.casing === "asWritten" && "t-as-written"].filter(Boolean).join(" ") || void 0;
229
+ function DataTable({ columns: columns2 = [], rows: rows2 = [], className, ...rest }) {
230
+ const cols = columns2.map((c) => typeof c === "string" ? { key: c, label: c } : c);
231
+ return /* @__PURE__ */ jsxs3("table", { ...rest, className: cx("table", className), children: [
232
+ /* @__PURE__ */ jsx5("thead", { children: /* @__PURE__ */ jsx5("tr", { children: cols.map((c) => /* @__PURE__ */ jsx5("th", { className: headerClass(c), children: c.label }, c.key)) }) }),
233
+ /* @__PURE__ */ jsx5("tbody", { children: rows2.map((row, i) => /* @__PURE__ */ jsx5("tr", { children: cols.map((c) => /* @__PURE__ */ jsx5("td", { className: c.numeric ? "num" : void 0, children: row[c.key] }, c.key)) }, i)) })
234
+ ] });
235
+ }
236
+
237
+ // src/TextField/TextField.jsx
238
+ import * as React from "react";
239
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
240
+ function TextField({
241
+ label,
242
+ hint,
243
+ error,
244
+ as = "input",
245
+ id,
246
+ invalid = false,
247
+ disabled = false,
248
+ labelCasing = "uppercase",
249
+ className,
250
+ children,
251
+ "aria-describedby": describedByProp,
252
+ ...rest
253
+ }) {
254
+ const generated = React.useId();
255
+ const fid = id || generated;
256
+ const hintId = hint ? `${fid}-hint` : void 0;
257
+ const errorId = error ? `${fid}-error` : void 0;
258
+ const describedBy = [describedByProp, hintId, errorId].filter(Boolean).join(" ") || void 0;
259
+ const common = {
260
+ ...rest,
261
+ id: fid,
262
+ className: cx("field__input", className),
263
+ "aria-invalid": invalid || error ? true : void 0,
264
+ "aria-describedby": describedBy,
265
+ disabled
266
+ };
267
+ let control;
268
+ if (as === "textarea") control = /* @__PURE__ */ jsx6("textarea", { ...common });
269
+ else if (as === "select") control = /* @__PURE__ */ jsx6("select", { ...common, children });
270
+ else control = /* @__PURE__ */ jsx6("input", { ...common });
271
+ return /* @__PURE__ */ jsxs4("div", { className: "field", children: [
272
+ label ? /* @__PURE__ */ jsx6("label", { className: cx("field__label", labelCasing === "asWritten" && "t-as-written"), htmlFor: fid, children: label }) : null,
273
+ control,
274
+ hint ? /* @__PURE__ */ jsx6("span", { className: "field__hint", id: hintId, children: hint }) : null,
275
+ error ? /* @__PURE__ */ jsx6("span", { className: "field__hint is-error", id: errorId, children: error }) : null
276
+ ] });
277
+ }
278
+
279
+ // src/sdui/serializer.js
280
+ var VALID_NODE_TYPES = /* @__PURE__ */ new Set([
281
+ "button",
282
+ "badge",
283
+ "tag",
284
+ "card",
285
+ "data-matrix",
286
+ "data-table",
287
+ "text-field",
288
+ "container",
289
+ "text"
290
+ ]);
291
+ var BUTTON_VARIANTS = ["primary", "outline", "secondary", "ghost", "accent"];
292
+ var BADGE_VARIANTS = ["outline", "solid", "accent", "accent-outline"];
293
+ var SIZES = ["sm", "md", "lg"];
294
+ var CASINGS = ["uppercase", "asWritten"];
295
+ var VALID_TOKEN_NAMES = /* @__PURE__ */ new Set([
296
+ // color
297
+ "substrate",
298
+ "surfaceElevated",
299
+ "surfaceRecessed",
300
+ "ink100",
301
+ "ink60",
302
+ "ink30",
303
+ "signalError",
304
+ // radius
305
+ "subtle",
306
+ "control",
307
+ "card",
308
+ "badge",
309
+ // space
310
+ "half",
311
+ "1",
312
+ "2",
313
+ "3",
314
+ "4",
315
+ "6",
316
+ "8",
317
+ "12",
318
+ "16",
319
+ "touch",
320
+ // measure
321
+ "body",
322
+ "compact",
323
+ "sidebar"
324
+ ]);
325
+ var string = { kind: "string" };
326
+ var boolean = { kind: "boolean" };
327
+ var oneOf = (values) => ({ kind: "enum", values });
328
+ var columns = { kind: "columns" };
329
+ var rows = { kind: "rows" };
330
+ var colorValue = { kind: "color" };
331
+ var SDUI_PROP_ALLOWLIST = {
332
+ button: { variant: oneOf(BUTTON_VARIANTS), size: oneOf(SIZES), block: boolean, disabled: boolean, type: oneOf(["button", "submit", "reset"]), token: string },
333
+ badge: { variant: oneOf(BADGE_VARIANTS), accentColor: colorValue, token: string },
334
+ tag: { token: string },
335
+ card: { eyebrow: string, eyebrowCasing: oneOf(CASINGS), title: string, flat: boolean, compact: boolean, token: string },
336
+ "data-matrix": { columns, rows, label: string, token: string },
337
+ "data-table": { columns, rows, token: string },
338
+ "text-field": {
339
+ label: string,
340
+ hint: string,
341
+ error: string,
342
+ as: oneOf(["input", "textarea", "select"]),
343
+ invalid: boolean,
344
+ disabled: boolean,
345
+ labelCasing: oneOf(CASINGS),
346
+ placeholder: string,
347
+ name: string,
348
+ type: oneOf(["text", "email", "number", "password", "search", "tel", "url"]),
349
+ defaultValue: string,
350
+ token: string
351
+ },
352
+ container: { token: string },
353
+ text: { token: string }
354
+ };
355
+ var COLOR_PATTERN = /^(#[0-9a-f]{3,8}|rgba?\([\d\s.,%/]+\)|oklch\([\d\s.%/deg]+\))$/i;
356
+ var COLUMN_KEYS = /* @__PURE__ */ new Set(["key", "label", "numeric", "width", "casing"]);
357
+ var fail = (message) => {
358
+ throw new Error(message);
359
+ };
360
+ function validateColumns(value, where) {
361
+ if (!Array.isArray(value)) fail(`${where}: "columns" must be an array.`);
362
+ for (const col of value) {
363
+ if (typeof col === "string") continue;
364
+ if (!col || typeof col !== "object" || Array.isArray(col)) fail(`${where}: a column must be a string or an object.`);
365
+ for (const [k, v] of Object.entries(col)) {
366
+ if (!COLUMN_KEYS.has(k)) fail(`${where}: unsupported column key "${k}".`);
367
+ if ((k === "key" || k === "label") && typeof v !== "string") fail(`${where}: column "${k}" must be a string.`);
368
+ if (k === "numeric" && typeof v !== "boolean") fail(`${where}: column "numeric" must be a boolean.`);
369
+ if (k === "width" && typeof v !== "number" && typeof v !== "string") fail(`${where}: column "width" must be a number or string.`);
370
+ if (k === "casing" && !CASINGS.includes(v)) fail(`${where}: column "casing" must be one of ${CASINGS.join(", ")}.`);
371
+ }
372
+ if (typeof col.key !== "string" || typeof col.label !== "string") fail(`${where}: a column needs "key" and "label" strings.`);
373
+ }
374
+ }
375
+ function validateRows(value, where) {
376
+ if (!Array.isArray(value)) fail(`${where}: "rows" must be an array.`);
377
+ for (const row of value) {
378
+ if (!row || typeof row !== "object" || Array.isArray(row)) fail(`${where}: a row must be an object.`);
379
+ for (const cell of Object.values(row)) {
380
+ if (cell !== null && !["string", "number", "boolean"].includes(typeof cell)) fail(`${where}: row cells must be strings, numbers, booleans or null.`);
381
+ }
382
+ }
383
+ }
384
+ function validateProp(type, name, spec, value) {
385
+ const where = `SDUI ${type} prop "${name}"`;
386
+ switch (spec.kind) {
387
+ case "string":
388
+ if (typeof value !== "string") fail(`${where} must be a string.`);
389
+ break;
390
+ case "boolean":
391
+ if (typeof value !== "boolean") fail(`${where} must be a boolean.`);
392
+ break;
393
+ case "enum":
394
+ if (!spec.values.includes(value)) fail(`Unsupported ${where}: "${value}". Allowed: ${spec.values.join(", ")}`);
395
+ break;
396
+ case "color":
397
+ if (typeof value !== "string" || !COLOR_PATTERN.test(value)) fail(`${where} must be a #hex, rgb() or oklch() colour.`);
398
+ break;
399
+ case "columns":
400
+ validateColumns(value, where);
401
+ break;
402
+ case "rows":
403
+ validateRows(value, where);
404
+ break;
405
+ default:
406
+ fail(`${where} has an unknown spec.`);
407
+ }
408
+ if (name === "token" && !VALID_TOKEN_NAMES.has(value)) fail(`Unsupported SDUI token reference: "${value}".`);
409
+ }
410
+ function serializeTokens(tokens) {
411
+ if (!tokens || typeof tokens !== "object") {
412
+ throw new Error("Invalid tokens object provided for SDUI serialization.");
413
+ }
414
+ const sduiTokens = {
415
+ $schema: "https://judarn.design/schemas/sdui/tokens/v1.json",
416
+ space: {},
417
+ measure: {},
418
+ border: {},
419
+ radius: {},
420
+ color: {}
421
+ };
422
+ if (tokens.space) {
423
+ for (const [key, val] of Object.entries(tokens.space)) {
424
+ sduiTokens.space[key] = val.$value;
425
+ }
426
+ }
427
+ if (tokens.measure) {
428
+ for (const [key, val] of Object.entries(tokens.measure)) {
429
+ sduiTokens.measure[key] = val.$value;
430
+ }
431
+ }
432
+ if (tokens.border) {
433
+ for (const [key, val] of Object.entries(tokens.border)) {
434
+ sduiTokens.border[key] = val.$value;
435
+ }
436
+ }
437
+ if (tokens.radius) {
438
+ for (const [key, val] of Object.entries(tokens.radius)) {
439
+ sduiTokens.radius[key] = val.$value;
440
+ }
441
+ }
442
+ if (tokens.color) {
443
+ for (const [key, val] of Object.entries(tokens.color)) {
444
+ sduiTokens.color[key] = {
445
+ light: val.light?.$value,
446
+ dark: val.dark?.$value,
447
+ lightHighContrast: val.lightHighContrast?.$value,
448
+ darkHighContrast: val.darkHighContrast?.$value
449
+ };
450
+ }
451
+ }
452
+ return sduiTokens;
453
+ }
454
+ function validateSDUINode(node) {
455
+ if (!node || typeof node !== "object" || Array.isArray(node)) {
456
+ fail("SDUI Node must be a non-null object.");
457
+ }
458
+ if (!node.type || typeof node.type !== "string") {
459
+ fail('SDUI Node missing required "type" string property.');
460
+ }
461
+ if (!VALID_NODE_TYPES.has(node.type)) {
462
+ fail(`Unsupported SDUI node type: "${node.type}". Allowed: ${Array.from(VALID_NODE_TYPES).join(", ")}`);
463
+ }
464
+ if (node.props !== void 0) {
465
+ if (!node.props || typeof node.props !== "object" || Array.isArray(node.props)) fail("SDUI Node props must be an object.");
466
+ const allowed = SDUI_PROP_ALLOWLIST[node.type];
467
+ for (const [name, value] of Object.entries(node.props)) {
468
+ if (!Object.prototype.hasOwnProperty.call(allowed, name)) {
469
+ fail(`Unsupported SDUI prop "${name}" on "${node.type}". Allowed: ${Object.keys(allowed).join(", ") || "none"}`);
470
+ }
471
+ validateProp(node.type, name, allowed[name], value);
472
+ }
473
+ }
474
+ if (node.content !== void 0 && typeof node.content !== "string") {
475
+ fail("SDUI Node content must be a string.");
476
+ }
477
+ if (node.children !== void 0) {
478
+ if (!Array.isArray(node.children)) fail("SDUI Node children must be an array.");
479
+ for (const child of node.children) validateSDUINode(child);
480
+ }
481
+ return true;
482
+ }
483
+ function serializeComponent(type, props = {}, children = [], content = void 0) {
484
+ const node = { type, props: { ...props } };
485
+ if (content !== void 0) node.content = String(content);
486
+ if (Array.isArray(children) && children.length > 0) node.children = children;
487
+ validateSDUINode(node);
488
+ return node;
489
+ }
490
+ function deserializeComponent(node, customComponents = {}, reactInstance = React2) {
491
+ validateSDUINode(node);
492
+ const components = {
493
+ Button,
494
+ Badge,
495
+ Tag,
496
+ Card,
497
+ DataMatrix,
498
+ DataTable,
499
+ TextField,
500
+ ...customComponents
501
+ };
502
+ const h = (type, props2, children) => reactInstance.createElement(type, props2, ...children);
503
+ const childElements = node.children ? node.children.map((child, i) => {
504
+ const el = deserializeComponent(child, customComponents, reactInstance);
505
+ return reactInstance.isValidElement(el) ? reactInstance.cloneElement(el, { key: i }) : el;
506
+ }) : [];
507
+ const contentOrChildren = node.content !== void 0 ? [node.content] : childElements;
508
+ const { token: _token, ...props } = node.props || {};
509
+ switch (node.type) {
510
+ case "button":
511
+ return h(components.Button, props, contentOrChildren);
512
+ case "badge":
513
+ return h(components.Badge, props, contentOrChildren);
514
+ case "tag":
515
+ return h(components.Tag, props, contentOrChildren);
516
+ case "card":
517
+ return h(components.Card, props, contentOrChildren);
518
+ case "data-matrix":
519
+ return h(components.DataMatrix, props, []);
520
+ case "data-table":
521
+ return h(components.DataTable, props, []);
522
+ case "text-field":
523
+ return h(components.TextField, props, []);
524
+ case "container":
525
+ return h("div", props, contentOrChildren);
526
+ case "text":
527
+ return h("span", props, [node.content || ""]);
528
+ default:
529
+ return fail(`Unhandled SDUI node type: ${node.type}`);
530
+ }
531
+ }
532
+ export {
533
+ SDUI_PROP_ALLOWLIST,
534
+ VALID_NODE_TYPES,
535
+ VALID_TOKEN_NAMES,
536
+ deserializeComponent,
537
+ serializeComponent,
538
+ serializeTokens,
539
+ validateSDUINode
540
+ };