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