@symbo.ls/brender 3.4.11 → 3.5.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/cjs/hydrate.js +97 -4
- package/dist/cjs/render.js +120 -5
- package/dist/esm/hydrate.js +97 -4
- package/dist/esm/render.js +120 -5
- package/hydrate.js +89 -5
- package/package.json +1 -1
- package/render.js +140 -5
package/dist/cjs/hydrate.js
CHANGED
|
@@ -115,6 +115,15 @@ const renderCSS = (el, emotion, colorMap, mediaMap) => {
|
|
|
115
115
|
css[key] = resolveValue(key, val, colorMap);
|
|
116
116
|
hasCss = true;
|
|
117
117
|
}
|
|
118
|
+
const extsCss = getExtendsCSS(el);
|
|
119
|
+
if (extsCss) {
|
|
120
|
+
for (const [k, v] of Object.entries(extsCss)) {
|
|
121
|
+
if (!css[k]) {
|
|
122
|
+
css[k] = v;
|
|
123
|
+
hasCss = true;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
118
127
|
if (el.style && typeof el.style === "object") {
|
|
119
128
|
Object.assign(css, el.style);
|
|
120
129
|
hasCss = true;
|
|
@@ -213,8 +222,34 @@ const NON_CSS_PROPS = /* @__PURE__ */ new Set([
|
|
|
213
222
|
"__element",
|
|
214
223
|
"update"
|
|
215
224
|
]);
|
|
225
|
+
const EXTENDS_CSS = {
|
|
226
|
+
Flex: { display: "flex" },
|
|
227
|
+
InlineFlex: { display: "inline-flex" },
|
|
228
|
+
Grid: { display: "grid" },
|
|
229
|
+
InlineGrid: { display: "inline-grid" },
|
|
230
|
+
Block: { display: "block" },
|
|
231
|
+
Inline: { display: "inline" }
|
|
232
|
+
};
|
|
233
|
+
const getExtendsCSS = (el) => {
|
|
234
|
+
const exts = el.__ref?.__extends;
|
|
235
|
+
if (!exts || !Array.isArray(exts)) return null;
|
|
236
|
+
for (const ext of exts) {
|
|
237
|
+
if (EXTENDS_CSS[ext]) return EXTENDS_CSS[ext];
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
};
|
|
216
241
|
const resolveShorthand = (key, val) => {
|
|
217
|
-
if (
|
|
242
|
+
if (typeof val === "undefined" || val === null) return null;
|
|
243
|
+
if (key === "flow" && typeof val === "string") {
|
|
244
|
+
let [direction, wrap] = (val || "row").split(" ");
|
|
245
|
+
if (val.startsWith("x") || val === "row") direction = "row";
|
|
246
|
+
if (val.startsWith("y") || val === "column") direction = "column";
|
|
247
|
+
return { display: "flex", flexFlow: (direction || "") + " " + (wrap || "") };
|
|
248
|
+
}
|
|
249
|
+
if (key === "wrap") {
|
|
250
|
+
return { display: "flex", flexWrap: val };
|
|
251
|
+
}
|
|
252
|
+
if ((key === "align" || key === "flexAlign") && typeof val === "string") {
|
|
218
253
|
const [alignItems, justifyContent] = val.split(" ");
|
|
219
254
|
return { display: "flex", alignItems, justifyContent };
|
|
220
255
|
}
|
|
@@ -222,12 +257,44 @@ const resolveShorthand = (key, val) => {
|
|
|
222
257
|
const [alignItems, justifyContent] = val.split(" ");
|
|
223
258
|
return { display: "grid", alignItems, justifyContent };
|
|
224
259
|
}
|
|
225
|
-
if (key === "
|
|
260
|
+
if (key === "flexFlow" && typeof val === "string") {
|
|
261
|
+
let [direction, wrap] = (val || "row").split(" ");
|
|
262
|
+
if (val.startsWith("x") || val === "row") direction = "row";
|
|
263
|
+
if (val.startsWith("y") || val === "column") direction = "column";
|
|
264
|
+
return { display: "flex", flexFlow: (direction || "") + " " + (wrap || "") };
|
|
265
|
+
}
|
|
266
|
+
if (key === "flexWrap") {
|
|
267
|
+
return { display: "flex", flexWrap: val };
|
|
268
|
+
}
|
|
269
|
+
if (key === "round" || key === "borderRadius" && val) {
|
|
226
270
|
return { borderRadius: typeof val === "number" ? val + "px" : val };
|
|
227
271
|
}
|
|
228
|
-
if (key === "boxSize" && val) {
|
|
229
|
-
|
|
272
|
+
if (key === "boxSize" && typeof val === "string") {
|
|
273
|
+
const [height, width] = val.split(" ");
|
|
274
|
+
return { height, width: width || height };
|
|
275
|
+
}
|
|
276
|
+
if (key === "widthRange" && typeof val === "string") {
|
|
277
|
+
const [minWidth, maxWidth] = val.split(" ");
|
|
278
|
+
return { minWidth, maxWidth: maxWidth || minWidth };
|
|
279
|
+
}
|
|
280
|
+
if (key === "heightRange" && typeof val === "string") {
|
|
281
|
+
const [minHeight, maxHeight] = val.split(" ");
|
|
282
|
+
return { minHeight, maxHeight: maxHeight || minHeight };
|
|
230
283
|
}
|
|
284
|
+
if (key === "column") return { gridColumn: val };
|
|
285
|
+
if (key === "columns") return { gridTemplateColumns: val };
|
|
286
|
+
if (key === "templateColumns") return { gridTemplateColumns: val };
|
|
287
|
+
if (key === "row") return { gridRow: val };
|
|
288
|
+
if (key === "rows") return { gridTemplateRows: val };
|
|
289
|
+
if (key === "templateRows") return { gridTemplateRows: val };
|
|
290
|
+
if (key === "area") return { gridArea: val };
|
|
291
|
+
if (key === "template") return { gridTemplate: val };
|
|
292
|
+
if (key === "templateAreas") return { gridTemplateAreas: val };
|
|
293
|
+
if (key === "autoColumns") return { gridAutoColumns: val };
|
|
294
|
+
if (key === "autoRows") return { gridAutoRows: val };
|
|
295
|
+
if (key === "autoFlow") return { gridAutoFlow: val };
|
|
296
|
+
if (key === "columnStart") return { gridColumnStart: val };
|
|
297
|
+
if (key === "rowStart") return { gridRowStart: val };
|
|
231
298
|
return null;
|
|
232
299
|
};
|
|
233
300
|
const isCSS = (key) => {
|
|
@@ -341,11 +408,37 @@ const CSS_PROPERTIES = /* @__PURE__ */ new Set([
|
|
|
341
408
|
"gridAutoFlow",
|
|
342
409
|
"gridAutoColumns",
|
|
343
410
|
"gridAutoRows",
|
|
411
|
+
"inset",
|
|
412
|
+
"inlineSize",
|
|
413
|
+
"blockSize",
|
|
414
|
+
"minInlineSize",
|
|
415
|
+
"maxInlineSize",
|
|
416
|
+
"minBlockSize",
|
|
417
|
+
"maxBlockSize",
|
|
418
|
+
"paddingBlockStart",
|
|
419
|
+
"paddingBlockEnd",
|
|
420
|
+
"paddingInlineStart",
|
|
421
|
+
"paddingInlineEnd",
|
|
422
|
+
"marginBlockStart",
|
|
423
|
+
"marginBlockEnd",
|
|
424
|
+
"marginInlineStart",
|
|
425
|
+
"marginInlineEnd",
|
|
344
426
|
"transform",
|
|
345
427
|
"transformOrigin",
|
|
346
428
|
"transition",
|
|
347
429
|
"animation",
|
|
430
|
+
"animationName",
|
|
431
|
+
"animationDuration",
|
|
348
432
|
"animationDelay",
|
|
433
|
+
"animationTimingFunction",
|
|
434
|
+
"animationFillMode",
|
|
435
|
+
"animationIterationCount",
|
|
436
|
+
"animationPlayState",
|
|
437
|
+
"animationDirection",
|
|
438
|
+
"gridTemplate",
|
|
439
|
+
"gridTemplateAreas",
|
|
440
|
+
"gridColumnStart",
|
|
441
|
+
"gridRowStart",
|
|
349
442
|
"boxShadow",
|
|
350
443
|
"outline",
|
|
351
444
|
"outlineColor",
|
package/dist/cjs/render.js
CHANGED
|
@@ -39,6 +39,55 @@ var import_metadata = require("./metadata.js");
|
|
|
39
39
|
var import_hydrate = require("./hydrate.js");
|
|
40
40
|
var import_linkedom = require("linkedom");
|
|
41
41
|
const import_meta = {};
|
|
42
|
+
const UIKIT_STUBS = {
|
|
43
|
+
Box: {},
|
|
44
|
+
Focusable: {},
|
|
45
|
+
Block: { display: "block" },
|
|
46
|
+
Inline: { display: "inline" },
|
|
47
|
+
Flex: { display: "flex" },
|
|
48
|
+
InlineFlex: { display: "inline-flex" },
|
|
49
|
+
Grid: { display: "grid" },
|
|
50
|
+
InlineGrid: { display: "inline-grid" },
|
|
51
|
+
Link: {
|
|
52
|
+
tag: "a",
|
|
53
|
+
attr: {
|
|
54
|
+
href: (el) => el.props?.href,
|
|
55
|
+
target: (el) => el.props?.target,
|
|
56
|
+
rel: (el) => el.props?.rel
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
A: { extends: "Link" },
|
|
60
|
+
RouteLink: { extends: "Link" },
|
|
61
|
+
Img: {
|
|
62
|
+
tag: "img",
|
|
63
|
+
attr: {
|
|
64
|
+
src: (el) => el.props?.src,
|
|
65
|
+
alt: (el) => el.props?.alt,
|
|
66
|
+
loading: (el) => el.props?.loading
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
Image: { extends: "Img" },
|
|
70
|
+
Button: { tag: "button" },
|
|
71
|
+
FocusableComponent: { tag: "button" },
|
|
72
|
+
Form: { tag: "form" },
|
|
73
|
+
Input: { tag: "input" },
|
|
74
|
+
TextArea: { tag: "textarea" },
|
|
75
|
+
Textarea: { tag: "textarea" },
|
|
76
|
+
Select: { tag: "select" },
|
|
77
|
+
Label: { tag: "label" },
|
|
78
|
+
Iframe: { tag: "iframe" },
|
|
79
|
+
Video: { tag: "video" },
|
|
80
|
+
Audio: { tag: "audio" },
|
|
81
|
+
Canvas: { tag: "canvas" },
|
|
82
|
+
Span: { tag: "span" },
|
|
83
|
+
P: { tag: "p" },
|
|
84
|
+
H1: { tag: "h1" },
|
|
85
|
+
H2: { tag: "h2" },
|
|
86
|
+
H3: { tag: "h3" },
|
|
87
|
+
H4: { tag: "h4" },
|
|
88
|
+
H5: { tag: "h5" },
|
|
89
|
+
H6: { tag: "h6" }
|
|
90
|
+
};
|
|
42
91
|
const render = async (data, options = {}) => {
|
|
43
92
|
const { route = "/", state: stateOverrides, context: contextOverrides } = options;
|
|
44
93
|
const { window, document } = (0, import_env.createEnv)();
|
|
@@ -78,9 +127,10 @@ const renderElement = async (elementDef, options = {}) => {
|
|
|
78
127
|
const { window, document } = (0, import_env.createEnv)();
|
|
79
128
|
const body = document.body;
|
|
80
129
|
const { create } = await import("@domql/element");
|
|
130
|
+
const components = { ...UIKIT_STUBS, ...context.components || {} };
|
|
81
131
|
(0, import_keys.resetKeys)();
|
|
82
132
|
const element = create(elementDef, { node: body }, "root", {
|
|
83
|
-
context: { document, window, ...context }
|
|
133
|
+
context: { document, window, ...context, components }
|
|
84
134
|
});
|
|
85
135
|
(0, import_keys.assignKeys)(body);
|
|
86
136
|
const registry = (0, import_keys.mapKeysToElements)(element);
|
|
@@ -191,7 +241,17 @@ const NON_CSS_PROPS = /* @__PURE__ */ new Set([
|
|
|
191
241
|
]);
|
|
192
242
|
const camelToKebab = (str) => str.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
|
|
193
243
|
const resolveShorthand = (key, val) => {
|
|
194
|
-
if (
|
|
244
|
+
if (typeof val === "undefined" || val === null) return null;
|
|
245
|
+
if (key === "flow" && typeof val === "string") {
|
|
246
|
+
let [direction, wrap] = (val || "row").split(" ");
|
|
247
|
+
if (val.startsWith("x") || val === "row") direction = "row";
|
|
248
|
+
if (val.startsWith("y") || val === "column") direction = "column";
|
|
249
|
+
return { display: "flex", "flex-flow": (direction || "") + " " + (wrap || "") };
|
|
250
|
+
}
|
|
251
|
+
if (key === "wrap") {
|
|
252
|
+
return { display: "flex", "flex-wrap": val };
|
|
253
|
+
}
|
|
254
|
+
if ((key === "align" || key === "flexAlign") && typeof val === "string") {
|
|
195
255
|
const [alignItems, justifyContent] = val.split(" ");
|
|
196
256
|
return { display: "flex", "align-items": alignItems, "justify-content": justifyContent };
|
|
197
257
|
}
|
|
@@ -199,12 +259,44 @@ const resolveShorthand = (key, val) => {
|
|
|
199
259
|
const [alignItems, justifyContent] = val.split(" ");
|
|
200
260
|
return { display: "grid", "align-items": alignItems, "justify-content": justifyContent };
|
|
201
261
|
}
|
|
202
|
-
if (key === "
|
|
262
|
+
if (key === "flexFlow" && typeof val === "string") {
|
|
263
|
+
let [direction, wrap] = (val || "row").split(" ");
|
|
264
|
+
if (val.startsWith("x") || val === "row") direction = "row";
|
|
265
|
+
if (val.startsWith("y") || val === "column") direction = "column";
|
|
266
|
+
return { display: "flex", "flex-flow": (direction || "") + " " + (wrap || "") };
|
|
267
|
+
}
|
|
268
|
+
if (key === "flexWrap") {
|
|
269
|
+
return { display: "flex", "flex-wrap": val };
|
|
270
|
+
}
|
|
271
|
+
if (key === "round" || key === "borderRadius" && val) {
|
|
203
272
|
return { "border-radius": typeof val === "number" ? val + "px" : val };
|
|
204
273
|
}
|
|
205
|
-
if (key === "boxSize" && val) {
|
|
206
|
-
|
|
274
|
+
if (key === "boxSize" && typeof val === "string") {
|
|
275
|
+
const [height, width] = val.split(" ");
|
|
276
|
+
return { height, width: width || height };
|
|
207
277
|
}
|
|
278
|
+
if (key === "widthRange" && typeof val === "string") {
|
|
279
|
+
const [minWidth, maxWidth] = val.split(" ");
|
|
280
|
+
return { "min-width": minWidth, "max-width": maxWidth || minWidth };
|
|
281
|
+
}
|
|
282
|
+
if (key === "heightRange" && typeof val === "string") {
|
|
283
|
+
const [minHeight, maxHeight] = val.split(" ");
|
|
284
|
+
return { "min-height": minHeight, "max-height": maxHeight || minHeight };
|
|
285
|
+
}
|
|
286
|
+
if (key === "column") return { "grid-column": val };
|
|
287
|
+
if (key === "columns") return { "grid-template-columns": val };
|
|
288
|
+
if (key === "templateColumns") return { "grid-template-columns": val };
|
|
289
|
+
if (key === "row") return { "grid-row": val };
|
|
290
|
+
if (key === "rows") return { "grid-template-rows": val };
|
|
291
|
+
if (key === "templateRows") return { "grid-template-rows": val };
|
|
292
|
+
if (key === "area") return { "grid-area": val };
|
|
293
|
+
if (key === "template") return { "grid-template": val };
|
|
294
|
+
if (key === "templateAreas") return { "grid-template-areas": val };
|
|
295
|
+
if (key === "autoColumns") return { "grid-auto-columns": val };
|
|
296
|
+
if (key === "autoRows") return { "grid-auto-rows": val };
|
|
297
|
+
if (key === "autoFlow") return { "grid-auto-flow": val };
|
|
298
|
+
if (key === "columnStart") return { "grid-column-start": val };
|
|
299
|
+
if (key === "rowStart") return { "grid-row-start": val };
|
|
208
300
|
return null;
|
|
209
301
|
};
|
|
210
302
|
const resolveInnerProps = (obj, colorMap) => {
|
|
@@ -267,6 +359,22 @@ const renderCSSRule = (selector, { base, mediaRules, pseudoRules }) => {
|
|
|
267
359
|
}
|
|
268
360
|
return lines.join("\n");
|
|
269
361
|
};
|
|
362
|
+
const EXTENDS_CSS = {
|
|
363
|
+
Flex: { display: "flex" },
|
|
364
|
+
InlineFlex: { display: "inline-flex" },
|
|
365
|
+
Grid: { display: "grid" },
|
|
366
|
+
InlineGrid: { display: "inline-grid" },
|
|
367
|
+
Block: { display: "block" },
|
|
368
|
+
Inline: { display: "inline" }
|
|
369
|
+
};
|
|
370
|
+
const getExtendsCSS = (el) => {
|
|
371
|
+
const exts = el.__ref?.__extends;
|
|
372
|
+
if (!exts || !Array.isArray(exts)) return null;
|
|
373
|
+
for (const ext of exts) {
|
|
374
|
+
if (EXTENDS_CSS[ext]) return EXTENDS_CSS[ext];
|
|
375
|
+
}
|
|
376
|
+
return null;
|
|
377
|
+
};
|
|
270
378
|
const extractCSS = (element, ds) => {
|
|
271
379
|
const colorMap = ds?.color || {};
|
|
272
380
|
const mediaMap = ds?.media || {};
|
|
@@ -282,6 +390,13 @@ const extractCSS = (element, ds) => {
|
|
|
282
390
|
if (cls && !seen.has(cls)) {
|
|
283
391
|
seen.add(cls);
|
|
284
392
|
const cssResult = buildCSSFromProps(props, colorMap, mediaMap);
|
|
393
|
+
const extsCss = getExtendsCSS(el);
|
|
394
|
+
if (extsCss) {
|
|
395
|
+
for (const [k, v] of Object.entries(extsCss)) {
|
|
396
|
+
const kebab = camelToKebab(k);
|
|
397
|
+
if (!cssResult.base[kebab]) cssResult.base[kebab] = v;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
285
400
|
const has = Object.keys(cssResult.base).length || Object.keys(cssResult.mediaRules).length || Object.keys(cssResult.pseudoRules).length;
|
|
286
401
|
if (has) rules.push(renderCSSRule("." + cls.split(" ")[0], cssResult));
|
|
287
402
|
const anim = props.animation || props.animationName;
|
package/dist/esm/hydrate.js
CHANGED
|
@@ -92,6 +92,15 @@ const renderCSS = (el, emotion, colorMap, mediaMap) => {
|
|
|
92
92
|
css[key] = resolveValue(key, val, colorMap);
|
|
93
93
|
hasCss = true;
|
|
94
94
|
}
|
|
95
|
+
const extsCss = getExtendsCSS(el);
|
|
96
|
+
if (extsCss) {
|
|
97
|
+
for (const [k, v] of Object.entries(extsCss)) {
|
|
98
|
+
if (!css[k]) {
|
|
99
|
+
css[k] = v;
|
|
100
|
+
hasCss = true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
95
104
|
if (el.style && typeof el.style === "object") {
|
|
96
105
|
Object.assign(css, el.style);
|
|
97
106
|
hasCss = true;
|
|
@@ -190,8 +199,34 @@ const NON_CSS_PROPS = /* @__PURE__ */ new Set([
|
|
|
190
199
|
"__element",
|
|
191
200
|
"update"
|
|
192
201
|
]);
|
|
202
|
+
const EXTENDS_CSS = {
|
|
203
|
+
Flex: { display: "flex" },
|
|
204
|
+
InlineFlex: { display: "inline-flex" },
|
|
205
|
+
Grid: { display: "grid" },
|
|
206
|
+
InlineGrid: { display: "inline-grid" },
|
|
207
|
+
Block: { display: "block" },
|
|
208
|
+
Inline: { display: "inline" }
|
|
209
|
+
};
|
|
210
|
+
const getExtendsCSS = (el) => {
|
|
211
|
+
const exts = el.__ref?.__extends;
|
|
212
|
+
if (!exts || !Array.isArray(exts)) return null;
|
|
213
|
+
for (const ext of exts) {
|
|
214
|
+
if (EXTENDS_CSS[ext]) return EXTENDS_CSS[ext];
|
|
215
|
+
}
|
|
216
|
+
return null;
|
|
217
|
+
};
|
|
193
218
|
const resolveShorthand = (key, val) => {
|
|
194
|
-
if (
|
|
219
|
+
if (typeof val === "undefined" || val === null) return null;
|
|
220
|
+
if (key === "flow" && typeof val === "string") {
|
|
221
|
+
let [direction, wrap] = (val || "row").split(" ");
|
|
222
|
+
if (val.startsWith("x") || val === "row") direction = "row";
|
|
223
|
+
if (val.startsWith("y") || val === "column") direction = "column";
|
|
224
|
+
return { display: "flex", flexFlow: (direction || "") + " " + (wrap || "") };
|
|
225
|
+
}
|
|
226
|
+
if (key === "wrap") {
|
|
227
|
+
return { display: "flex", flexWrap: val };
|
|
228
|
+
}
|
|
229
|
+
if ((key === "align" || key === "flexAlign") && typeof val === "string") {
|
|
195
230
|
const [alignItems, justifyContent] = val.split(" ");
|
|
196
231
|
return { display: "flex", alignItems, justifyContent };
|
|
197
232
|
}
|
|
@@ -199,12 +234,44 @@ const resolveShorthand = (key, val) => {
|
|
|
199
234
|
const [alignItems, justifyContent] = val.split(" ");
|
|
200
235
|
return { display: "grid", alignItems, justifyContent };
|
|
201
236
|
}
|
|
202
|
-
if (key === "
|
|
237
|
+
if (key === "flexFlow" && typeof val === "string") {
|
|
238
|
+
let [direction, wrap] = (val || "row").split(" ");
|
|
239
|
+
if (val.startsWith("x") || val === "row") direction = "row";
|
|
240
|
+
if (val.startsWith("y") || val === "column") direction = "column";
|
|
241
|
+
return { display: "flex", flexFlow: (direction || "") + " " + (wrap || "") };
|
|
242
|
+
}
|
|
243
|
+
if (key === "flexWrap") {
|
|
244
|
+
return { display: "flex", flexWrap: val };
|
|
245
|
+
}
|
|
246
|
+
if (key === "round" || key === "borderRadius" && val) {
|
|
203
247
|
return { borderRadius: typeof val === "number" ? val + "px" : val };
|
|
204
248
|
}
|
|
205
|
-
if (key === "boxSize" && val) {
|
|
206
|
-
|
|
249
|
+
if (key === "boxSize" && typeof val === "string") {
|
|
250
|
+
const [height, width] = val.split(" ");
|
|
251
|
+
return { height, width: width || height };
|
|
252
|
+
}
|
|
253
|
+
if (key === "widthRange" && typeof val === "string") {
|
|
254
|
+
const [minWidth, maxWidth] = val.split(" ");
|
|
255
|
+
return { minWidth, maxWidth: maxWidth || minWidth };
|
|
256
|
+
}
|
|
257
|
+
if (key === "heightRange" && typeof val === "string") {
|
|
258
|
+
const [minHeight, maxHeight] = val.split(" ");
|
|
259
|
+
return { minHeight, maxHeight: maxHeight || minHeight };
|
|
207
260
|
}
|
|
261
|
+
if (key === "column") return { gridColumn: val };
|
|
262
|
+
if (key === "columns") return { gridTemplateColumns: val };
|
|
263
|
+
if (key === "templateColumns") return { gridTemplateColumns: val };
|
|
264
|
+
if (key === "row") return { gridRow: val };
|
|
265
|
+
if (key === "rows") return { gridTemplateRows: val };
|
|
266
|
+
if (key === "templateRows") return { gridTemplateRows: val };
|
|
267
|
+
if (key === "area") return { gridArea: val };
|
|
268
|
+
if (key === "template") return { gridTemplate: val };
|
|
269
|
+
if (key === "templateAreas") return { gridTemplateAreas: val };
|
|
270
|
+
if (key === "autoColumns") return { gridAutoColumns: val };
|
|
271
|
+
if (key === "autoRows") return { gridAutoRows: val };
|
|
272
|
+
if (key === "autoFlow") return { gridAutoFlow: val };
|
|
273
|
+
if (key === "columnStart") return { gridColumnStart: val };
|
|
274
|
+
if (key === "rowStart") return { gridRowStart: val };
|
|
208
275
|
return null;
|
|
209
276
|
};
|
|
210
277
|
const isCSS = (key) => {
|
|
@@ -318,11 +385,37 @@ const CSS_PROPERTIES = /* @__PURE__ */ new Set([
|
|
|
318
385
|
"gridAutoFlow",
|
|
319
386
|
"gridAutoColumns",
|
|
320
387
|
"gridAutoRows",
|
|
388
|
+
"inset",
|
|
389
|
+
"inlineSize",
|
|
390
|
+
"blockSize",
|
|
391
|
+
"minInlineSize",
|
|
392
|
+
"maxInlineSize",
|
|
393
|
+
"minBlockSize",
|
|
394
|
+
"maxBlockSize",
|
|
395
|
+
"paddingBlockStart",
|
|
396
|
+
"paddingBlockEnd",
|
|
397
|
+
"paddingInlineStart",
|
|
398
|
+
"paddingInlineEnd",
|
|
399
|
+
"marginBlockStart",
|
|
400
|
+
"marginBlockEnd",
|
|
401
|
+
"marginInlineStart",
|
|
402
|
+
"marginInlineEnd",
|
|
321
403
|
"transform",
|
|
322
404
|
"transformOrigin",
|
|
323
405
|
"transition",
|
|
324
406
|
"animation",
|
|
407
|
+
"animationName",
|
|
408
|
+
"animationDuration",
|
|
325
409
|
"animationDelay",
|
|
410
|
+
"animationTimingFunction",
|
|
411
|
+
"animationFillMode",
|
|
412
|
+
"animationIterationCount",
|
|
413
|
+
"animationPlayState",
|
|
414
|
+
"animationDirection",
|
|
415
|
+
"gridTemplate",
|
|
416
|
+
"gridTemplateAreas",
|
|
417
|
+
"gridColumnStart",
|
|
418
|
+
"gridRowStart",
|
|
326
419
|
"boxShadow",
|
|
327
420
|
"outline",
|
|
328
421
|
"outlineColor",
|
package/dist/esm/render.js
CHANGED
|
@@ -3,6 +3,55 @@ import { resetKeys, assignKeys, mapKeysToElements } from "./keys.js";
|
|
|
3
3
|
import { extractMetadata, generateHeadHtml } from "./metadata.js";
|
|
4
4
|
import { hydrate } from "./hydrate.js";
|
|
5
5
|
import { parseHTML } from "linkedom";
|
|
6
|
+
const UIKIT_STUBS = {
|
|
7
|
+
Box: {},
|
|
8
|
+
Focusable: {},
|
|
9
|
+
Block: { display: "block" },
|
|
10
|
+
Inline: { display: "inline" },
|
|
11
|
+
Flex: { display: "flex" },
|
|
12
|
+
InlineFlex: { display: "inline-flex" },
|
|
13
|
+
Grid: { display: "grid" },
|
|
14
|
+
InlineGrid: { display: "inline-grid" },
|
|
15
|
+
Link: {
|
|
16
|
+
tag: "a",
|
|
17
|
+
attr: {
|
|
18
|
+
href: (el) => el.props?.href,
|
|
19
|
+
target: (el) => el.props?.target,
|
|
20
|
+
rel: (el) => el.props?.rel
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
A: { extends: "Link" },
|
|
24
|
+
RouteLink: { extends: "Link" },
|
|
25
|
+
Img: {
|
|
26
|
+
tag: "img",
|
|
27
|
+
attr: {
|
|
28
|
+
src: (el) => el.props?.src,
|
|
29
|
+
alt: (el) => el.props?.alt,
|
|
30
|
+
loading: (el) => el.props?.loading
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
Image: { extends: "Img" },
|
|
34
|
+
Button: { tag: "button" },
|
|
35
|
+
FocusableComponent: { tag: "button" },
|
|
36
|
+
Form: { tag: "form" },
|
|
37
|
+
Input: { tag: "input" },
|
|
38
|
+
TextArea: { tag: "textarea" },
|
|
39
|
+
Textarea: { tag: "textarea" },
|
|
40
|
+
Select: { tag: "select" },
|
|
41
|
+
Label: { tag: "label" },
|
|
42
|
+
Iframe: { tag: "iframe" },
|
|
43
|
+
Video: { tag: "video" },
|
|
44
|
+
Audio: { tag: "audio" },
|
|
45
|
+
Canvas: { tag: "canvas" },
|
|
46
|
+
Span: { tag: "span" },
|
|
47
|
+
P: { tag: "p" },
|
|
48
|
+
H1: { tag: "h1" },
|
|
49
|
+
H2: { tag: "h2" },
|
|
50
|
+
H3: { tag: "h3" },
|
|
51
|
+
H4: { tag: "h4" },
|
|
52
|
+
H5: { tag: "h5" },
|
|
53
|
+
H6: { tag: "h6" }
|
|
54
|
+
};
|
|
6
55
|
const render = async (data, options = {}) => {
|
|
7
56
|
const { route = "/", state: stateOverrides, context: contextOverrides } = options;
|
|
8
57
|
const { window, document } = createEnv();
|
|
@@ -42,9 +91,10 @@ const renderElement = async (elementDef, options = {}) => {
|
|
|
42
91
|
const { window, document } = createEnv();
|
|
43
92
|
const body = document.body;
|
|
44
93
|
const { create } = await import("@domql/element");
|
|
94
|
+
const components = { ...UIKIT_STUBS, ...context.components || {} };
|
|
45
95
|
resetKeys();
|
|
46
96
|
const element = create(elementDef, { node: body }, "root", {
|
|
47
|
-
context: { document, window, ...context }
|
|
97
|
+
context: { document, window, ...context, components }
|
|
48
98
|
});
|
|
49
99
|
assignKeys(body);
|
|
50
100
|
const registry = mapKeysToElements(element);
|
|
@@ -155,7 +205,17 @@ const NON_CSS_PROPS = /* @__PURE__ */ new Set([
|
|
|
155
205
|
]);
|
|
156
206
|
const camelToKebab = (str) => str.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
|
|
157
207
|
const resolveShorthand = (key, val) => {
|
|
158
|
-
if (
|
|
208
|
+
if (typeof val === "undefined" || val === null) return null;
|
|
209
|
+
if (key === "flow" && typeof val === "string") {
|
|
210
|
+
let [direction, wrap] = (val || "row").split(" ");
|
|
211
|
+
if (val.startsWith("x") || val === "row") direction = "row";
|
|
212
|
+
if (val.startsWith("y") || val === "column") direction = "column";
|
|
213
|
+
return { display: "flex", "flex-flow": (direction || "") + " " + (wrap || "") };
|
|
214
|
+
}
|
|
215
|
+
if (key === "wrap") {
|
|
216
|
+
return { display: "flex", "flex-wrap": val };
|
|
217
|
+
}
|
|
218
|
+
if ((key === "align" || key === "flexAlign") && typeof val === "string") {
|
|
159
219
|
const [alignItems, justifyContent] = val.split(" ");
|
|
160
220
|
return { display: "flex", "align-items": alignItems, "justify-content": justifyContent };
|
|
161
221
|
}
|
|
@@ -163,12 +223,44 @@ const resolveShorthand = (key, val) => {
|
|
|
163
223
|
const [alignItems, justifyContent] = val.split(" ");
|
|
164
224
|
return { display: "grid", "align-items": alignItems, "justify-content": justifyContent };
|
|
165
225
|
}
|
|
166
|
-
if (key === "
|
|
226
|
+
if (key === "flexFlow" && typeof val === "string") {
|
|
227
|
+
let [direction, wrap] = (val || "row").split(" ");
|
|
228
|
+
if (val.startsWith("x") || val === "row") direction = "row";
|
|
229
|
+
if (val.startsWith("y") || val === "column") direction = "column";
|
|
230
|
+
return { display: "flex", "flex-flow": (direction || "") + " " + (wrap || "") };
|
|
231
|
+
}
|
|
232
|
+
if (key === "flexWrap") {
|
|
233
|
+
return { display: "flex", "flex-wrap": val };
|
|
234
|
+
}
|
|
235
|
+
if (key === "round" || key === "borderRadius" && val) {
|
|
167
236
|
return { "border-radius": typeof val === "number" ? val + "px" : val };
|
|
168
237
|
}
|
|
169
|
-
if (key === "boxSize" && val) {
|
|
170
|
-
|
|
238
|
+
if (key === "boxSize" && typeof val === "string") {
|
|
239
|
+
const [height, width] = val.split(" ");
|
|
240
|
+
return { height, width: width || height };
|
|
171
241
|
}
|
|
242
|
+
if (key === "widthRange" && typeof val === "string") {
|
|
243
|
+
const [minWidth, maxWidth] = val.split(" ");
|
|
244
|
+
return { "min-width": minWidth, "max-width": maxWidth || minWidth };
|
|
245
|
+
}
|
|
246
|
+
if (key === "heightRange" && typeof val === "string") {
|
|
247
|
+
const [minHeight, maxHeight] = val.split(" ");
|
|
248
|
+
return { "min-height": minHeight, "max-height": maxHeight || minHeight };
|
|
249
|
+
}
|
|
250
|
+
if (key === "column") return { "grid-column": val };
|
|
251
|
+
if (key === "columns") return { "grid-template-columns": val };
|
|
252
|
+
if (key === "templateColumns") return { "grid-template-columns": val };
|
|
253
|
+
if (key === "row") return { "grid-row": val };
|
|
254
|
+
if (key === "rows") return { "grid-template-rows": val };
|
|
255
|
+
if (key === "templateRows") return { "grid-template-rows": val };
|
|
256
|
+
if (key === "area") return { "grid-area": val };
|
|
257
|
+
if (key === "template") return { "grid-template": val };
|
|
258
|
+
if (key === "templateAreas") return { "grid-template-areas": val };
|
|
259
|
+
if (key === "autoColumns") return { "grid-auto-columns": val };
|
|
260
|
+
if (key === "autoRows") return { "grid-auto-rows": val };
|
|
261
|
+
if (key === "autoFlow") return { "grid-auto-flow": val };
|
|
262
|
+
if (key === "columnStart") return { "grid-column-start": val };
|
|
263
|
+
if (key === "rowStart") return { "grid-row-start": val };
|
|
172
264
|
return null;
|
|
173
265
|
};
|
|
174
266
|
const resolveInnerProps = (obj, colorMap) => {
|
|
@@ -231,6 +323,22 @@ const renderCSSRule = (selector, { base, mediaRules, pseudoRules }) => {
|
|
|
231
323
|
}
|
|
232
324
|
return lines.join("\n");
|
|
233
325
|
};
|
|
326
|
+
const EXTENDS_CSS = {
|
|
327
|
+
Flex: { display: "flex" },
|
|
328
|
+
InlineFlex: { display: "inline-flex" },
|
|
329
|
+
Grid: { display: "grid" },
|
|
330
|
+
InlineGrid: { display: "inline-grid" },
|
|
331
|
+
Block: { display: "block" },
|
|
332
|
+
Inline: { display: "inline" }
|
|
333
|
+
};
|
|
334
|
+
const getExtendsCSS = (el) => {
|
|
335
|
+
const exts = el.__ref?.__extends;
|
|
336
|
+
if (!exts || !Array.isArray(exts)) return null;
|
|
337
|
+
for (const ext of exts) {
|
|
338
|
+
if (EXTENDS_CSS[ext]) return EXTENDS_CSS[ext];
|
|
339
|
+
}
|
|
340
|
+
return null;
|
|
341
|
+
};
|
|
234
342
|
const extractCSS = (element, ds) => {
|
|
235
343
|
const colorMap = ds?.color || {};
|
|
236
344
|
const mediaMap = ds?.media || {};
|
|
@@ -246,6 +354,13 @@ const extractCSS = (element, ds) => {
|
|
|
246
354
|
if (cls && !seen.has(cls)) {
|
|
247
355
|
seen.add(cls);
|
|
248
356
|
const cssResult = buildCSSFromProps(props, colorMap, mediaMap);
|
|
357
|
+
const extsCss = getExtendsCSS(el);
|
|
358
|
+
if (extsCss) {
|
|
359
|
+
for (const [k, v] of Object.entries(extsCss)) {
|
|
360
|
+
const kebab = camelToKebab(k);
|
|
361
|
+
if (!cssResult.base[kebab]) cssResult.base[kebab] = v;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
249
364
|
const has = Object.keys(cssResult.base).length || Object.keys(cssResult.mediaRules).length || Object.keys(cssResult.pseudoRules).length;
|
|
250
365
|
if (has) rules.push(renderCSSRule("." + cls.split(" ")[0], cssResult));
|
|
251
366
|
const anim = props.animation || props.animationName;
|
package/hydrate.js
CHANGED
|
@@ -152,6 +152,14 @@ const renderCSS = (el, emotion, colorMap, mediaMap) => {
|
|
|
152
152
|
hasCss = true
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
// Inject CSS from extends chain (e.g. extends: 'Flex' → display: flex)
|
|
156
|
+
const extsCss = getExtendsCSS(el)
|
|
157
|
+
if (extsCss) {
|
|
158
|
+
for (const [k, v] of Object.entries(extsCss)) {
|
|
159
|
+
if (!css[k]) { css[k] = v; hasCss = true }
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
155
163
|
// Handle element.style object
|
|
156
164
|
if (el.style && typeof el.style === 'object') {
|
|
157
165
|
Object.assign(css, el.style)
|
|
@@ -236,9 +244,40 @@ const NON_CSS_PROPS = new Set([
|
|
|
236
244
|
'theme', '__element', 'update'
|
|
237
245
|
])
|
|
238
246
|
|
|
247
|
+
// Map of component names to their implicit CSS from extends
|
|
248
|
+
const EXTENDS_CSS = {
|
|
249
|
+
Flex: { display: 'flex' },
|
|
250
|
+
InlineFlex: { display: 'inline-flex' },
|
|
251
|
+
Grid: { display: 'grid' },
|
|
252
|
+
InlineGrid: { display: 'inline-grid' },
|
|
253
|
+
Block: { display: 'block' },
|
|
254
|
+
Inline: { display: 'inline' }
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const getExtendsCSS = (el) => {
|
|
258
|
+
const exts = el.__ref?.__extends
|
|
259
|
+
if (!exts || !Array.isArray(exts)) return null
|
|
260
|
+
for (const ext of exts) {
|
|
261
|
+
if (EXTENDS_CSS[ext]) return EXTENDS_CSS[ext]
|
|
262
|
+
}
|
|
263
|
+
return null
|
|
264
|
+
}
|
|
265
|
+
|
|
239
266
|
// DomQL shorthand props that expand to multiple CSS properties
|
|
240
267
|
const resolveShorthand = (key, val) => {
|
|
241
|
-
if (
|
|
268
|
+
if (typeof val === 'undefined' || val === null) return null
|
|
269
|
+
|
|
270
|
+
// Flex shorthands
|
|
271
|
+
if (key === 'flow' && typeof val === 'string') {
|
|
272
|
+
let [direction, wrap] = (val || 'row').split(' ')
|
|
273
|
+
if (val.startsWith('x') || val === 'row') direction = 'row'
|
|
274
|
+
if (val.startsWith('y') || val === 'column') direction = 'column'
|
|
275
|
+
return { display: 'flex', flexFlow: (direction || '') + ' ' + (wrap || '') }
|
|
276
|
+
}
|
|
277
|
+
if (key === 'wrap') {
|
|
278
|
+
return { display: 'flex', flexWrap: val }
|
|
279
|
+
}
|
|
280
|
+
if ((key === 'align' || key === 'flexAlign') && typeof val === 'string') {
|
|
242
281
|
const [alignItems, justifyContent] = val.split(' ')
|
|
243
282
|
return { display: 'flex', alignItems, justifyContent }
|
|
244
283
|
}
|
|
@@ -246,12 +285,49 @@ const resolveShorthand = (key, val) => {
|
|
|
246
285
|
const [alignItems, justifyContent] = val.split(' ')
|
|
247
286
|
return { display: 'grid', alignItems, justifyContent }
|
|
248
287
|
}
|
|
249
|
-
if (key === '
|
|
288
|
+
if (key === 'flexFlow' && typeof val === 'string') {
|
|
289
|
+
let [direction, wrap] = (val || 'row').split(' ')
|
|
290
|
+
if (val.startsWith('x') || val === 'row') direction = 'row'
|
|
291
|
+
if (val.startsWith('y') || val === 'column') direction = 'column'
|
|
292
|
+
return { display: 'flex', flexFlow: (direction || '') + ' ' + (wrap || '') }
|
|
293
|
+
}
|
|
294
|
+
if (key === 'flexWrap') {
|
|
295
|
+
return { display: 'flex', flexWrap: val }
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Box/size shorthands
|
|
299
|
+
if (key === 'round' || (key === 'borderRadius' && val)) {
|
|
250
300
|
return { borderRadius: typeof val === 'number' ? val + 'px' : val }
|
|
251
301
|
}
|
|
252
|
-
if (key === 'boxSize' && val) {
|
|
253
|
-
|
|
302
|
+
if (key === 'boxSize' && typeof val === 'string') {
|
|
303
|
+
const [height, width] = val.split(' ')
|
|
304
|
+
return { height, width: width || height }
|
|
254
305
|
}
|
|
306
|
+
if (key === 'widthRange' && typeof val === 'string') {
|
|
307
|
+
const [minWidth, maxWidth] = val.split(' ')
|
|
308
|
+
return { minWidth, maxWidth: maxWidth || minWidth }
|
|
309
|
+
}
|
|
310
|
+
if (key === 'heightRange' && typeof val === 'string') {
|
|
311
|
+
const [minHeight, maxHeight] = val.split(' ')
|
|
312
|
+
return { minHeight, maxHeight: maxHeight || minHeight }
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Grid aliases
|
|
316
|
+
if (key === 'column') return { gridColumn: val }
|
|
317
|
+
if (key === 'columns') return { gridTemplateColumns: val }
|
|
318
|
+
if (key === 'templateColumns') return { gridTemplateColumns: val }
|
|
319
|
+
if (key === 'row') return { gridRow: val }
|
|
320
|
+
if (key === 'rows') return { gridTemplateRows: val }
|
|
321
|
+
if (key === 'templateRows') return { gridTemplateRows: val }
|
|
322
|
+
if (key === 'area') return { gridArea: val }
|
|
323
|
+
if (key === 'template') return { gridTemplate: val }
|
|
324
|
+
if (key === 'templateAreas') return { gridTemplateAreas: val }
|
|
325
|
+
if (key === 'autoColumns') return { gridAutoColumns: val }
|
|
326
|
+
if (key === 'autoRows') return { gridAutoRows: val }
|
|
327
|
+
if (key === 'autoFlow') return { gridAutoFlow: val }
|
|
328
|
+
if (key === 'columnStart') return { gridColumnStart: val }
|
|
329
|
+
if (key === 'rowStart') return { gridRowStart: val }
|
|
330
|
+
|
|
255
331
|
return null
|
|
256
332
|
}
|
|
257
333
|
|
|
@@ -289,7 +365,15 @@ const CSS_PROPERTIES = new Set([
|
|
|
289
365
|
'gap', 'rowGap', 'columnGap',
|
|
290
366
|
'gridTemplateColumns', 'gridTemplateRows', 'gridColumn', 'gridRow',
|
|
291
367
|
'gridArea', 'gridAutoFlow', 'gridAutoColumns', 'gridAutoRows',
|
|
292
|
-
'
|
|
368
|
+
'inset',
|
|
369
|
+
'inlineSize', 'blockSize', 'minInlineSize', 'maxInlineSize', 'minBlockSize', 'maxBlockSize',
|
|
370
|
+
'paddingBlockStart', 'paddingBlockEnd', 'paddingInlineStart', 'paddingInlineEnd',
|
|
371
|
+
'marginBlockStart', 'marginBlockEnd', 'marginInlineStart', 'marginInlineEnd',
|
|
372
|
+
'transform', 'transformOrigin', 'transition',
|
|
373
|
+
'animation', 'animationName', 'animationDuration', 'animationDelay',
|
|
374
|
+
'animationTimingFunction', 'animationFillMode', 'animationIterationCount',
|
|
375
|
+
'animationPlayState', 'animationDirection',
|
|
376
|
+
'gridTemplate', 'gridTemplateAreas', 'gridColumnStart', 'gridRowStart',
|
|
293
377
|
'boxShadow', 'outline', 'outlineColor', 'outlineWidth', 'outlineStyle', 'outlineOffset',
|
|
294
378
|
'whiteSpace', 'wordBreak', 'wordWrap', 'overflowWrap',
|
|
295
379
|
'visibility', 'boxSizing', 'objectFit', 'objectPosition',
|
package/package.json
CHANGED
package/render.js
CHANGED
|
@@ -4,6 +4,59 @@ import { extractMetadata, generateHeadHtml } from './metadata.js'
|
|
|
4
4
|
import { hydrate } from './hydrate.js'
|
|
5
5
|
import { parseHTML } from 'linkedom'
|
|
6
6
|
|
|
7
|
+
// ── Minimal uikit stubs ──────────────────────────────────────────────────────
|
|
8
|
+
// Lightweight versions of uikit components so DOMQL can resolve extends chains
|
|
9
|
+
// (tag, display, attrs) without importing the full @symbo.ls/uikit package.
|
|
10
|
+
const UIKIT_STUBS = {
|
|
11
|
+
Box: {},
|
|
12
|
+
Focusable: {},
|
|
13
|
+
Block: { display: 'block' },
|
|
14
|
+
Inline: { display: 'inline' },
|
|
15
|
+
Flex: { display: 'flex' },
|
|
16
|
+
InlineFlex: { display: 'inline-flex' },
|
|
17
|
+
Grid: { display: 'grid' },
|
|
18
|
+
InlineGrid: { display: 'inline-grid' },
|
|
19
|
+
Link: {
|
|
20
|
+
tag: 'a',
|
|
21
|
+
attr: {
|
|
22
|
+
href: (el) => el.props?.href,
|
|
23
|
+
target: (el) => el.props?.target,
|
|
24
|
+
rel: (el) => el.props?.rel
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
A: { extends: 'Link' },
|
|
28
|
+
RouteLink: { extends: 'Link' },
|
|
29
|
+
Img: {
|
|
30
|
+
tag: 'img',
|
|
31
|
+
attr: {
|
|
32
|
+
src: (el) => el.props?.src,
|
|
33
|
+
alt: (el) => el.props?.alt,
|
|
34
|
+
loading: (el) => el.props?.loading
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
Image: { extends: 'Img' },
|
|
38
|
+
Button: { tag: 'button' },
|
|
39
|
+
FocusableComponent: { tag: 'button' },
|
|
40
|
+
Form: { tag: 'form' },
|
|
41
|
+
Input: { tag: 'input' },
|
|
42
|
+
TextArea: { tag: 'textarea' },
|
|
43
|
+
Textarea: { tag: 'textarea' },
|
|
44
|
+
Select: { tag: 'select' },
|
|
45
|
+
Label: { tag: 'label' },
|
|
46
|
+
Iframe: { tag: 'iframe' },
|
|
47
|
+
Video: { tag: 'video' },
|
|
48
|
+
Audio: { tag: 'audio' },
|
|
49
|
+
Canvas: { tag: 'canvas' },
|
|
50
|
+
Span: { tag: 'span' },
|
|
51
|
+
P: { tag: 'p' },
|
|
52
|
+
H1: { tag: 'h1' },
|
|
53
|
+
H2: { tag: 'h2' },
|
|
54
|
+
H3: { tag: 'h3' },
|
|
55
|
+
H4: { tag: 'h4' },
|
|
56
|
+
H5: { tag: 'h5' },
|
|
57
|
+
H6: { tag: 'h6' }
|
|
58
|
+
}
|
|
59
|
+
|
|
7
60
|
/**
|
|
8
61
|
* Renders a Symbols/DomQL project to HTML on the server.
|
|
9
62
|
*
|
|
@@ -89,10 +142,14 @@ export const renderElement = async (elementDef, options = {}) => {
|
|
|
89
142
|
|
|
90
143
|
const { create } = await import('@domql/element')
|
|
91
144
|
|
|
145
|
+
// Merge minimal uikit stubs so DOMQL resolves extends chains
|
|
146
|
+
// (e.g. extends: 'Link' → tag: 'a', extends: 'Flex' → display: flex)
|
|
147
|
+
const components = { ...UIKIT_STUBS, ...(context.components || {}) }
|
|
148
|
+
|
|
92
149
|
resetKeys()
|
|
93
150
|
|
|
94
151
|
const element = create(elementDef, { node: body }, 'root', {
|
|
95
|
-
context: { document, window, ...context }
|
|
152
|
+
context: { document, window, ...context, components }
|
|
96
153
|
})
|
|
97
154
|
|
|
98
155
|
assignKeys(body)
|
|
@@ -216,7 +273,19 @@ const NON_CSS_PROPS = new Set([
|
|
|
216
273
|
const camelToKebab = (str) => str.replace(/[A-Z]/g, m => '-' + m.toLowerCase())
|
|
217
274
|
|
|
218
275
|
const resolveShorthand = (key, val) => {
|
|
219
|
-
if (
|
|
276
|
+
if (typeof val === 'undefined' || val === null) return null
|
|
277
|
+
|
|
278
|
+
// Flex shorthands
|
|
279
|
+
if (key === 'flow' && typeof val === 'string') {
|
|
280
|
+
let [direction, wrap] = (val || 'row').split(' ')
|
|
281
|
+
if (val.startsWith('x') || val === 'row') direction = 'row'
|
|
282
|
+
if (val.startsWith('y') || val === 'column') direction = 'column'
|
|
283
|
+
return { display: 'flex', 'flex-flow': (direction || '') + ' ' + (wrap || '') }
|
|
284
|
+
}
|
|
285
|
+
if (key === 'wrap') {
|
|
286
|
+
return { display: 'flex', 'flex-wrap': val }
|
|
287
|
+
}
|
|
288
|
+
if ((key === 'align' || key === 'flexAlign') && typeof val === 'string') {
|
|
220
289
|
const [alignItems, justifyContent] = val.split(' ')
|
|
221
290
|
return { display: 'flex', 'align-items': alignItems, 'justify-content': justifyContent }
|
|
222
291
|
}
|
|
@@ -224,12 +293,49 @@ const resolveShorthand = (key, val) => {
|
|
|
224
293
|
const [alignItems, justifyContent] = val.split(' ')
|
|
225
294
|
return { display: 'grid', 'align-items': alignItems, 'justify-content': justifyContent }
|
|
226
295
|
}
|
|
227
|
-
if (key === '
|
|
296
|
+
if (key === 'flexFlow' && typeof val === 'string') {
|
|
297
|
+
let [direction, wrap] = (val || 'row').split(' ')
|
|
298
|
+
if (val.startsWith('x') || val === 'row') direction = 'row'
|
|
299
|
+
if (val.startsWith('y') || val === 'column') direction = 'column'
|
|
300
|
+
return { display: 'flex', 'flex-flow': (direction || '') + ' ' + (wrap || '') }
|
|
301
|
+
}
|
|
302
|
+
if (key === 'flexWrap') {
|
|
303
|
+
return { display: 'flex', 'flex-wrap': val }
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Box/size shorthands
|
|
307
|
+
if (key === 'round' || (key === 'borderRadius' && val)) {
|
|
228
308
|
return { 'border-radius': typeof val === 'number' ? val + 'px' : val }
|
|
229
309
|
}
|
|
230
|
-
if (key === 'boxSize' && val) {
|
|
231
|
-
|
|
310
|
+
if (key === 'boxSize' && typeof val === 'string') {
|
|
311
|
+
const [height, width] = val.split(' ')
|
|
312
|
+
return { height, width: width || height }
|
|
313
|
+
}
|
|
314
|
+
if (key === 'widthRange' && typeof val === 'string') {
|
|
315
|
+
const [minWidth, maxWidth] = val.split(' ')
|
|
316
|
+
return { 'min-width': minWidth, 'max-width': maxWidth || minWidth }
|
|
317
|
+
}
|
|
318
|
+
if (key === 'heightRange' && typeof val === 'string') {
|
|
319
|
+
const [minHeight, maxHeight] = val.split(' ')
|
|
320
|
+
return { 'min-height': minHeight, 'max-height': maxHeight || minHeight }
|
|
232
321
|
}
|
|
322
|
+
|
|
323
|
+
// Grid aliases
|
|
324
|
+
if (key === 'column') return { 'grid-column': val }
|
|
325
|
+
if (key === 'columns') return { 'grid-template-columns': val }
|
|
326
|
+
if (key === 'templateColumns') return { 'grid-template-columns': val }
|
|
327
|
+
if (key === 'row') return { 'grid-row': val }
|
|
328
|
+
if (key === 'rows') return { 'grid-template-rows': val }
|
|
329
|
+
if (key === 'templateRows') return { 'grid-template-rows': val }
|
|
330
|
+
if (key === 'area') return { 'grid-area': val }
|
|
331
|
+
if (key === 'template') return { 'grid-template': val }
|
|
332
|
+
if (key === 'templateAreas') return { 'grid-template-areas': val }
|
|
333
|
+
if (key === 'autoColumns') return { 'grid-auto-columns': val }
|
|
334
|
+
if (key === 'autoRows') return { 'grid-auto-rows': val }
|
|
335
|
+
if (key === 'autoFlow') return { 'grid-auto-flow': val }
|
|
336
|
+
if (key === 'columnStart') return { 'grid-column-start': val }
|
|
337
|
+
if (key === 'rowStart') return { 'grid-row-start': val }
|
|
338
|
+
|
|
233
339
|
return null
|
|
234
340
|
}
|
|
235
341
|
|
|
@@ -300,6 +406,25 @@ const renderCSSRule = (selector, { base, mediaRules, pseudoRules }) => {
|
|
|
300
406
|
return lines.join('\n')
|
|
301
407
|
}
|
|
302
408
|
|
|
409
|
+
// Map of component names to their implicit CSS from extends
|
|
410
|
+
const EXTENDS_CSS = {
|
|
411
|
+
Flex: { display: 'flex' },
|
|
412
|
+
InlineFlex: { display: 'inline-flex' },
|
|
413
|
+
Grid: { display: 'grid' },
|
|
414
|
+
InlineGrid: { display: 'inline-grid' },
|
|
415
|
+
Block: { display: 'block' },
|
|
416
|
+
Inline: { display: 'inline' }
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const getExtendsCSS = (el) => {
|
|
420
|
+
const exts = el.__ref?.__extends
|
|
421
|
+
if (!exts || !Array.isArray(exts)) return null
|
|
422
|
+
for (const ext of exts) {
|
|
423
|
+
if (EXTENDS_CSS[ext]) return EXTENDS_CSS[ext]
|
|
424
|
+
}
|
|
425
|
+
return null
|
|
426
|
+
}
|
|
427
|
+
|
|
303
428
|
const extractCSS = (element, ds) => {
|
|
304
429
|
const colorMap = ds?.color || {}
|
|
305
430
|
const mediaMap = ds?.media || {}
|
|
@@ -316,6 +441,16 @@ const extractCSS = (element, ds) => {
|
|
|
316
441
|
if (cls && !seen.has(cls)) {
|
|
317
442
|
seen.add(cls)
|
|
318
443
|
const cssResult = buildCSSFromProps(props, colorMap, mediaMap)
|
|
444
|
+
|
|
445
|
+
// Inject CSS from extends chain (e.g. extends: 'Flex' → display: flex)
|
|
446
|
+
const extsCss = getExtendsCSS(el)
|
|
447
|
+
if (extsCss) {
|
|
448
|
+
for (const [k, v] of Object.entries(extsCss)) {
|
|
449
|
+
const kebab = camelToKebab(k)
|
|
450
|
+
if (!cssResult.base[kebab]) cssResult.base[kebab] = v
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
319
454
|
const has = Object.keys(cssResult.base).length || Object.keys(cssResult.mediaRules).length || Object.keys(cssResult.pseudoRules).length
|
|
320
455
|
if (has) rules.push(renderCSSRule('.' + cls.split(' ')[0], cssResult))
|
|
321
456
|
|