@bendyline/squisq-react 2.11.11 → 2.11.12
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/{chunk-X4I6Y3OP.js → chunk-7T26IKW2.js} +1 -1
- package/dist/{chunk-CLP3VWJ2.js → chunk-P3FJ3YKS.js} +6 -4
- package/dist/{chunk-HERX7GUP.js → chunk-QWUQNB4E.js} +32 -9
- package/dist/{chunk-3SKOLZUA.js → chunk-U2FIZWAL.js} +11 -1
- package/dist/{chunk-W6MKRWL6.js → chunk-WHVSQ42V.js} +193 -116
- package/dist/hooks/index.d.ts +6 -1
- package/dist/hooks/index.js +1 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.js +5 -5
- package/dist/layers/index.js +1 -1
- package/dist/page/index.js +3 -3
- package/dist/player/index.d.ts +1 -1
- package/dist/player/index.js +4 -4
- package/dist/{player-DOdWRK4X.d.ts → player-BCPc3Tv3.d.ts} +7 -1
- package/dist/squisq-player.full.global.js +441 -441
- package/dist/squisq-player.global.js +71 -71
- package/dist/standalone-source.js +1 -1
- package/dist/styles/index.css +216 -0
- package/package.json +2 -2
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
TreeLayer,
|
|
10
10
|
VideoLayer,
|
|
11
11
|
getTransitionClass
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-WHVSQ42V.js";
|
|
13
13
|
import {
|
|
14
14
|
useAutoSurface
|
|
15
15
|
} from "./chunk-TT6ENR6T.js";
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
// src/BlockRenderer.tsx
|
|
25
25
|
import { useId, useLayoutEffect, useRef, useState } from "react";
|
|
26
26
|
import { resolveTransitionDuration } from "@bendyline/squisq/schemas";
|
|
27
|
+
import { isGeometryBoundTemplate } from "@bendyline/squisq/doc";
|
|
27
28
|
|
|
28
29
|
// src/utils/textFit.ts
|
|
29
30
|
function largestFittingTextScale(fits, maximum = 2) {
|
|
@@ -138,6 +139,7 @@ function BlockRenderer({
|
|
|
138
139
|
const clipId = `vb-clip-${instanceId}-${block.id}`;
|
|
139
140
|
const svgRef = useRef(null);
|
|
140
141
|
const [textScale, setTextScale] = useState(1);
|
|
142
|
+
const growText = growTextToFit && !isGeometryBoundTemplate(block.template);
|
|
141
143
|
useLayoutEffect(() => {
|
|
142
144
|
const svg = svgRef.current;
|
|
143
145
|
let cancelled = false;
|
|
@@ -145,7 +147,7 @@ function BlockRenderer({
|
|
|
145
147
|
const measure = () => {
|
|
146
148
|
if (cancelled) return;
|
|
147
149
|
const targets = Array.from(svg.querySelectorAll("[data-squisq-text-fit]"));
|
|
148
|
-
if (!
|
|
150
|
+
if (!growText || targets.length === 0) {
|
|
149
151
|
applyTextScale(targets, 1);
|
|
150
152
|
setTextScale(1);
|
|
151
153
|
return;
|
|
@@ -175,7 +177,7 @@ function BlockRenderer({
|
|
|
175
177
|
return () => {
|
|
176
178
|
cancelled = true;
|
|
177
179
|
};
|
|
178
|
-
}, [block.layers,
|
|
180
|
+
}, [block.layers, growText, viewport.height, viewport.width]);
|
|
179
181
|
return /* @__PURE__ */ jsxs(
|
|
180
182
|
"svg",
|
|
181
183
|
{
|
|
@@ -186,7 +188,7 @@ function BlockRenderer({
|
|
|
186
188
|
preserveAspectRatio: "xMidYMid meet",
|
|
187
189
|
overflow: "hidden",
|
|
188
190
|
"data-block-id": block.id,
|
|
189
|
-
"data-squisq-text-scale":
|
|
191
|
+
"data-squisq-text-scale": growText ? textScale : void 0,
|
|
190
192
|
children: [
|
|
191
193
|
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("clipPath", { id: clipId, children: /* @__PURE__ */ jsx("rect", { x: "0", y: "0", width: viewport.width, height: viewport.height }) }) }),
|
|
192
194
|
/* @__PURE__ */ jsx("g", { clipPath: `url(#${clipId})`, children: (block.layers ?? []).map((layer) => /* @__PURE__ */ jsx(
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BlockRenderer,
|
|
3
3
|
LinearDocView
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-P3FJ3YKS.js";
|
|
5
5
|
import {
|
|
6
6
|
isCaptureDrivenVideo,
|
|
7
7
|
seekVideoToFrame
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-WHVSQ42V.js";
|
|
9
9
|
import {
|
|
10
10
|
useAudioSync,
|
|
11
11
|
useDocPlayback,
|
|
12
12
|
useMediaSchedule,
|
|
13
13
|
useViewportOrientation
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-U2FIZWAL.js";
|
|
15
15
|
import {
|
|
16
16
|
useAutoSurface
|
|
17
17
|
} from "./chunk-TT6ENR6T.js";
|
|
@@ -2288,7 +2288,8 @@ import {
|
|
|
2288
2288
|
isTemplateBlock as isTemplateBlock2,
|
|
2289
2289
|
getCaptionAtTime as getCaptionAtTime2,
|
|
2290
2290
|
resolveMediaSchedule,
|
|
2291
|
-
getDocPlaybackDuration
|
|
2291
|
+
getDocPlaybackDuration,
|
|
2292
|
+
resolveMotionForDoc
|
|
2292
2293
|
} from "@bendyline/squisq/schemas";
|
|
2293
2294
|
import { applySurface, pipStyleVars } from "@bendyline/squisq/schemas";
|
|
2294
2295
|
|
|
@@ -2585,6 +2586,7 @@ function DocPlayerContent({
|
|
|
2585
2586
|
coverSlidePlayback,
|
|
2586
2587
|
coverVisible,
|
|
2587
2588
|
theme,
|
|
2589
|
+
motion: motionProp,
|
|
2588
2590
|
surface,
|
|
2589
2591
|
captionStyle = "standard",
|
|
2590
2592
|
captionPosition,
|
|
@@ -2787,6 +2789,12 @@ function DocPlayerContent({
|
|
|
2787
2789
|
}),
|
|
2788
2790
|
[resolvedPipStyle]
|
|
2789
2791
|
);
|
|
2792
|
+
const hasAnchoredBlocks = useMemo5(
|
|
2793
|
+
() => doc.blocks.some(
|
|
2794
|
+
(block) => typeof block.sourceStartTime === "number"
|
|
2795
|
+
),
|
|
2796
|
+
[doc.blocks]
|
|
2797
|
+
);
|
|
2790
2798
|
const {
|
|
2791
2799
|
currentBlock,
|
|
2792
2800
|
currentBlockIndex,
|
|
@@ -2803,6 +2811,7 @@ function DocPlayerContent({
|
|
|
2803
2811
|
} = useDocPlayback(doc, currentTime, {
|
|
2804
2812
|
viewport: activeViewport,
|
|
2805
2813
|
theme: effectiveTheme,
|
|
2814
|
+
motion: motionProp,
|
|
2806
2815
|
onSeek: seekTo,
|
|
2807
2816
|
// Offline capture may visit frames out of order; derive the crossfade
|
|
2808
2817
|
// partner from block order rather than from what was rendered last.
|
|
@@ -2810,13 +2819,21 @@ function DocPlayerContent({
|
|
|
2810
2819
|
// A synthetic track is only the timer used by an unnarrated preview.
|
|
2811
2820
|
// Narration pacing may compact short visual beats, but it must never
|
|
2812
2821
|
// remove authored slides from the default loss-averse projection.
|
|
2813
|
-
|
|
2822
|
+
// Offline capture also runs on the synthetic clock, yet a narrated doc
|
|
2823
|
+
// (blocks anchored to its segments by `sourceStartTime`) still takes its
|
|
2824
|
+
// timeline from those segments there — the exporter muxes the audio
|
|
2825
|
+
// afterwards. Without them, block durations would simply stack and every
|
|
2826
|
+
// gap between anchored blocks would pull the rest earlier, so the render
|
|
2827
|
+
// would no longer match live playback.
|
|
2828
|
+
useAudioSegmentTiming: audioMode !== "synthetic" || renderMode && hasAnchoredBlocks
|
|
2814
2829
|
});
|
|
2815
2830
|
const coverBlock = useMemo5(() => {
|
|
2816
2831
|
const startBlockConfig = doc.startBlock;
|
|
2817
2832
|
if (!appearance.showCoverSlide) return null;
|
|
2818
2833
|
if (!startBlockConfig) return null;
|
|
2819
|
-
const context = createTemplateContext(effectiveTheme, 0, 1, activeViewport
|
|
2834
|
+
const context = createTemplateContext(effectiveTheme, 0, 1, activeViewport, {
|
|
2835
|
+
motion: resolveMotionForDoc(doc, effectiveTheme, motionProp)
|
|
2836
|
+
});
|
|
2820
2837
|
const layers = expandCoverBlock(startBlockConfig, context, appearance.coverSlideTemplate);
|
|
2821
2838
|
return {
|
|
2822
2839
|
id: "cover-block",
|
|
@@ -2828,7 +2845,8 @@ function DocPlayerContent({
|
|
|
2828
2845
|
layers
|
|
2829
2846
|
};
|
|
2830
2847
|
}, [
|
|
2831
|
-
doc
|
|
2848
|
+
doc,
|
|
2849
|
+
motionProp,
|
|
2832
2850
|
activeViewport,
|
|
2833
2851
|
effectiveTheme,
|
|
2834
2852
|
appearance.showCoverSlide,
|
|
@@ -2962,6 +2980,7 @@ function DocPlayerContent({
|
|
|
2962
2980
|
if (selector && await selector(video, targetTime)) return;
|
|
2963
2981
|
await seekVideoToFrame(video, targetTime);
|
|
2964
2982
|
};
|
|
2983
|
+
let seekedActiveAnimations = 0;
|
|
2965
2984
|
(root.getAnimations?.({ subtree: true }) ?? []).forEach((anim) => {
|
|
2966
2985
|
const target = anim.effect?.target;
|
|
2967
2986
|
if (!target) return;
|
|
@@ -2979,6 +2998,8 @@ function DocPlayerContent({
|
|
|
2979
2998
|
anim.pause();
|
|
2980
2999
|
}
|
|
2981
3000
|
anim.currentTime = Math.max(0, position);
|
|
3001
|
+
const end = anim.effect?.getComputedTiming?.().endTime;
|
|
3002
|
+
if (typeof end !== "number" || position < end) seekedActiveAnimations += 1;
|
|
2982
3003
|
});
|
|
2983
3004
|
const blockElapsed = time - blockStartTime;
|
|
2984
3005
|
const videoSeekPromises = [];
|
|
@@ -3014,7 +3035,9 @@ function DocPlayerContent({
|
|
|
3014
3035
|
const blocksUnchanged = previousBlocks !== null && previousBlocks.length === presentedBlocks.length && presentedBlocks.every(
|
|
3015
3036
|
(block, index) => block.element === previousBlocks[index].element && block.className === previousBlocks[index].className
|
|
3016
3037
|
);
|
|
3017
|
-
if (!renderMode || !blocksUnchanged
|
|
3038
|
+
if (!renderMode || !blocksUnchanged || seekedActiveAnimations > 0) {
|
|
3039
|
+
await waitForVisualUpdate();
|
|
3040
|
+
}
|
|
3018
3041
|
};
|
|
3019
3042
|
const getDuration = () => {
|
|
3020
3043
|
const mediaDuration = getDocPlaybackDuration(doc);
|
|
@@ -3556,7 +3579,7 @@ function DocPlayerContent({
|
|
|
3556
3579
|
BlockRenderer,
|
|
3557
3580
|
{
|
|
3558
3581
|
block: previousBlock,
|
|
3559
|
-
blockTime,
|
|
3582
|
+
blockTime: Math.max(0, currentTime - previousBlock.startTime),
|
|
3560
3583
|
basePath,
|
|
3561
3584
|
isExiting: true,
|
|
3562
3585
|
transition: currentBlock?.transition,
|
|
@@ -476,6 +476,7 @@ function useAudioSync(audioRef, track, basePath = "", enabled = true, mode = "me
|
|
|
476
476
|
|
|
477
477
|
// src/hooks/useDocPlayback.ts
|
|
478
478
|
import { useMemo as useMemo3, useCallback as useCallback2, useRef as useRef2 } from "react";
|
|
479
|
+
import { resolveMotionForDoc } from "@bendyline/squisq/schemas";
|
|
479
480
|
import {
|
|
480
481
|
DEFAULT_THEME,
|
|
481
482
|
getBlockAtTime,
|
|
@@ -495,7 +496,8 @@ function useDocPlayback(script, currentTime, options = {}) {
|
|
|
495
496
|
theme,
|
|
496
497
|
onSeek,
|
|
497
498
|
useAudioSegmentTiming = true,
|
|
498
|
-
deterministicTransitions = false
|
|
499
|
+
deterministicTransitions = false,
|
|
500
|
+
motion: motionOverride
|
|
499
501
|
} = options;
|
|
500
502
|
const blocks = useMemo3(() => {
|
|
501
503
|
if (!script?.blocks) {
|
|
@@ -519,6 +521,11 @@ function useDocPlayback(script, currentTime, options = {}) {
|
|
|
519
521
|
viewport,
|
|
520
522
|
persistentLayers,
|
|
521
523
|
theme,
|
|
524
|
+
motion: resolveMotionForDoc(
|
|
525
|
+
{ motion: script.motion, frontmatter: script.frontmatter },
|
|
526
|
+
resolvedTheme,
|
|
527
|
+
motionOverride
|
|
528
|
+
),
|
|
522
529
|
// Custom (user-defined) templates inlined into the doc's
|
|
523
530
|
// frontmatter — see CustomTemplates.ts. Merged onto the
|
|
524
531
|
// built-in registry so blocks annotated with `{[myhero]}`
|
|
@@ -536,6 +543,9 @@ function useDocPlayback(script, currentTime, options = {}) {
|
|
|
536
543
|
script?.audio?.segments,
|
|
537
544
|
script?.persistentLayers,
|
|
538
545
|
script?.customTemplates,
|
|
546
|
+
script?.motion,
|
|
547
|
+
script?.frontmatter,
|
|
548
|
+
motionOverride,
|
|
539
549
|
viewport,
|
|
540
550
|
theme,
|
|
541
551
|
useAudioSegmentTiming
|
|
@@ -239,8 +239,16 @@ function remapToKenBurns(anim) {
|
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
// src/layers/TextLayer.tsx
|
|
242
|
-
import {
|
|
242
|
+
import {
|
|
243
|
+
Fragment,
|
|
244
|
+
useId,
|
|
245
|
+
useLayoutEffect,
|
|
246
|
+
useMemo,
|
|
247
|
+
useRef,
|
|
248
|
+
useState
|
|
249
|
+
} from "react";
|
|
243
250
|
import { DEFAULT_DOC_FONT } from "@bendyline/squisq/schemas";
|
|
251
|
+
import { formatCountUp, wordRevealDelay } from "@bendyline/squisq/doc";
|
|
244
252
|
import {
|
|
245
253
|
parseHtmlToNodes,
|
|
246
254
|
sanitizeHtmlNodes,
|
|
@@ -253,6 +261,16 @@ import {
|
|
|
253
261
|
iconClass
|
|
254
262
|
} from "@bendyline/squisq/icon-marker";
|
|
255
263
|
|
|
264
|
+
// src/utils/defsId.ts
|
|
265
|
+
function defsIdFor(reactId, layerId) {
|
|
266
|
+
const raw = `${reactId}-${layerId}`;
|
|
267
|
+
const safe = raw.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
268
|
+
if (safe === raw.replace(/:/g, "_")) return safe.replace(/^_+/, "d");
|
|
269
|
+
let hash = 0;
|
|
270
|
+
for (let i = 0; i < raw.length; i++) hash = hash * 31 + raw.charCodeAt(i) >>> 0;
|
|
271
|
+
return `${safe.replace(/^_+/, "d")}-${hash.toString(36)}`;
|
|
272
|
+
}
|
|
273
|
+
|
|
256
274
|
// src/utils/fillStyle.tsx
|
|
257
275
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
258
276
|
function borderDashArray(style, strokeWidth) {
|
|
@@ -351,7 +369,7 @@ function resolveShapeFilter(layerId, filter) {
|
|
|
351
369
|
}
|
|
352
370
|
|
|
353
371
|
// src/layers/TextLayer.tsx
|
|
354
|
-
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
372
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
355
373
|
function TextLayer(props) {
|
|
356
374
|
if (props.layer.content.html?.trim()) return /* @__PURE__ */ jsx3(RichTextLayer, { ...props });
|
|
357
375
|
if (hasIconMarker(props.layer.content.text ?? "")) return /* @__PURE__ */ jsx3(IconTextLayer, { ...props });
|
|
@@ -401,37 +419,45 @@ function IconTextLayer({ layer, viewport, blockTime, textScale = 1 }) {
|
|
|
401
419
|
...style.shadow ? { textShadow: "0 2px 3px rgba(0,0,0,0.7)" } : {},
|
|
402
420
|
...animStyle.style
|
|
403
421
|
};
|
|
404
|
-
return /* @__PURE__ */ jsx3(
|
|
405
|
-
"
|
|
422
|
+
return /* @__PURE__ */ jsx3(
|
|
423
|
+
"g",
|
|
406
424
|
{
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
height: boxHeight,
|
|
411
|
-
style: { overflow: "visible" },
|
|
425
|
+
className: `block-layer block-layer--text ${animStyle.className}`,
|
|
426
|
+
style: animStyle.style,
|
|
427
|
+
"data-layer-id": layer.id,
|
|
412
428
|
children: /* @__PURE__ */ jsx3(
|
|
413
|
-
"
|
|
429
|
+
"foreignObject",
|
|
414
430
|
{
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
431
|
+
x: boxX,
|
|
432
|
+
y: boxY,
|
|
433
|
+
width: boxWidth,
|
|
434
|
+
height: boxHeight,
|
|
435
|
+
style: { overflow: "visible" },
|
|
419
436
|
children: /* @__PURE__ */ jsx3(
|
|
420
437
|
"div",
|
|
421
438
|
{
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
"
|
|
425
|
-
|
|
439
|
+
...{ xmlns: "http://www.w3.org/1999/xhtml" },
|
|
440
|
+
"data-squisq-text-fit": "html",
|
|
441
|
+
"data-squisq-base-font-size": style.fontSize,
|
|
442
|
+
style: boxStyle,
|
|
443
|
+
children: /* @__PURE__ */ jsx3(
|
|
444
|
+
"div",
|
|
445
|
+
{
|
|
446
|
+
"data-squisq-text-content": "true",
|
|
447
|
+
style: { width: "100%", whiteSpace: "pre-wrap", wordBreak: "break-word" },
|
|
448
|
+
"aria-label": plain,
|
|
449
|
+
dangerouslySetInnerHTML: { __html: html }
|
|
450
|
+
}
|
|
451
|
+
)
|
|
426
452
|
}
|
|
427
453
|
)
|
|
428
454
|
}
|
|
429
455
|
)
|
|
430
456
|
}
|
|
431
|
-
)
|
|
457
|
+
);
|
|
432
458
|
}
|
|
433
459
|
function PlainTextLayer({ layer, viewport, blockTime, textScale = 1 }) {
|
|
434
|
-
const defsId =
|
|
460
|
+
const defsId = defsIdFor(useId(), layer.id);
|
|
435
461
|
const { content, position, animation } = layer;
|
|
436
462
|
const { text, style } = content;
|
|
437
463
|
const rawX = resolveValue(position.x, viewport.width);
|
|
@@ -447,7 +473,10 @@ function PlainTextLayer({ layer, viewport, blockTime, textScale = 1 }) {
|
|
|
447
473
|
const fitWidth = boxWidth ?? (textAnchor === "middle" ? Math.max(0, Math.min(x, viewport.width - x) * 2) : textAnchor === "end" ? Math.max(0, x) : Math.max(0, viewport.width - x));
|
|
448
474
|
const fitHeight = boxHeight ?? (dominantBaseline === "middle" ? Math.max(0, Math.min(y, viewport.height - y) * 2) : dominantBaseline === "text-after-edge" ? Math.max(0, y) : Math.max(0, viewport.height - y));
|
|
449
475
|
const animStyle = getAnimationStyle(animation, blockTime);
|
|
450
|
-
const
|
|
476
|
+
const isCountUp = animation?.type === "countUp";
|
|
477
|
+
const isWordReveal = animation?.type === "wordReveal";
|
|
478
|
+
const displayText = isCountUp && animation ? formatCountUp(text ?? "", animation, blockTime) : text;
|
|
479
|
+
const rawLines = (displayText ?? "").split("\n");
|
|
451
480
|
let lines = maxWidth ? rawLines.reduce(
|
|
452
481
|
(acc, line) => acc.concat(wrapText(line, style.fontSize, maxWidth)),
|
|
453
482
|
[]
|
|
@@ -517,58 +546,78 @@ function PlainTextLayer({ layer, viewport, blockTime, textScale = 1 }) {
|
|
|
517
546
|
...animStyle.style
|
|
518
547
|
};
|
|
519
548
|
const filterId = style.shadow ? `shadow-${defsId}` : void 0;
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
549
|
+
let wordIndex = 0;
|
|
550
|
+
const renderLine = (line) => {
|
|
551
|
+
if (!isWordReveal || !animation || !line) return line || "\xA0";
|
|
552
|
+
const words = line.split(" ");
|
|
553
|
+
return words.map((word, w) => {
|
|
554
|
+
const delay = wordRevealDelay(animation, wordIndex++);
|
|
555
|
+
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
556
|
+
w > 0 ? " " : null,
|
|
557
|
+
/* @__PURE__ */ jsx3("tspan", { className: "anim-word", style: { "--anim-delay": `${delay}s` }, children: word })
|
|
558
|
+
] }, w);
|
|
559
|
+
});
|
|
560
|
+
};
|
|
561
|
+
return /* @__PURE__ */ jsxs2(
|
|
562
|
+
"g",
|
|
563
|
+
{
|
|
564
|
+
className: `block-layer block-layer--text ${animStyle.className}`,
|
|
565
|
+
style: animStyle.style,
|
|
566
|
+
"data-layer-id": layer.id,
|
|
567
|
+
children: [
|
|
568
|
+
style.shadow && /* @__PURE__ */ jsx3("defs", { children: /* @__PURE__ */ jsx3("filter", { id: filterId, x: "-20%", y: "-20%", width: "140%", height: "140%", children: /* @__PURE__ */ jsx3("feDropShadow", { dx: "0", dy: "2", stdDeviation: "3", floodColor: "rgba(0,0,0,0.7)" }) }) }),
|
|
569
|
+
/* @__PURE__ */ jsx3(
|
|
570
|
+
TextBox,
|
|
571
|
+
{
|
|
572
|
+
layerId: defsId,
|
|
573
|
+
style,
|
|
574
|
+
box: boxWidth != null && boxHeight != null ? {
|
|
575
|
+
x: rawX - anchorAxis(anchor, boxWidth, "x"),
|
|
576
|
+
y: rawY - anchorAxis(anchor, boxHeight, "y"),
|
|
577
|
+
width: boxWidth,
|
|
578
|
+
height: boxHeight
|
|
579
|
+
} : {
|
|
580
|
+
x: x - (style.padding || 16),
|
|
581
|
+
y: y - style.fontSize - (style.padding || 16),
|
|
582
|
+
width: getTextBoxWidth(lines, style) + (style.padding || 16) * 2,
|
|
583
|
+
height: lines.length * lineHeightPx + (style.padding || 16) * 2
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
),
|
|
587
|
+
/* @__PURE__ */ jsx3(
|
|
588
|
+
"g",
|
|
553
589
|
{
|
|
554
|
-
|
|
555
|
-
"data-squisq-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
590
|
+
"data-squisq-text-fit": "svg",
|
|
591
|
+
"data-squisq-fit-width": fitWidth,
|
|
592
|
+
"data-squisq-fit-height": fitHeight,
|
|
593
|
+
style: {
|
|
594
|
+
transform: `scale(${textScale})`,
|
|
595
|
+
transformOrigin: `${x}px ${y}px`,
|
|
596
|
+
transformBox: "view-box"
|
|
597
|
+
},
|
|
598
|
+
children: /* @__PURE__ */ jsx3(
|
|
599
|
+
"text",
|
|
600
|
+
{
|
|
601
|
+
ref: textRef,
|
|
602
|
+
"data-squisq-text-content": "true",
|
|
603
|
+
x,
|
|
604
|
+
y,
|
|
605
|
+
textAnchor,
|
|
606
|
+
dominantBaseline,
|
|
607
|
+
style: textStyles,
|
|
608
|
+
filter: filterId ? `url(#${filterId})` : void 0,
|
|
609
|
+
transform: fitTransform,
|
|
610
|
+
children: lines.map((line, i) => /* @__PURE__ */ jsxs2("tspan", { x, dy: i === 0 ? firstLineDy : lineHeightPx, children: [
|
|
611
|
+
renderLine(line),
|
|
612
|
+
" "
|
|
613
|
+
] }, i))
|
|
614
|
+
}
|
|
615
|
+
)
|
|
567
616
|
}
|
|
568
617
|
)
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
618
|
+
]
|
|
619
|
+
}
|
|
620
|
+
);
|
|
572
621
|
}
|
|
573
622
|
function RichTextLayer({ layer, viewport, blockTime, textScale = 1 }) {
|
|
574
623
|
const { content, position, animation } = layer;
|
|
@@ -618,48 +667,56 @@ function RichTextLayer({ layer, viewport, blockTime, textScale = 1 }) {
|
|
|
618
667
|
if (target?.closest?.("[data-squisq-table-scroll]")) event.stopPropagation();
|
|
619
668
|
};
|
|
620
669
|
const scopedCss = `.${cls}{margin:0}.${cls} p{margin:0 0 .4em}.${cls} h1,.${cls} h2,.${cls} h3,.${cls} h4,.${cls} h5,.${cls} h6{margin:0 0 .3em;line-height:1.2}.${cls} ul,.${cls} ol{margin:0 0 .7em;padding-left:1.2em;list-style-position:${listStylePosition}}.${cls} li{margin:0}.${cls} li>p{display:inline;margin:0}.${cls} [data-squisq-table-scroll]{width:100%;max-height:20em;margin:.35em 0;overflow:auto;overscroll-behavior:contain;box-sizing:border-box}.${cls} table{width:100%;min-width:48em;margin:0;border-collapse:separate;border-spacing:0;table-layout:fixed;font-size:.76em;line-height:1.2}.${cls} th,.${cls} td{box-sizing:border-box;padding:.28em .45em;border:1px solid rgba(127,127,127,.55);border-color:color-mix(in srgb,currentColor 32%,transparent);vertical-align:top;white-space:normal;overflow-wrap:anywhere}.${cls} th{position:sticky;top:0;z-index:1;background:rgba(127,127,127,.16);background:color-mix(in srgb,currentColor 12%,transparent);font-weight:600}.${cls} tbody tr:nth-child(even){background:rgba(127,127,127,.07);background:color-mix(in srgb,currentColor 5%,transparent)}.${cls} *:first-child{margin-top:0}.${cls} *:last-child{margin-bottom:0}.${cls} a{color:inherit;text-decoration:underline}`;
|
|
621
|
-
return /* @__PURE__ */ jsx3(
|
|
622
|
-
"
|
|
670
|
+
return /* @__PURE__ */ jsx3(
|
|
671
|
+
"g",
|
|
623
672
|
{
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
style: { color: style.color },
|
|
630
|
-
children: /* @__PURE__ */ jsxs2(
|
|
631
|
-
"div",
|
|
673
|
+
className: `block-layer block-layer--text ${animStyle.className}`,
|
|
674
|
+
style: animStyle.style,
|
|
675
|
+
"data-layer-id": layer.id,
|
|
676
|
+
children: /* @__PURE__ */ jsx3(
|
|
677
|
+
"foreignObject",
|
|
632
678
|
{
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
"
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
679
|
+
x: boxX,
|
|
680
|
+
y: boxY,
|
|
681
|
+
width: boxWidth,
|
|
682
|
+
height: boxHeight,
|
|
683
|
+
color: style.color,
|
|
684
|
+
style: { color: style.color },
|
|
685
|
+
children: /* @__PURE__ */ jsxs2(
|
|
686
|
+
"div",
|
|
687
|
+
{
|
|
688
|
+
...{ xmlns: "http://www.w3.org/1999/xhtml" },
|
|
689
|
+
"data-squisq-text-fit": "html",
|
|
690
|
+
"data-squisq-base-font-size": style.fontSize,
|
|
691
|
+
style: boxStyle,
|
|
692
|
+
onPointerDown: stopEmbeddedTableEvent,
|
|
693
|
+
onClick: stopEmbeddedTableEvent,
|
|
694
|
+
onWheel: stopEmbeddedTableEvent,
|
|
695
|
+
onKeyDown: stopEmbeddedTableEvent,
|
|
696
|
+
children: [
|
|
697
|
+
/* @__PURE__ */ jsx3("style", { children: scopedCss }),
|
|
698
|
+
/* @__PURE__ */ jsx3(
|
|
699
|
+
"div",
|
|
700
|
+
{
|
|
701
|
+
"data-squisq-text-content": "true",
|
|
702
|
+
className: cls,
|
|
703
|
+
"aria-label": content.text,
|
|
704
|
+
style: {
|
|
705
|
+
width: "100%",
|
|
706
|
+
color: style.color,
|
|
707
|
+
whiteSpace: "pre-wrap",
|
|
708
|
+
wordBreak: "break-word"
|
|
709
|
+
},
|
|
710
|
+
dangerouslySetInnerHTML: { __html: safeHtml }
|
|
711
|
+
}
|
|
712
|
+
)
|
|
713
|
+
]
|
|
714
|
+
}
|
|
715
|
+
)
|
|
659
716
|
}
|
|
660
717
|
)
|
|
661
718
|
}
|
|
662
|
-
)
|
|
719
|
+
);
|
|
663
720
|
}
|
|
664
721
|
function cssId(id) {
|
|
665
722
|
return id.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
@@ -674,7 +731,7 @@ function TextBox({
|
|
|
674
731
|
if (!hasFill && !hasBorder) return null;
|
|
675
732
|
const { fill, def } = resolveFill(layerId, style.background, style.backgroundGradient);
|
|
676
733
|
const dash = borderDashArray(style.borderStyle, style.borderWidth);
|
|
677
|
-
return /* @__PURE__ */ jsxs2(
|
|
734
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
678
735
|
def && /* @__PURE__ */ jsx3("defs", { children: def }),
|
|
679
736
|
/* @__PURE__ */ jsx3(
|
|
680
737
|
"rect",
|
|
@@ -773,7 +830,7 @@ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
|
773
830
|
var FULL_BLEED_OVERSCAN = 1;
|
|
774
831
|
function ShapeLayer({ layer, viewport, blockTime }) {
|
|
775
832
|
const { content, position, animation } = layer;
|
|
776
|
-
const defsId =
|
|
833
|
+
const defsId = defsIdFor(useId2(), layer.id);
|
|
777
834
|
const rawX = resolveValue(position.x, viewport.width);
|
|
778
835
|
const rawY = resolveValue(position.y, viewport.height);
|
|
779
836
|
const width = position.width ? resolveValue(position.width, viewport.width) : 100;
|
|
@@ -913,7 +970,7 @@ function effectiveMarker(explicit, legacyArrow, end) {
|
|
|
913
970
|
}
|
|
914
971
|
function PathLayer({ layer, viewport, blockTime }) {
|
|
915
972
|
const { content, animation, id } = layer;
|
|
916
|
-
const defsId =
|
|
973
|
+
const defsId = defsIdFor(useId3(), id);
|
|
917
974
|
const d = effectivePath(layer, viewport);
|
|
918
975
|
const stroke = content.stroke ?? "#1e293b";
|
|
919
976
|
const strokeWidth = content.strokeWidth ?? 2;
|
|
@@ -925,6 +982,9 @@ function PathLayer({ layer, viewport, blockTime }) {
|
|
|
925
982
|
const legacyArrow = readLegacyArrow(content);
|
|
926
983
|
const start = markerPath(effectiveMarker(content.startMarker, legacyArrow, "start"), "start");
|
|
927
984
|
const end = markerPath(effectiveMarker(content.endMarker, legacyArrow, "end"), "end");
|
|
985
|
+
const markerStart = start ? `url(#${startId})` : void 0;
|
|
986
|
+
const markerEnd = end ? `url(#${endId})` : void 0;
|
|
987
|
+
const drawOn = animation?.type === "drawOn";
|
|
928
988
|
return /* @__PURE__ */ jsxs4(
|
|
929
989
|
"g",
|
|
930
990
|
{
|
|
@@ -945,9 +1005,24 @@ function PathLayer({ layer, viewport, blockTime }) {
|
|
|
945
1005
|
strokeWidth,
|
|
946
1006
|
fill,
|
|
947
1007
|
fillOpacity: content.fillOpacity,
|
|
948
|
-
strokeDasharray: dash,
|
|
949
|
-
|
|
950
|
-
|
|
1008
|
+
strokeDasharray: drawOn ? void 0 : dash,
|
|
1009
|
+
pathLength: drawOn ? 1 : void 0,
|
|
1010
|
+
"data-squisq-draw": drawOn ? "stroke" : void 0,
|
|
1011
|
+
markerStart: drawOn ? void 0 : markerStart,
|
|
1012
|
+
markerEnd: drawOn ? void 0 : markerEnd
|
|
1013
|
+
}
|
|
1014
|
+
),
|
|
1015
|
+
drawOn && (markerStart || markerEnd) && /* @__PURE__ */ jsx5(
|
|
1016
|
+
"path",
|
|
1017
|
+
{
|
|
1018
|
+
d,
|
|
1019
|
+
stroke,
|
|
1020
|
+
strokeWidth,
|
|
1021
|
+
strokeOpacity: 0,
|
|
1022
|
+
fill: "none",
|
|
1023
|
+
"data-squisq-draw": "markers",
|
|
1024
|
+
markerStart,
|
|
1025
|
+
markerEnd
|
|
951
1026
|
}
|
|
952
1027
|
)
|
|
953
1028
|
]
|
|
@@ -1185,8 +1260,11 @@ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
|
1185
1260
|
function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
1186
1261
|
const { content, position, animation } = layer;
|
|
1187
1262
|
const clipId = `map-clip-${useId4().replace(/:/g, "")}-${layer.id}`;
|
|
1188
|
-
const
|
|
1189
|
-
const [
|
|
1263
|
+
const staticSrc = useMediaUrl(content.staticSrc ?? "", basePath);
|
|
1264
|
+
const [mapImageUrl, setMapImageUrl] = useState2(
|
|
1265
|
+
() => content.staticSrc ? staticSrc || null : null
|
|
1266
|
+
);
|
|
1267
|
+
const [isLoading, setIsLoading] = useState2(!content.staticSrc);
|
|
1190
1268
|
const [error, setError] = useState2(null);
|
|
1191
1269
|
const [blockedByPolicy, setBlockedByPolicy] = useState2(false);
|
|
1192
1270
|
const x = resolveValue(position.x, viewport.width);
|
|
@@ -1196,7 +1274,6 @@ function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
1196
1274
|
const offset = getAnchorOffset(position.anchor, width, height);
|
|
1197
1275
|
const finalX = x + offset.x;
|
|
1198
1276
|
const finalY = y + offset.y;
|
|
1199
|
-
const staticSrc = useMediaUrl(content.staticSrc ?? "", basePath);
|
|
1200
1277
|
const resourcePolicy = useResourcePolicy();
|
|
1201
1278
|
useEffect(() => {
|
|
1202
1279
|
let cancelled = false;
|