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