@compact-design/html 0.1.0 → 0.2.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/css.d.ts +29 -1
- package/dist/index.cjs +501 -94
- package/dist/index.js +501 -94
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -204,6 +204,18 @@ function overflowDeclarations(props) {
|
|
|
204
204
|
if (props.overflowDirection === "BOTH") return ["overflow: auto"];
|
|
205
205
|
return [];
|
|
206
206
|
}
|
|
207
|
+
function isScrollingOverflow(direction) {
|
|
208
|
+
return direction === "HORIZONTAL" || direction === "VERTICAL" || direction === "BOTH";
|
|
209
|
+
}
|
|
210
|
+
function clampedFixedChildCount(value, childCount) {
|
|
211
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0 || childCount <= 0) return 0;
|
|
212
|
+
return Math.max(0, Math.min(childCount, Math.round(value)));
|
|
213
|
+
}
|
|
214
|
+
function fixedChildStickyDeclarations(overflow) {
|
|
215
|
+
if (overflow === "VERTICAL") return ["position: sticky", "bottom: 0"];
|
|
216
|
+
if (overflow === "HORIZONTAL") return ["position: sticky", "right: 0"];
|
|
217
|
+
return ["position: sticky", "bottom: 0", "right: 0"];
|
|
218
|
+
}
|
|
207
219
|
function radiusDeclarations(props) {
|
|
208
220
|
if (Array.isArray(props.cornerRadii) && props.cornerRadii.length === 4) {
|
|
209
221
|
return [`border-radius: ${props.cornerRadii.map((value) => `${finite(value, 0)}px`).join(" ")}`];
|
|
@@ -211,11 +223,11 @@ function radiusDeclarations(props) {
|
|
|
211
223
|
if (typeof props.cornerRadius === "number") return [`border-radius: ${props.cornerRadius}px`];
|
|
212
224
|
return [];
|
|
213
225
|
}
|
|
214
|
-
function strokeDeclarations(props) {
|
|
215
|
-
const color = paintColor(props.styles.strokes[0]);
|
|
226
|
+
function strokeDeclarations(props, colorOverride) {
|
|
227
|
+
const color = colorOverride ?? paintColor(props.styles.strokes[0]);
|
|
216
228
|
if (!color) return [];
|
|
217
229
|
const style = props.dashPattern?.length ? "dashed" : "solid";
|
|
218
|
-
const
|
|
230
|
+
const align = props.strokeAlign;
|
|
219
231
|
const sides = [
|
|
220
232
|
["border-top-width", props.strokeTopWeight],
|
|
221
233
|
["border-right-width", props.strokeRightWeight],
|
|
@@ -224,14 +236,31 @@ function strokeDeclarations(props) {
|
|
|
224
236
|
];
|
|
225
237
|
const perSide = sides.some(([, value]) => typeof value === "number");
|
|
226
238
|
if (perSide) {
|
|
227
|
-
declarations
|
|
239
|
+
const declarations = [`border-style: ${style}`, `border-color: ${color}`, "border-width: 0"];
|
|
228
240
|
for (const [property, value] of sides) if (typeof value === "number") declarations.push(`${property}: ${value}px`);
|
|
229
|
-
|
|
230
|
-
const weight = finite(props.strokeWeight, 1);
|
|
231
|
-
if (weight <= 0) return [];
|
|
232
|
-
declarations.push(`border: ${weight}px ${style} ${color}`);
|
|
241
|
+
return declarations;
|
|
233
242
|
}
|
|
234
|
-
|
|
243
|
+
const weight = finite(props.strokeWeight, 1);
|
|
244
|
+
if (weight <= 0) return [];
|
|
245
|
+
if (align === "OUTSIDE") {
|
|
246
|
+
return [`box-shadow: 0 0 0 ${weight}px ${color}`];
|
|
247
|
+
}
|
|
248
|
+
if (align === "CENTER") {
|
|
249
|
+
const half = weight / 2;
|
|
250
|
+
return [`border: ${half}px ${style} ${color}`, `box-shadow: 0 0 0 ${half}px ${color}`];
|
|
251
|
+
}
|
|
252
|
+
return [`border: ${weight}px ${style} ${color}`];
|
|
253
|
+
}
|
|
254
|
+
function coalesceBoxShadows(declarations) {
|
|
255
|
+
const shadows = [];
|
|
256
|
+
const rest = [];
|
|
257
|
+
for (const declaration of declarations) {
|
|
258
|
+
const match = /^box-shadow:\s*(.+)$/i.exec(declaration);
|
|
259
|
+
if (match) shadows.push(match[1]);
|
|
260
|
+
else rest.push(declaration);
|
|
261
|
+
}
|
|
262
|
+
if (shadows.length) rest.push(`box-shadow: ${shadows.join(", ")}`);
|
|
263
|
+
return rest;
|
|
235
264
|
}
|
|
236
265
|
function blendModeCss(value) {
|
|
237
266
|
if (!value || value === "PASS_THROUGH" || value === "NORMAL") return void 0;
|
|
@@ -243,6 +272,11 @@ function textCaseCss(value) {
|
|
|
243
272
|
if (value === "TITLE") return "capitalize";
|
|
244
273
|
return void 0;
|
|
245
274
|
}
|
|
275
|
+
function textCaseVariantCss(value) {
|
|
276
|
+
if (value === "SMALL_CAPS") return "small-caps";
|
|
277
|
+
if (value === "SMALL_CAPS_FORCED") return "all-small-caps";
|
|
278
|
+
return void 0;
|
|
279
|
+
}
|
|
246
280
|
function sanitizeSvg(markup) {
|
|
247
281
|
return markup.replace(/<script[\s\S]*?<\/script>/gi, "").replace(/\son[a-z]+\s*=\s*("[^"]*"|'[^']*'|[^\s>]+)/gi, "");
|
|
248
282
|
}
|
|
@@ -257,6 +291,111 @@ function regularPolygon(cx, cy, radius, points, innerRatio) {
|
|
|
257
291
|
}
|
|
258
292
|
return coords.join(" ");
|
|
259
293
|
}
|
|
294
|
+
var TURN = Math.PI * 2;
|
|
295
|
+
function round(value) {
|
|
296
|
+
return Math.round(value * 1e3) / 1e3;
|
|
297
|
+
}
|
|
298
|
+
function arcPath(width, height, startingAngle, endingAngle, innerRadiusRatio) {
|
|
299
|
+
const rx = width / 2;
|
|
300
|
+
const ry = height / 2;
|
|
301
|
+
const ratio = Math.min(1, Math.max(0, innerRadiusRatio));
|
|
302
|
+
let sweep = endingAngle - startingAngle;
|
|
303
|
+
if (sweep < 0) sweep = sweep % TURN + TURN;
|
|
304
|
+
const point = (angle, scale) => `${round(rx + rx * scale * Math.cos(angle))} ${round(ry + ry * scale * Math.sin(angle))}`;
|
|
305
|
+
const ellipse = (scale) => {
|
|
306
|
+
const r = `${round(rx * scale)} ${round(ry * scale)}`;
|
|
307
|
+
return `M ${point(startingAngle, scale)} A ${r} 0 1 1 ${point(startingAngle + Math.PI, scale)} A ${r} 0 1 1 ${point(startingAngle, scale)} Z`;
|
|
308
|
+
};
|
|
309
|
+
if (sweep === 0 || sweep >= TURN) return ratio > 0 ? `${ellipse(1)} ${ellipse(ratio)}` : ellipse(1);
|
|
310
|
+
const end = startingAngle + sweep;
|
|
311
|
+
const large = sweep > Math.PI ? 1 : 0;
|
|
312
|
+
const outer = `M ${point(startingAngle, 1)} A ${round(rx)} ${round(ry)} 0 ${large} 1 ${point(end, 1)}`;
|
|
313
|
+
if (ratio === 0) return `${outer} L ${round(rx)} ${round(ry)} Z`;
|
|
314
|
+
return `${outer} L ${point(end, ratio)} A ${round(rx * ratio)} ${round(ry * ratio)} 0 ${large} 0 ${point(startingAngle, ratio)} Z`;
|
|
315
|
+
}
|
|
316
|
+
function layoutGridColor(color) {
|
|
317
|
+
if (isDesignColor(color)) return cssColor(color);
|
|
318
|
+
return cssColor({ r: 0, g: 0, b: 0, a: 0.1 });
|
|
319
|
+
}
|
|
320
|
+
function layoutGridLayers(grids) {
|
|
321
|
+
if (!Array.isArray(grids)) return [];
|
|
322
|
+
const layers = [];
|
|
323
|
+
for (const grid of grids) {
|
|
324
|
+
if (!isRecord(grid) || grid.visible === false) continue;
|
|
325
|
+
const color = layoutGridColor(grid.color);
|
|
326
|
+
if (grid.pattern === "GRID") {
|
|
327
|
+
const size = Math.max(1, finite(grid.sectionSize, 8));
|
|
328
|
+
for (const angle of [0, 90]) {
|
|
329
|
+
layers.push({
|
|
330
|
+
image: `repeating-linear-gradient(${angle}deg, ${color} 0, ${color} 1px, transparent 1px, transparent ${size}px)`,
|
|
331
|
+
size: "auto",
|
|
332
|
+
position: "0 0",
|
|
333
|
+
repeat: "repeat"
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (grid.pattern === "COLUMNS" || grid.pattern === "ROWS") {
|
|
339
|
+
const layer = rowsColsGridLayer(grid.pattern, grid, color);
|
|
340
|
+
if (layer) layers.push(layer);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return layers;
|
|
344
|
+
}
|
|
345
|
+
function rowsColsGridLayer(pattern, grid, color) {
|
|
346
|
+
const count = Math.max(1, Math.round(finite(grid.count, 12)));
|
|
347
|
+
const gutter = Math.max(0, finite(grid.gutterSize, 20));
|
|
348
|
+
const offset = Math.max(0, finite(grid.offset, 0));
|
|
349
|
+
const alignment = typeof grid.alignment === "string" ? grid.alignment : "STRETCH";
|
|
350
|
+
const horizontal = pattern === "COLUMNS";
|
|
351
|
+
const axis = horizontal ? "to right" : "to bottom";
|
|
352
|
+
if (alignment === "STRETCH") {
|
|
353
|
+
const section = `(100% - ${(count - 1) * gutter}px) / ${count}`;
|
|
354
|
+
return {
|
|
355
|
+
image: `repeating-linear-gradient(${axis}, ${color} 0, ${color} calc(${section}), transparent calc(${section}), transparent calc(${section} + ${gutter}px))`,
|
|
356
|
+
size: horizontal ? `calc(100% - ${2 * offset}px) 100%` : `100% calc(100% - ${2 * offset}px)`,
|
|
357
|
+
position: horizontal ? `${offset}px 0` : `0 ${offset}px`,
|
|
358
|
+
repeat: "no-repeat"
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
const sectionSize = Math.max(1, finite(grid.sectionSize, 64));
|
|
362
|
+
const span = count * sectionSize + Math.max(0, count - 1) * gutter;
|
|
363
|
+
const image = `repeating-linear-gradient(${axis}, ${color} 0, ${color} ${sectionSize}px, transparent ${sectionSize}px, transparent ${sectionSize + gutter}px)`;
|
|
364
|
+
const size = horizontal ? `${span}px 100%` : `100% ${span}px`;
|
|
365
|
+
if (alignment === "MAX") {
|
|
366
|
+
return {
|
|
367
|
+
image,
|
|
368
|
+
size,
|
|
369
|
+
position: horizontal ? `calc(100% - ${offset}px - ${span}px) 0` : `0 calc(100% - ${offset}px - ${span}px)`,
|
|
370
|
+
repeat: "no-repeat"
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
if (alignment === "CENTER") {
|
|
374
|
+
return { image, size, position: "center", repeat: "no-repeat" };
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
image,
|
|
378
|
+
size,
|
|
379
|
+
position: horizontal ? `${offset}px 0` : `0 ${offset}px`,
|
|
380
|
+
repeat: "no-repeat"
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
function layoutGridOverlayDeclarations(grids) {
|
|
384
|
+
const layers = layoutGridLayers(grids);
|
|
385
|
+
if (!layers.length) return [];
|
|
386
|
+
return [
|
|
387
|
+
'content: ""',
|
|
388
|
+
"position: absolute",
|
|
389
|
+
"inset: 0",
|
|
390
|
+
"pointer-events: none",
|
|
391
|
+
"border-radius: inherit",
|
|
392
|
+
"z-index: 9999",
|
|
393
|
+
`background-image: ${layers.map((layer) => layer.image).join(", ")}`,
|
|
394
|
+
`background-size: ${layers.map((layer) => layer.size).join(", ")}`,
|
|
395
|
+
`background-position: ${layers.map((layer) => layer.position).join(", ")}`,
|
|
396
|
+
`background-repeat: ${layers.map((layer) => layer.repeat).join(", ")}`
|
|
397
|
+
];
|
|
398
|
+
}
|
|
260
399
|
|
|
261
400
|
// src/render.ts
|
|
262
401
|
var RenderError = class extends Error {
|
|
@@ -275,13 +414,15 @@ function render(input, options = {}) {
|
|
|
275
414
|
}
|
|
276
415
|
function renderDocument(document, options = {}) {
|
|
277
416
|
const ctx = createContext(document);
|
|
278
|
-
const
|
|
279
|
-
const
|
|
417
|
+
const modes = initialBoardModes(document);
|
|
418
|
+
const css = [baseCss(options.background || "#d8d4cb"), themeCss(document)];
|
|
419
|
+
const body = document.nodes.map((node) => renderNode(node, ctx, { parent: void 0, parentHasLayout: false, modes: { ...modes } }, css)).join("\n");
|
|
280
420
|
const board = boardCss(document.nodes);
|
|
281
421
|
const fonts = fontLinks(ctx.fonts);
|
|
282
422
|
const script = runtimeScript(ctx.timeouts);
|
|
283
|
-
const stylesheet = [board, ...css].join("\n");
|
|
423
|
+
const stylesheet = [board, ...css].filter(Boolean).join("\n");
|
|
284
424
|
const title = escapeHtml(options.title || document.nodes[0]?.name || "Compact Design");
|
|
425
|
+
const modeAttrs = Object.entries(modes).map(([collection, mode]) => `data-cd-mode-${cssVar(collection)}="${escapeHtml(mode)}"`).join(" ");
|
|
285
426
|
const page = `<!doctype html>
|
|
286
427
|
<html lang="en">
|
|
287
428
|
<head>
|
|
@@ -294,7 +435,7 @@ ${stylesheet}
|
|
|
294
435
|
</style>
|
|
295
436
|
</head>
|
|
296
437
|
<body>
|
|
297
|
-
<main class="cd-board">
|
|
438
|
+
<main class="cd-board"${modeAttrs ? ` ${modeAttrs}` : ""}>
|
|
298
439
|
${body}
|
|
299
440
|
</main>
|
|
300
441
|
${script}
|
|
@@ -352,15 +493,28 @@ function renderNode(node, ctx, state, css) {
|
|
|
352
493
|
const link = prototypeLink(props.prototype, ctx, id);
|
|
353
494
|
css.push(nodeRule(id, node, props, state, modes, ctx));
|
|
354
495
|
const childrenSource = instanceOf ? instanceOf.children : node.children;
|
|
496
|
+
const parentVisual = instanceOf || node;
|
|
497
|
+
const stickyOverflow = isScrollingOverflow(parentVisual.properties.overflowDirection) ? parentVisual.properties.overflowDirection : void 0;
|
|
498
|
+
const fixedCount = stickyOverflow ? clampedFixedChildCount(parentVisual.properties.numberOfFixedChildren, childrenSource.length) : 0;
|
|
499
|
+
const fixedFrom = childrenSource.length - fixedCount;
|
|
355
500
|
const childState = {
|
|
356
|
-
parent:
|
|
357
|
-
parentHasLayout: Boolean(
|
|
501
|
+
parent: parentVisual,
|
|
502
|
+
parentHasLayout: Boolean(parentVisual.properties.layout?.direction),
|
|
358
503
|
modes
|
|
359
504
|
};
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
505
|
+
let activeMask;
|
|
506
|
+
const childMarkup = childrenSource.map((child, index) => {
|
|
507
|
+
const applied = applyInstanceText(child, node);
|
|
508
|
+
const fixedSticky = index >= fixedFrom && stickyOverflow ? stickyOverflow : void 0;
|
|
509
|
+
const maskVisual = maskVisualNode(applied, ctx);
|
|
510
|
+
if (maskVisual) {
|
|
511
|
+
activeMask = maskVisual;
|
|
512
|
+
return renderNode(applied, ctx, { ...childState, mask: void 0, asMask: true, fixedSticky }, css);
|
|
513
|
+
}
|
|
514
|
+
return renderNode(applied, ctx, { ...childState, mask: activeMask, asMask: false, fixedSticky }, css);
|
|
515
|
+
});
|
|
516
|
+
const shape = state.asMask ? "" : shapeMarkup(node.type === "INSTANCE" ? visual.type : node.type, props, ctx);
|
|
517
|
+
const inner = [shape, ...childMarkup].join("");
|
|
364
518
|
const attrs = [
|
|
365
519
|
`id="${escapeHtml(id)}"`,
|
|
366
520
|
`class="cd-node"`,
|
|
@@ -369,8 +523,12 @@ function renderNode(node, ctx, state, css) {
|
|
|
369
523
|
];
|
|
370
524
|
if (link.timeout) attrs.push(`data-cd-timeout="${link.timeout.seconds}"`, `data-cd-navigate="${escapeHtml(htmlId(link.timeout.destination))}"`);
|
|
371
525
|
if (link.back) attrs.push(`data-cd-back="true"`);
|
|
372
|
-
|
|
526
|
+
if (link.setMode) {
|
|
527
|
+
attrs.push(`data-cd-set-collection="${escapeHtml(cssVar(link.setMode.collection))}"`, `data-cd-set-mode="${escapeHtml(link.setMode.mode)}"`);
|
|
528
|
+
}
|
|
529
|
+
const tag = link.href || link.back || link.setMode ? "a" : "div";
|
|
373
530
|
if (link.href) attrs.push(`href="${escapeHtml(link.href)}"`);
|
|
531
|
+
else if (link.back || link.setMode) attrs.push(`href="#"`);
|
|
374
532
|
if (node.type === "TEXT") return `<${tag} ${attrs.join(" ")}>${textInner(props, ctx)}</${tag}>`;
|
|
375
533
|
if (node.type === "SVG" && props.svg) return `<${tag} ${attrs.join(" ")}>${sanitizeSvg(props.svg)}</${tag}>`;
|
|
376
534
|
return `<${tag} ${attrs.join(" ")}>${inner}</${tag}>`;
|
|
@@ -399,30 +557,44 @@ function applyInstanceText(child, instance) {
|
|
|
399
557
|
return { ...child, properties: { ...child.properties, text: named } };
|
|
400
558
|
}
|
|
401
559
|
function nodeRule(id, node, props, state, modes, ctx) {
|
|
560
|
+
const resolved = resolvedProps(props, ctx, modes);
|
|
402
561
|
const declarations = [
|
|
403
562
|
...positionDeclarations(props, state),
|
|
404
563
|
...sizeDeclarations(props, state),
|
|
405
564
|
...layoutDeclarations(props),
|
|
406
565
|
...childSizingDeclarations(props, state.parent?.properties.layout?.direction),
|
|
407
566
|
...overflowDeclarations(props),
|
|
408
|
-
...radiusDeclarations(props)
|
|
409
|
-
...strokeDeclarations(resolvedProps(props, ctx, modes)),
|
|
410
|
-
...paintDeclarations(node.type, resolvedProps(props, ctx, modes)),
|
|
411
|
-
...effectDeclarations(props),
|
|
412
|
-
...textDeclarations(node.type, resolvedProps(props, ctx, modes), ctx),
|
|
413
|
-
...variableCustomProperties(props, modes, ctx)
|
|
567
|
+
...radiusDeclarations(props)
|
|
414
568
|
];
|
|
415
|
-
if (
|
|
416
|
-
|
|
569
|
+
if (state.asMask) {
|
|
570
|
+
declarations.push("opacity: 0");
|
|
571
|
+
} else {
|
|
572
|
+
if (node.type !== "ARC" && node.type !== "LINE" && node.type !== "STAR" && node.type !== "POLYGON" && node.type !== "VECTOR") declarations.push(...boundStrokeDeclarations(resolved, ctx));
|
|
573
|
+
declarations.push(
|
|
574
|
+
...paintDeclarations(node.type, resolved, ctx),
|
|
575
|
+
...effectDeclarations(node.type, props),
|
|
576
|
+
...textDeclarations(node.type, resolved, ctx)
|
|
577
|
+
);
|
|
578
|
+
if (node.type === "ELLIPSE") declarations.push("border-radius: 50%");
|
|
579
|
+
if (typeof props.opacity === "number") declarations.push(`opacity: ${props.opacity}`);
|
|
580
|
+
if (state.mask) declarations.push(...siblingMaskDeclarations(state.mask, node));
|
|
581
|
+
}
|
|
417
582
|
if (typeof props.rotation === "number" && props.rotation !== 0) {
|
|
418
583
|
declarations.push("transform-origin: center center", `transform: rotate(${props.rotation}deg)`);
|
|
419
584
|
}
|
|
420
585
|
const blend = blendModeCss(props.blendMode);
|
|
421
586
|
if (blend) declarations.push(`mix-blend-mode: ${blend}`);
|
|
422
|
-
if (props.layoutPositioning === "ABSOLUTE") declarations.push("position: absolute");
|
|
423
|
-
|
|
587
|
+
if (props.layoutPositioning === "ABSOLUTE" && !state.fixedSticky) declarations.push("position: absolute");
|
|
588
|
+
const merged = coalesceBoxShadows(declarations.filter(Boolean));
|
|
589
|
+
const rules = [`#${cssEscape(id)} { ${merged.join("; ")} }`];
|
|
590
|
+
if (!state.asMask) {
|
|
591
|
+
const overlay = layoutGridOverlayDeclarations(props.layoutGrids);
|
|
592
|
+
if (overlay.length) rules.push(`#${cssEscape(id)}::after { ${overlay.join("; ")} }`);
|
|
593
|
+
}
|
|
594
|
+
return rules.join("\n");
|
|
424
595
|
}
|
|
425
596
|
function positionDeclarations(props, state) {
|
|
597
|
+
if (state.fixedSticky) return fixedChildStickyDeclarations(state.fixedSticky);
|
|
426
598
|
if (!state.parent) {
|
|
427
599
|
return [
|
|
428
600
|
"position: absolute",
|
|
@@ -459,50 +631,80 @@ function resolvedProps(props, ctx, modes) {
|
|
|
459
631
|
}
|
|
460
632
|
const next = { ...props, styles: { ...props.styles, fills, strokes } };
|
|
461
633
|
const fillBinding = props.bindings?.fill;
|
|
462
|
-
if (fillBinding) {
|
|
634
|
+
if (fillBinding && !isColorBinding(fillBinding, ctx)) {
|
|
463
635
|
const color = resolveColor(fillBinding, modes, ctx);
|
|
464
636
|
if (color) next.styles = { ...next.styles, fills: [{ type: "SOLID", color, opacity: 1 }, ...fills.slice(1)] };
|
|
465
637
|
}
|
|
466
638
|
const strokeBinding = props.bindings?.stroke;
|
|
467
|
-
if (strokeBinding) {
|
|
639
|
+
if (strokeBinding && !isColorBinding(strokeBinding, ctx)) {
|
|
468
640
|
const color = resolveColor(strokeBinding, modes, ctx);
|
|
469
641
|
if (color) next.styles = { ...next.styles, strokes: [{ type: "SOLID", color, opacity: 1 }, ...strokes.slice(1)] };
|
|
470
642
|
}
|
|
471
643
|
return next;
|
|
472
644
|
}
|
|
473
|
-
function paintDeclarations(type, props) {
|
|
645
|
+
function paintDeclarations(type, props, ctx) {
|
|
646
|
+
const fillBinding = props.bindings?.fill;
|
|
647
|
+
if (fillBinding && isColorBinding(fillBinding, ctx)) {
|
|
648
|
+
const value = `var(--cd-${cssVar(fillBinding)})`;
|
|
649
|
+
if (type === "TEXT") return [`color: ${value}`];
|
|
650
|
+
if (type === "STAR" || type === "POLYGON" || type === "VECTOR" || type === "SVG" || type === "ARC" || type === "LINE") return [];
|
|
651
|
+
return [`background: ${value}`];
|
|
652
|
+
}
|
|
474
653
|
if (type === "TEXT") {
|
|
475
654
|
const color = paintColor(props.styles.fills[0]);
|
|
476
655
|
return color ? [`color: ${color}`] : [];
|
|
477
656
|
}
|
|
478
|
-
if (type === "STAR" || type === "POLYGON" || type === "VECTOR" || type === "SVG") return [];
|
|
657
|
+
if (type === "STAR" || type === "POLYGON" || type === "VECTOR" || type === "SVG" || type === "ARC" || type === "LINE") return [];
|
|
479
658
|
const layers = backgroundLayers(props.styles.fills);
|
|
480
659
|
return layers.length ? [`background: ${layers.join(", ")}`] : [];
|
|
481
660
|
}
|
|
482
|
-
function effectDeclarations(props) {
|
|
661
|
+
function effectDeclarations(type, props) {
|
|
483
662
|
const declarations = [];
|
|
484
|
-
|
|
485
|
-
|
|
663
|
+
if (type === "TEXT") {
|
|
664
|
+
const shadows = textShadows(props.styles.effects);
|
|
665
|
+
if (shadows.length) declarations.push(`text-shadow: ${shadows.join(", ")}`);
|
|
666
|
+
} else {
|
|
667
|
+
const shadows = effectShadows(props.styles.effects);
|
|
668
|
+
if (shadows.length) declarations.push(`box-shadow: ${shadows.join(", ")}`);
|
|
669
|
+
}
|
|
486
670
|
const blur = blurFilters(props.styles.effects);
|
|
487
671
|
if (blur.filter) declarations.push(`filter: ${blur.filter}`);
|
|
488
672
|
if (blur.backdrop) declarations.push(`backdrop-filter: ${blur.backdrop}`);
|
|
489
673
|
return declarations;
|
|
490
674
|
}
|
|
675
|
+
function textShadows(effects) {
|
|
676
|
+
return effects.filter((effect) => effect.visible !== false && effect.type === "DROP_SHADOW").map((effect) => `${finite(effect.offset?.x, 0)}px ${finite(effect.offset?.y, 4)}px ${finite(effect.radius, 8)}px ${cssColor(effect.color)}`);
|
|
677
|
+
}
|
|
678
|
+
function paragraphSpacingPx(props) {
|
|
679
|
+
const value = props.paragraphSpacing;
|
|
680
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return void 0;
|
|
681
|
+
return value;
|
|
682
|
+
}
|
|
491
683
|
function textDeclarations(type, props, ctx) {
|
|
492
684
|
if (type !== "TEXT") return [];
|
|
493
685
|
const font = textFont(props, ctx);
|
|
494
686
|
const declarations = fontDeclarations(font);
|
|
495
687
|
const lineHeight = lineHeightCss(props.lineHeight);
|
|
496
688
|
const letterSpacing = letterSpacingCss(props.letterSpacing);
|
|
689
|
+
const spacing = paragraphSpacingPx(props);
|
|
497
690
|
if (lineHeight) declarations.push(`line-height: ${lineHeight}`);
|
|
498
691
|
if (letterSpacing) declarations.push(`letter-spacing: ${letterSpacing}`);
|
|
499
692
|
if (props.alignment) declarations.push(`text-align: ${props.alignment.toLowerCase()}`);
|
|
500
|
-
if (
|
|
501
|
-
|
|
693
|
+
if (spacing !== void 0) {
|
|
694
|
+
declarations.push("display: flex", "flex-direction: column", `gap: ${spacing}px`);
|
|
695
|
+
if (props.verticalAlignment === "CENTER") declarations.push("justify-content: center");
|
|
696
|
+
if (props.verticalAlignment === "BOTTOM") declarations.push("justify-content: flex-end");
|
|
697
|
+
} else {
|
|
698
|
+
if (props.verticalAlignment === "CENTER") declarations.push("display: flex", "align-items: center");
|
|
699
|
+
if (props.verticalAlignment === "BOTTOM") declarations.push("display: flex", "align-items: flex-end");
|
|
700
|
+
}
|
|
502
701
|
if (props.textDecoration === "UNDERLINE") declarations.push("text-decoration: underline");
|
|
503
702
|
if (props.textDecoration === "STRIKETHROUGH") declarations.push("text-decoration: line-through");
|
|
504
703
|
const transform = textCaseCss(props.textCase);
|
|
505
704
|
if (transform) declarations.push(`text-transform: ${transform}`);
|
|
705
|
+
const variant = textCaseVariantCss(props.textCase);
|
|
706
|
+
if (variant) declarations.push(`font-variant-caps: ${variant}`);
|
|
707
|
+
if (props.hangingPunctuation === true) declarations.push("hanging-punctuation: first");
|
|
506
708
|
if (typeof props.paragraphIndent === "number") declarations.push(`text-indent: ${props.paragraphIndent}px`);
|
|
507
709
|
if (props.textTruncation === "ENDING" || props.textAutoResize === "TRUNCATE") {
|
|
508
710
|
declarations.push("overflow: hidden", "text-overflow: ellipsis", "white-space: nowrap");
|
|
@@ -521,27 +723,62 @@ function textFont(props, ctx) {
|
|
|
521
723
|
return props.font;
|
|
522
724
|
}
|
|
523
725
|
function textInner(props, ctx) {
|
|
726
|
+
const spacing = paragraphSpacingPx(props);
|
|
727
|
+
if (spacing === void 0) return textContent(props, ctx);
|
|
728
|
+
return textParagraphs(props, ctx).map((content) => `<p style="margin: 0">${content}</p>`).join("");
|
|
729
|
+
}
|
|
730
|
+
function textContent(props, ctx) {
|
|
524
731
|
if (Array.isArray(props.runs) && props.runs.length) {
|
|
525
732
|
return props.runs.map((run) => {
|
|
526
733
|
if (!isRecord(run)) return "";
|
|
527
|
-
|
|
528
|
-
const style = [];
|
|
529
|
-
if (isRecord(run.font)) {
|
|
530
|
-
const font = {
|
|
531
|
-
family: typeof run.font.family === "string" ? run.font.family : "Arial",
|
|
532
|
-
style: typeof run.font.style === "string" ? run.font.style : "Regular",
|
|
533
|
-
size: finite(run.font.size, 16)
|
|
534
|
-
};
|
|
535
|
-
ctx.fonts.add(font.family);
|
|
536
|
-
style.push(...fontDeclarations(font));
|
|
537
|
-
}
|
|
538
|
-
const fill = runFill(run.fill);
|
|
539
|
-
if (fill) style.push(`color: ${fill}`);
|
|
540
|
-
return `<span${style.length ? ` style="${escapeHtml(style.join("; "))}"` : ""}>${escapeHtml(text)}</span>`;
|
|
734
|
+
return renderRunMarkup(run, typeof run.text === "string" ? run.text : "", ctx);
|
|
541
735
|
}).join("");
|
|
542
736
|
}
|
|
543
737
|
return escapeHtml(props.text || "");
|
|
544
738
|
}
|
|
739
|
+
function textParagraphs(props, ctx) {
|
|
740
|
+
if (Array.isArray(props.runs) && props.runs.length) {
|
|
741
|
+
const paragraphs = [[]];
|
|
742
|
+
for (const run of props.runs) {
|
|
743
|
+
if (!isRecord(run)) continue;
|
|
744
|
+
const parts = (typeof run.text === "string" ? run.text : "").split("\n");
|
|
745
|
+
for (let index = 0; index < parts.length; index++) {
|
|
746
|
+
if (index > 0) paragraphs.push([]);
|
|
747
|
+
const piece = renderRunMarkup(run, parts[index], ctx);
|
|
748
|
+
if (piece) paragraphs[paragraphs.length - 1].push(piece);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
return paragraphs.map((pieces) => pieces.join(""));
|
|
752
|
+
}
|
|
753
|
+
return (props.text || "").split("\n").map((line) => escapeHtml(line));
|
|
754
|
+
}
|
|
755
|
+
function renderRunMarkup(run, text, ctx) {
|
|
756
|
+
if (!text) return "";
|
|
757
|
+
const style = [];
|
|
758
|
+
if (isRecord(run.font)) {
|
|
759
|
+
const font = {
|
|
760
|
+
family: typeof run.font.family === "string" ? run.font.family : "Arial",
|
|
761
|
+
style: typeof run.font.style === "string" ? run.font.style : "Regular",
|
|
762
|
+
size: finite(run.font.size, 16)
|
|
763
|
+
};
|
|
764
|
+
ctx.fonts.add(font.family);
|
|
765
|
+
style.push(...fontDeclarations(font));
|
|
766
|
+
}
|
|
767
|
+
const fill = runFill(run.fill);
|
|
768
|
+
if (fill) style.push(`color: ${fill}`);
|
|
769
|
+
if (run.textDecoration === "UNDERLINE") style.push("text-decoration: underline");
|
|
770
|
+
if (run.textDecoration === "STRIKETHROUGH") style.push("text-decoration: line-through");
|
|
771
|
+
if (isRecord(run.letterSpacing)) {
|
|
772
|
+
const spacing = letterSpacingCss({
|
|
773
|
+
unit: run.letterSpacing.unit === "PERCENT" ? "PERCENT" : "PIXELS",
|
|
774
|
+
value: finite(run.letterSpacing.value, 0)
|
|
775
|
+
});
|
|
776
|
+
if (spacing) style.push(`letter-spacing: ${spacing}`);
|
|
777
|
+
}
|
|
778
|
+
let markup = `<span${style.length ? ` style="${escapeHtml(style.join("; "))}"` : ""}>${escapeHtml(text)}</span>`;
|
|
779
|
+
if (typeof run.link === "string") markup = `<a href="${escapeHtml(run.link)}">${markup}</a>`;
|
|
780
|
+
return markup;
|
|
781
|
+
}
|
|
545
782
|
function runFill(value) {
|
|
546
783
|
if (typeof value === "string" && /^#/.test(value)) return value;
|
|
547
784
|
if (isDesignColor(value)) return cssColor(value);
|
|
@@ -551,23 +788,71 @@ function runFill(value) {
|
|
|
551
788
|
}
|
|
552
789
|
return void 0;
|
|
553
790
|
}
|
|
554
|
-
function
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
if (ref.collection !== collection) continue;
|
|
559
|
-
if (ref.item.id !== key) continue;
|
|
560
|
-
const value = resolveVariableValue(ref, modes);
|
|
561
|
-
if (isDesignColor(value)) declarations.push(`--cd-${cssVar(ref.item.id || ref.item.name)}: ${cssColor(value)}`);
|
|
562
|
-
}
|
|
791
|
+
function boundStrokeDeclarations(props, ctx) {
|
|
792
|
+
const strokeBinding = props.bindings?.stroke;
|
|
793
|
+
if (strokeBinding && isColorBinding(strokeBinding, ctx)) {
|
|
794
|
+
return strokeDeclarations(props, `var(--cd-${cssVar(strokeBinding)})`);
|
|
563
795
|
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
796
|
+
return strokeDeclarations(props);
|
|
797
|
+
}
|
|
798
|
+
function isColorBinding(key, ctx) {
|
|
799
|
+
const ref = ctx.variables.get(key);
|
|
800
|
+
return Boolean(ref && ref.item.type === "COLOR");
|
|
567
801
|
}
|
|
568
802
|
function cssVar(name) {
|
|
569
803
|
return name.replace(/[^a-zA-Z0-9_-]+/g, "-");
|
|
570
804
|
}
|
|
805
|
+
function initialBoardModes(document) {
|
|
806
|
+
const modes = {};
|
|
807
|
+
for (const collection of document.variables) {
|
|
808
|
+
if (collection.modes?.length) modes[collection.name] = collection.modes[0];
|
|
809
|
+
else {
|
|
810
|
+
for (const item of collection.items) {
|
|
811
|
+
if (item.values) {
|
|
812
|
+
const first = Object.keys(item.values)[0];
|
|
813
|
+
if (first) {
|
|
814
|
+
modes[collection.name] = first;
|
|
815
|
+
break;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
const walk = (nodes) => {
|
|
822
|
+
for (const node of nodes) {
|
|
823
|
+
Object.assign(modes, node.properties.variableModes || {});
|
|
824
|
+
walk(node.children);
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
walk(document.nodes);
|
|
828
|
+
return modes;
|
|
829
|
+
}
|
|
830
|
+
function themeCss(document) {
|
|
831
|
+
const values = [];
|
|
832
|
+
const selectors = [];
|
|
833
|
+
for (const collection of document.variables) {
|
|
834
|
+
const modeNames = collection.modes?.length ? collection.modes : [...new Set(collection.items.flatMap((item) => item.values ? Object.keys(item.values) : []))];
|
|
835
|
+
if (!modeNames.length) continue;
|
|
836
|
+
const colorItems = collection.items.filter((item) => item.type === "COLOR");
|
|
837
|
+
if (!colorItems.length) continue;
|
|
838
|
+
for (const item of colorItems) {
|
|
839
|
+
const key = cssVar(item.id || item.name);
|
|
840
|
+
for (const mode of modeNames) {
|
|
841
|
+
const raw = item.values?.[mode] ?? item.value;
|
|
842
|
+
if (isDesignColor(raw)) values.push(`--cd-${key}-${cssVar(mode)}: ${cssColor(raw)}`);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
for (const mode of modeNames) {
|
|
846
|
+
const active = colorItems.map((item) => {
|
|
847
|
+
const key = cssVar(item.id || item.name);
|
|
848
|
+
return `--cd-${key}: var(--cd-${key}-${cssVar(mode)})`;
|
|
849
|
+
});
|
|
850
|
+
selectors.push(`.cd-board[data-cd-mode-${cssVar(collection.name)}="${mode}"] { ${active.join("; ")} }`);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
if (!values.length) return "";
|
|
854
|
+
return [`.cd-board { ${values.join("; ")} }`, ...selectors].join("\n");
|
|
855
|
+
}
|
|
571
856
|
function resolveColor(key, modes, ctx) {
|
|
572
857
|
const ref = ctx.variables.get(key);
|
|
573
858
|
if (!ref) return void 0;
|
|
@@ -598,40 +883,166 @@ function prototypeLink(value, ctx, sourceId) {
|
|
|
598
883
|
if (!reaction) return {};
|
|
599
884
|
const trigger = isRecord(reaction.trigger) ? reaction.trigger : {};
|
|
600
885
|
const actions = Array.isArray(reaction.actions) ? reaction.actions : [];
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
const
|
|
609
|
-
|
|
610
|
-
|
|
886
|
+
if (!actions.some(isRecord)) return {};
|
|
887
|
+
const triggerType = String(trigger.type || "ON_CLICK").toUpperCase();
|
|
888
|
+
const link = {};
|
|
889
|
+
for (const candidate of actions.filter(isRecord)) {
|
|
890
|
+
const type = String(candidate.type || "").toUpperCase();
|
|
891
|
+
if (type === "SET_VARIABLE_MODE" && triggerType !== "AFTER_TIMEOUT") {
|
|
892
|
+
const collection = typeof candidate.collection === "string" ? candidate.collection : typeof candidate.variableCollectionId === "string" ? candidate.variableCollectionId : "";
|
|
893
|
+
const mode = typeof candidate.mode === "string" ? candidate.mode : typeof candidate.variableModeId === "string" ? candidate.variableModeId : "";
|
|
894
|
+
if (collection && mode) link.setMode = { collection, mode };
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
if (type === "URL" && typeof candidate.url === "string" && !link.href) link.href = candidate.url;
|
|
898
|
+
if (type === "BACK") {
|
|
899
|
+
link.href = link.href || "#";
|
|
900
|
+
link.back = true;
|
|
901
|
+
}
|
|
902
|
+
if ((type === "NAVIGATE" || type === "SWAP" || type === "OVERLAY") && typeof candidate.destination === "string") {
|
|
903
|
+
if (triggerType === "AFTER_TIMEOUT") {
|
|
904
|
+
const seconds = finite(trigger.timeout, 1);
|
|
905
|
+
ctx.timeouts.push({ id: sourceId, seconds, destination: candidate.destination });
|
|
906
|
+
link.timeout = { seconds, destination: candidate.destination };
|
|
907
|
+
} else if (!link.href) {
|
|
908
|
+
link.href = `#${htmlId(candidate.destination)}`;
|
|
909
|
+
}
|
|
611
910
|
}
|
|
612
|
-
return { href: `#${htmlId(action.destination)}` };
|
|
613
911
|
}
|
|
614
|
-
return
|
|
912
|
+
return link;
|
|
913
|
+
}
|
|
914
|
+
function maskVisualNode(node, ctx) {
|
|
915
|
+
const instanceOf = node.type === "INSTANCE" && node.properties.componentId ? ctx.components.get(node.properties.componentId) : void 0;
|
|
916
|
+
const props = mergeInstance(node, instanceOf);
|
|
917
|
+
if (props.isMask !== true) return void 0;
|
|
918
|
+
if (instanceOf) return { ...node, type: instanceOf.type, properties: props };
|
|
919
|
+
return { ...node, properties: props };
|
|
920
|
+
}
|
|
921
|
+
function siblingMaskDeclarations(mask, sibling) {
|
|
922
|
+
const mw = finite(mask.properties.size.width, 0);
|
|
923
|
+
const mh = finite(mask.properties.size.height, 0);
|
|
924
|
+
if (mw <= 0 || mh <= 0) return [];
|
|
925
|
+
const x = finite(mask.properties.position.x, 0) - finite(sibling.properties.position.x, 0);
|
|
926
|
+
const y = finite(mask.properties.position.y, 0) - finite(sibling.properties.position.y, 0);
|
|
927
|
+
if (mask.type === "ELLIPSE") {
|
|
928
|
+
return [`clip-path: ellipse(${mw / 2}px ${mh / 2}px at ${x + mw / 2}px ${y + mh / 2}px)`];
|
|
929
|
+
}
|
|
930
|
+
if (mask.type !== "RECTANGLE") return [];
|
|
931
|
+
const round2 = rectangleMaskRound(mask.properties);
|
|
932
|
+
if (round2) {
|
|
933
|
+
return [`clip-path: xywh(${x}px ${y}px ${mw}px ${mh}px${round2})`];
|
|
934
|
+
}
|
|
935
|
+
return [
|
|
936
|
+
"-webkit-mask-image: linear-gradient(#000 0 0)",
|
|
937
|
+
"mask-image: linear-gradient(#000 0 0)",
|
|
938
|
+
`-webkit-mask-size: ${mw}px ${mh}px`,
|
|
939
|
+
`mask-size: ${mw}px ${mh}px`,
|
|
940
|
+
`-webkit-mask-position: ${x}px ${y}px`,
|
|
941
|
+
`mask-position: ${x}px ${y}px`,
|
|
942
|
+
"-webkit-mask-repeat: no-repeat",
|
|
943
|
+
"mask-repeat: no-repeat"
|
|
944
|
+
];
|
|
945
|
+
}
|
|
946
|
+
function rectangleMaskRound(props) {
|
|
947
|
+
if (Array.isArray(props.cornerRadii) && props.cornerRadii.length === 4) {
|
|
948
|
+
const radii = props.cornerRadii.map((value) => finite(value, 0));
|
|
949
|
+
if (radii.some((value) => value > 0)) return ` round ${radii.map((value) => `${value}px`).join(" ")}`;
|
|
950
|
+
return "";
|
|
951
|
+
}
|
|
952
|
+
if (typeof props.cornerRadius === "number" && props.cornerRadius > 0) {
|
|
953
|
+
return ` round ${props.cornerRadius}px`;
|
|
954
|
+
}
|
|
955
|
+
return "";
|
|
956
|
+
}
|
|
957
|
+
function shapeFill(props, ctx) {
|
|
958
|
+
const fillBinding = props.bindings?.fill;
|
|
959
|
+
if (fillBinding && isColorBinding(fillBinding, ctx)) return `var(--cd-${cssVar(fillBinding)})`;
|
|
960
|
+
return paintColor(props.styles.fills[0]) || "currentColor";
|
|
615
961
|
}
|
|
616
|
-
function
|
|
617
|
-
const
|
|
618
|
-
if (
|
|
962
|
+
function shapeStroke(props, ctx) {
|
|
963
|
+
const strokeBinding = props.bindings?.stroke;
|
|
964
|
+
if (strokeBinding && isColorBinding(strokeBinding, ctx)) return `var(--cd-${cssVar(strokeBinding)})`;
|
|
965
|
+
return paintColor(props.styles.strokes[0]);
|
|
966
|
+
}
|
|
967
|
+
function shapeMarkup(type, props, ctx) {
|
|
968
|
+
const fill = shapeFill(props, ctx);
|
|
969
|
+
if (type === "ELLIPSE") {
|
|
619
970
|
return "";
|
|
620
971
|
}
|
|
972
|
+
if (type === "ARC") {
|
|
973
|
+
const width = Math.max(1, finite(props.size.width, 1));
|
|
974
|
+
const height = Math.max(1, finite(props.size.height, 1));
|
|
975
|
+
const d = arcPath(width, height, finite(props.startingAngle, 0), finite(props.endingAngle, Math.PI * 1.5), finite(props.innerRadiusRatio, 0));
|
|
976
|
+
const strokeAttrs = svgPaintStrokeAttrs(props, ctx);
|
|
977
|
+
return `<svg class="cd-shape" viewBox="0 0 ${width} ${height}" overflow="visible" aria-hidden="true"><path d="${d}" fill-rule="evenodd" fill="${escapeHtml(fill)}"${strokeAttrs}/></svg>`;
|
|
978
|
+
}
|
|
621
979
|
if (type === "POLYGON" || type === "STAR") {
|
|
622
980
|
const width = Math.max(1, finite(props.size.width, 1));
|
|
623
981
|
const height = Math.max(1, finite(props.size.height, 1));
|
|
624
982
|
const points = regularPolygon(width / 2, height / 2, Math.min(width, height) / 2, finite(props.pointCount, type === "STAR" ? 5 : 6), type === "STAR" ? finite(props.innerRadius, 0.5) : void 0);
|
|
625
|
-
|
|
983
|
+
const strokeAttrs = svgPaintStrokeAttrs(props, ctx);
|
|
984
|
+
return `<svg class="cd-shape" viewBox="0 0 ${width} ${height}" aria-hidden="true"><polygon points="${points}" fill="${escapeHtml(fill)}"${strokeAttrs}/></svg>`;
|
|
626
985
|
}
|
|
627
986
|
if (type === "VECTOR" && props.vectorPaths?.length) {
|
|
628
987
|
const width = Math.max(1, finite(props.size.width, 1));
|
|
629
988
|
const height = Math.max(1, finite(props.size.height, 1));
|
|
630
|
-
const
|
|
989
|
+
const strokeAttrs = svgPaintStrokeAttrs(props, ctx);
|
|
990
|
+
const paths = props.vectorPaths.map((path) => {
|
|
991
|
+
const pathFill = path.windingRule === "NONE" ? "none" : fill;
|
|
992
|
+
const fillRule = path.windingRule === "EVENODD" ? "evenodd" : "nonzero";
|
|
993
|
+
return `<path d="${escapeHtml(path.data)}" fill-rule="${fillRule}" fill="${escapeHtml(pathFill)}"${strokeAttrs}/>`;
|
|
994
|
+
}).join("");
|
|
631
995
|
return `<svg class="cd-shape" viewBox="0 0 ${width} ${height}" aria-hidden="true">${paths}</svg>`;
|
|
632
996
|
}
|
|
997
|
+
if (type === "LINE") {
|
|
998
|
+
const width = Math.max(1, finite(props.size.width, 1));
|
|
999
|
+
const height = Math.max(1, finite(props.size.height, 1));
|
|
1000
|
+
const horizontal = width >= height;
|
|
1001
|
+
const x1 = horizontal ? 0 : width / 2;
|
|
1002
|
+
const y1 = horizontal ? height / 2 : 0;
|
|
1003
|
+
const x2 = horizontal ? width : width / 2;
|
|
1004
|
+
const y2 = horizontal ? height / 2 : height;
|
|
1005
|
+
const stroke = shapeStroke(props, ctx) || fill;
|
|
1006
|
+
const attrs = [
|
|
1007
|
+
`x1="${x1}"`,
|
|
1008
|
+
`y1="${y1}"`,
|
|
1009
|
+
`x2="${x2}"`,
|
|
1010
|
+
`y2="${y2}"`,
|
|
1011
|
+
...svgStrokeAttrList(props, stroke)
|
|
1012
|
+
];
|
|
1013
|
+
return `<svg class="cd-shape" viewBox="0 0 ${width} ${height}" overflow="visible" aria-hidden="true"><line ${attrs.join(" ")}/></svg>`;
|
|
1014
|
+
}
|
|
633
1015
|
return "";
|
|
634
1016
|
}
|
|
1017
|
+
function svgPaintStrokeAttrs(props, ctx) {
|
|
1018
|
+
const stroke = shapeStroke(props, ctx);
|
|
1019
|
+
if (!stroke) return "";
|
|
1020
|
+
return ` ${svgStrokeAttrList(props, stroke).join(" ")}`;
|
|
1021
|
+
}
|
|
1022
|
+
function svgStrokeAttrList(props, stroke) {
|
|
1023
|
+
const attrs = [
|
|
1024
|
+
`stroke="${escapeHtml(stroke)}"`,
|
|
1025
|
+
`stroke-width="${finite(props.strokeWeight, 1)}"`
|
|
1026
|
+
];
|
|
1027
|
+
if (props.dashPattern?.length) attrs.push(`stroke-dasharray="${props.dashPattern.map((n) => finite(n, 0)).join(" ")}"`);
|
|
1028
|
+
const linecap = strokeLinecapCss(props.strokeCap);
|
|
1029
|
+
if (linecap) attrs.push(`stroke-linecap="${linecap}"`);
|
|
1030
|
+
const linejoin = strokeLinejoinCss(props.strokeJoin);
|
|
1031
|
+
if (linejoin) attrs.push(`stroke-linejoin="${linejoin}"`);
|
|
1032
|
+
return attrs;
|
|
1033
|
+
}
|
|
1034
|
+
function strokeLinecapCss(cap) {
|
|
1035
|
+
if (cap === "ROUND") return "round";
|
|
1036
|
+
if (cap === "SQUARE") return "square";
|
|
1037
|
+
if (cap === "NONE") return "butt";
|
|
1038
|
+
return void 0;
|
|
1039
|
+
}
|
|
1040
|
+
function strokeLinejoinCss(join) {
|
|
1041
|
+
if (join === "MITER") return "miter";
|
|
1042
|
+
if (join === "BEVEL") return "bevel";
|
|
1043
|
+
if (join === "ROUND") return "round";
|
|
1044
|
+
return void 0;
|
|
1045
|
+
}
|
|
635
1046
|
function fontLinks(families) {
|
|
636
1047
|
const custom = [...families].filter((family) => !isWebSafeFont(family));
|
|
637
1048
|
if (!custom.length) return "";
|
|
@@ -640,23 +1051,19 @@ function fontLinks(families) {
|
|
|
640
1051
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
641
1052
|
<link href="https://fonts.googleapis.com/css2?${query}&display=swap" rel="stylesheet">`;
|
|
642
1053
|
}
|
|
643
|
-
function runtimeScript(
|
|
644
|
-
|
|
645
|
-
return `<script>
|
|
1054
|
+
function runtimeScript(_timeouts) {
|
|
1055
|
+
return `<script>
|
|
646
1056
|
(() => {
|
|
647
|
-
|
|
648
|
-
addEventListener("hashchange", () => stack.push(location.hash));
|
|
649
|
-
document.querySelectorAll("[data-cd-back]").forEach((node) => {
|
|
1057
|
+
document.querySelectorAll("[data-cd-set-mode]").forEach((node) => {
|
|
650
1058
|
node.addEventListener("click", (event) => {
|
|
651
1059
|
event.preventDefault();
|
|
652
|
-
|
|
1060
|
+
const collection = (node.getAttribute("data-cd-set-collection") || "").replace(/[^a-zA-Z0-9_-]+/g, "-");
|
|
1061
|
+
const mode = node.getAttribute("data-cd-set-mode");
|
|
1062
|
+
const board = node.closest(".cd-board");
|
|
1063
|
+
if (!board || !collection || !mode) return;
|
|
1064
|
+
board.setAttribute("data-cd-mode-" + collection, mode);
|
|
653
1065
|
});
|
|
654
1066
|
});
|
|
655
|
-
})();
|
|
656
|
-
</script>`;
|
|
657
|
-
}
|
|
658
|
-
return `<script>
|
|
659
|
-
(() => {
|
|
660
1067
|
document.querySelectorAll("[data-cd-timeout]").forEach((node) => {
|
|
661
1068
|
const seconds = Number(node.getAttribute("data-cd-timeout"));
|
|
662
1069
|
const destination = node.getAttribute("data-cd-navigate");
|