@bendyline/squisq-react 2.11.2 → 2.11.4
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/TableLayer-B7ADuUGc.d.ts +31 -0
- package/dist/{chunk-ZC7LI5UF.js → chunk-5UGIE2QO.js} +156 -9
- package/dist/{chunk-HK7TY64K.js → chunk-KGWT454C.js} +12 -5
- package/dist/{chunk-4MP7CH3C.js → chunk-XSOMFBH2.js} +213 -137
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -3
- package/dist/layers/index.d.ts +6 -14
- package/dist/layers/index.js +1 -1
- package/dist/page/index.js +2 -2
- package/dist/player/index.d.ts +2 -1
- package/dist/player/index.js +3 -3
- package/dist/{player-DSMhm8SL.d.ts → player-BlD8zREs.d.ts} +8 -1
- package/dist/squisq-player.full.global.js +419 -416
- package/dist/squisq-player.global.js +67 -64
- package/dist/standalone-source.js +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ComponentType, ReactNode } from 'react';
|
|
3
|
+
import { Block, TableLayer as TableLayer$1 } from '@bendyline/squisq/schemas';
|
|
4
|
+
|
|
5
|
+
interface TableLayerContentRendererProps {
|
|
6
|
+
/** Owning block, including sourceBlockId for transformed projections. */
|
|
7
|
+
block: Block;
|
|
8
|
+
layer: TableLayer$1;
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
/** Native themed table used while custom data is unavailable. */
|
|
12
|
+
fallback: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
type TableLayerContentRenderer = ComponentType<TableLayerContentRendererProps>;
|
|
15
|
+
interface TableLayerProps {
|
|
16
|
+
/** Owning block (custom renderers use its id to resolve sidecar data). */
|
|
17
|
+
block?: Block;
|
|
18
|
+
layer: TableLayer$1;
|
|
19
|
+
/** Viewport dimensions for percentage calculations */
|
|
20
|
+
viewport: {
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
};
|
|
24
|
+
/** Current time relative to block start (for animation) */
|
|
25
|
+
blockTime: number;
|
|
26
|
+
/** Optional host renderer for interactive table content. */
|
|
27
|
+
contentRenderer?: TableLayerContentRenderer;
|
|
28
|
+
}
|
|
29
|
+
declare function TableLayer({ block, layer, viewport, blockTime, contentRenderer, }: TableLayerProps): react_jsx_runtime.JSX.Element;
|
|
30
|
+
|
|
31
|
+
export { TableLayer as T, type TableLayerContentRenderer as a, type TableLayerContentRendererProps as b };
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
TreeLayer,
|
|
10
10
|
VideoLayer,
|
|
11
11
|
getTransitionClass
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-XSOMFBH2.js";
|
|
13
13
|
import {
|
|
14
14
|
useAutoSurface
|
|
15
15
|
} from "./chunk-TT6ENR6T.js";
|
|
@@ -22,13 +22,93 @@ import {
|
|
|
22
22
|
} from "./chunk-LR3AIGDD.js";
|
|
23
23
|
|
|
24
24
|
// src/BlockRenderer.tsx
|
|
25
|
-
import { useId } from "react";
|
|
25
|
+
import { useId, useLayoutEffect, useRef, useState } from "react";
|
|
26
26
|
import { resolveTransitionDuration } from "@bendyline/squisq/schemas";
|
|
27
|
+
|
|
28
|
+
// src/utils/textFit.ts
|
|
29
|
+
function largestFittingTextScale(fits, maximum = 2) {
|
|
30
|
+
if (maximum <= 1 || !fits(1)) return 1;
|
|
31
|
+
if (fits(maximum)) return maximum;
|
|
32
|
+
let low = 1;
|
|
33
|
+
let high = maximum;
|
|
34
|
+
for (let attempt = 0; attempt < 10; attempt += 1) {
|
|
35
|
+
const candidate = (low + high) / 2;
|
|
36
|
+
if (fits(candidate)) low = candidate;
|
|
37
|
+
else high = candidate;
|
|
38
|
+
}
|
|
39
|
+
return Math.max(1, Math.floor(low * 100) / 100);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/BlockRenderer.tsx
|
|
27
43
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
28
44
|
var DEFAULT_VIEWPORT = {
|
|
29
45
|
width: 1920,
|
|
30
46
|
height: 1080
|
|
31
47
|
};
|
|
48
|
+
var FIT_TOLERANCE_PX = 1;
|
|
49
|
+
function applyTextScale(targets, scale) {
|
|
50
|
+
for (const target of targets) {
|
|
51
|
+
if (target.dataset.squisqTextFit === "html") {
|
|
52
|
+
const baseFontSize = Number(target.dataset.squisqBaseFontSize);
|
|
53
|
+
if (Number.isFinite(baseFontSize)) target.style.fontSize = `${baseFontSize * scale}px`;
|
|
54
|
+
} else {
|
|
55
|
+
target.style.transform = `scale(${scale})`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function cssPixels(value) {
|
|
60
|
+
const parsed = parseFloat(value);
|
|
61
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
62
|
+
}
|
|
63
|
+
function htmlTargetFits(target) {
|
|
64
|
+
const content = target.querySelector('[data-squisq-text-content="true"]');
|
|
65
|
+
if (!content || content.scrollWidth <= 0 || content.scrollHeight <= 0) return false;
|
|
66
|
+
const view = target.ownerDocument.defaultView;
|
|
67
|
+
const computed = view?.getComputedStyle(target);
|
|
68
|
+
const horizontalInset = computed ? cssPixels(computed.paddingLeft) + cssPixels(computed.paddingRight) + cssPixels(computed.borderLeftWidth) + cssPixels(computed.borderRightWidth) : 0;
|
|
69
|
+
const verticalInset = computed ? cssPixels(computed.paddingTop) + cssPixels(computed.paddingBottom) + cssPixels(computed.borderTopWidth) + cssPixels(computed.borderBottomWidth) : 0;
|
|
70
|
+
const availableWidth = Math.max(0, target.clientWidth - horizontalInset);
|
|
71
|
+
const availableHeight = Math.max(0, target.clientHeight - verticalInset);
|
|
72
|
+
if (content.scrollWidth > availableWidth + FIT_TOLERANCE_PX || content.scrollHeight > availableHeight + FIT_TOLERANCE_PX) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
for (const scrollRegion of content.querySelectorAll("[data-squisq-table-scroll]")) {
|
|
76
|
+
if (scrollRegion.clientWidth > 0 && scrollRegion.scrollWidth > scrollRegion.clientWidth + FIT_TOLERANCE_PX || scrollRegion.clientHeight > 0 && scrollRegion.scrollHeight > scrollRegion.clientHeight + FIT_TOLERANCE_PX) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
function svgTargetFits(target, scale) {
|
|
83
|
+
const content = target.querySelector('[data-squisq-text-content="true"]');
|
|
84
|
+
if (!content || typeof content.getBBox !== "function") return false;
|
|
85
|
+
try {
|
|
86
|
+
const box = content.getBBox();
|
|
87
|
+
const availableWidth = Number(target.dataset.squisqFitWidth);
|
|
88
|
+
const availableHeight = Number(target.dataset.squisqFitHeight);
|
|
89
|
+
return box.width > 0 && box.height > 0 && box.width * scale <= availableWidth + FIT_TOLERANCE_PX && box.height * scale <= availableHeight + FIT_TOLERANCE_PX;
|
|
90
|
+
} catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function renderedTextRects(targets) {
|
|
95
|
+
return targets.map(
|
|
96
|
+
(target) => target.querySelector('[data-squisq-text-content="true"]')?.getBoundingClientRect()
|
|
97
|
+
).filter((rect) => !!rect && rect.width > 0 && rect.height > 0);
|
|
98
|
+
}
|
|
99
|
+
function overlapPairs(rects) {
|
|
100
|
+
const pairs = /* @__PURE__ */ new Set();
|
|
101
|
+
for (let left = 0; left < rects.length; left += 1) {
|
|
102
|
+
for (let right = left + 1; right < rects.length; right += 1) {
|
|
103
|
+
const a = rects[left];
|
|
104
|
+
const b = rects[right];
|
|
105
|
+
if (a.left < b.right - FIT_TOLERANCE_PX && a.right > b.left + FIT_TOLERANCE_PX && a.top < b.bottom - FIT_TOLERANCE_PX && a.bottom > b.top + FIT_TOLERANCE_PX) {
|
|
106
|
+
pairs.add(`${left}:${right}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return pairs;
|
|
111
|
+
}
|
|
32
112
|
function BlockRenderer({
|
|
33
113
|
block,
|
|
34
114
|
blockTime,
|
|
@@ -40,7 +120,9 @@ function BlockRenderer({
|
|
|
40
120
|
isPlaying,
|
|
41
121
|
muted = false,
|
|
42
122
|
animationsEnabled = true,
|
|
43
|
-
theme
|
|
123
|
+
theme,
|
|
124
|
+
tableContentRenderer,
|
|
125
|
+
growTextToFit = false
|
|
44
126
|
}) {
|
|
45
127
|
let transitionClass = "";
|
|
46
128
|
const transitionStyle = {};
|
|
@@ -54,20 +136,63 @@ function BlockRenderer({
|
|
|
54
136
|
}
|
|
55
137
|
const instanceId = useId().replace(/:/g, "");
|
|
56
138
|
const clipId = `vb-clip-${instanceId}-${block.id}`;
|
|
139
|
+
const svgRef = useRef(null);
|
|
140
|
+
const [textScale, setTextScale] = useState(1);
|
|
141
|
+
useLayoutEffect(() => {
|
|
142
|
+
const svg = svgRef.current;
|
|
143
|
+
let cancelled = false;
|
|
144
|
+
if (!svg) return;
|
|
145
|
+
const measure = () => {
|
|
146
|
+
if (cancelled) return;
|
|
147
|
+
const targets = Array.from(svg.querySelectorAll("[data-squisq-text-fit]"));
|
|
148
|
+
if (!growTextToFit || targets.length === 0) {
|
|
149
|
+
applyTextScale(targets, 1);
|
|
150
|
+
setTextScale(1);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
applyTextScale(targets, 1);
|
|
154
|
+
const baselineOverlaps = overlapPairs(renderedTextRects(targets));
|
|
155
|
+
const fits = (scale) => {
|
|
156
|
+
applyTextScale(targets, scale);
|
|
157
|
+
if (targets.some(
|
|
158
|
+
(target) => target.dataset.squisqTextFit === "html" ? !htmlTargetFits(target) : !svgTargetFits(target, scale)
|
|
159
|
+
)) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
const candidateOverlaps = overlapPairs(renderedTextRects(targets));
|
|
163
|
+
for (const pair of candidateOverlaps) {
|
|
164
|
+
if (!baselineOverlaps.has(pair)) return false;
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
};
|
|
168
|
+
const nextScale = largestFittingTextScale(fits);
|
|
169
|
+
applyTextScale(targets, nextScale);
|
|
170
|
+
setTextScale((current) => current === nextScale ? current : nextScale);
|
|
171
|
+
};
|
|
172
|
+
measure();
|
|
173
|
+
const fonts = svg.ownerDocument?.fonts;
|
|
174
|
+
if (fonts && fonts.status !== "loaded") void fonts.ready.then(measure);
|
|
175
|
+
return () => {
|
|
176
|
+
cancelled = true;
|
|
177
|
+
};
|
|
178
|
+
}, [block.layers, growTextToFit, viewport.height, viewport.width]);
|
|
57
179
|
return /* @__PURE__ */ jsxs(
|
|
58
180
|
"svg",
|
|
59
181
|
{
|
|
182
|
+
ref: svgRef,
|
|
60
183
|
className: `block-svg ${transitionClass}`,
|
|
61
184
|
style: transitionStyle,
|
|
62
185
|
viewBox: `0 0 ${viewport.width} ${viewport.height}`,
|
|
63
186
|
preserveAspectRatio: "xMidYMid meet",
|
|
64
187
|
overflow: "hidden",
|
|
65
188
|
"data-block-id": block.id,
|
|
189
|
+
"data-squisq-text-scale": growTextToFit ? textScale : void 0,
|
|
66
190
|
children: [
|
|
67
191
|
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("clipPath", { id: clipId, children: /* @__PURE__ */ jsx("rect", { x: "0", y: "0", width: viewport.width, height: viewport.height }) }) }),
|
|
68
192
|
/* @__PURE__ */ jsx("g", { clipPath: `url(#${clipId})`, children: (block.layers ?? []).map((layer) => /* @__PURE__ */ jsx(
|
|
69
193
|
LayerRenderer,
|
|
70
194
|
{
|
|
195
|
+
block,
|
|
71
196
|
layer,
|
|
72
197
|
basePath,
|
|
73
198
|
viewport,
|
|
@@ -75,7 +200,9 @@ function BlockRenderer({
|
|
|
75
200
|
isPlaying,
|
|
76
201
|
muted,
|
|
77
202
|
animationsEnabled,
|
|
78
|
-
theme
|
|
203
|
+
theme,
|
|
204
|
+
tableContentRenderer,
|
|
205
|
+
textScale
|
|
79
206
|
},
|
|
80
207
|
layer.id
|
|
81
208
|
)) })
|
|
@@ -84,6 +211,7 @@ function BlockRenderer({
|
|
|
84
211
|
);
|
|
85
212
|
}
|
|
86
213
|
function LayerRenderer({
|
|
214
|
+
block,
|
|
87
215
|
layer,
|
|
88
216
|
basePath,
|
|
89
217
|
viewport,
|
|
@@ -91,7 +219,9 @@ function LayerRenderer({
|
|
|
91
219
|
isPlaying,
|
|
92
220
|
muted,
|
|
93
221
|
animationsEnabled,
|
|
94
|
-
theme
|
|
222
|
+
theme,
|
|
223
|
+
tableContentRenderer,
|
|
224
|
+
textScale
|
|
95
225
|
}) {
|
|
96
226
|
const renderedLayer = animationsEnabled || !layer.animation ? layer : { ...layer, animation: void 0 };
|
|
97
227
|
switch (renderedLayer.type) {
|
|
@@ -107,7 +237,15 @@ function LayerRenderer({
|
|
|
107
237
|
}
|
|
108
238
|
);
|
|
109
239
|
case "text":
|
|
110
|
-
return /* @__PURE__ */ jsx(
|
|
240
|
+
return /* @__PURE__ */ jsx(
|
|
241
|
+
TextLayer,
|
|
242
|
+
{
|
|
243
|
+
layer: renderedLayer,
|
|
244
|
+
viewport,
|
|
245
|
+
blockTime,
|
|
246
|
+
textScale
|
|
247
|
+
}
|
|
248
|
+
);
|
|
111
249
|
case "shape":
|
|
112
250
|
return /* @__PURE__ */ jsx(ShapeLayer, { layer: renderedLayer, viewport, blockTime });
|
|
113
251
|
case "path":
|
|
@@ -135,7 +273,16 @@ function LayerRenderer({
|
|
|
135
273
|
}
|
|
136
274
|
);
|
|
137
275
|
case "table":
|
|
138
|
-
return /* @__PURE__ */ jsx(
|
|
276
|
+
return /* @__PURE__ */ jsx(
|
|
277
|
+
TableLayer,
|
|
278
|
+
{
|
|
279
|
+
block,
|
|
280
|
+
layer: renderedLayer,
|
|
281
|
+
viewport,
|
|
282
|
+
blockTime,
|
|
283
|
+
contentRenderer: tableContentRenderer
|
|
284
|
+
}
|
|
285
|
+
);
|
|
139
286
|
case "tree":
|
|
140
287
|
return /* @__PURE__ */ jsx(TreeLayer, { layer: renderedLayer, viewport, blockTime });
|
|
141
288
|
case "mermaid":
|
|
@@ -625,7 +772,7 @@ function PageSectionView({ entry, featureFlip, isLeadProse }) {
|
|
|
625
772
|
}
|
|
626
773
|
|
|
627
774
|
// src/LinearDocView.tsx
|
|
628
|
-
import { useEffect, useMemo as useMemo2, useRef } from "react";
|
|
775
|
+
import { useEffect, useMemo as useMemo2, useRef as useRef2 } from "react";
|
|
629
776
|
import {
|
|
630
777
|
applySurface,
|
|
631
778
|
resolveFontFamily
|
|
@@ -663,7 +810,7 @@ function LinearDocView({
|
|
|
663
810
|
htmlPolicy,
|
|
664
811
|
linkSchemes
|
|
665
812
|
}) {
|
|
666
|
-
const scrollRef =
|
|
813
|
+
const scrollRef = useRef2(null);
|
|
667
814
|
const activeViewport = viewport ?? VIEWPORT_PRESETS2.landscape;
|
|
668
815
|
const markdownDoc = useMemo2(
|
|
669
816
|
() => !doc && markdown !== void 0 ? markdownToDoc(parseMarkdown(markdown)) : void 0,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BlockRenderer,
|
|
3
3
|
LinearDocView
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-5UGIE2QO.js";
|
|
5
5
|
import {
|
|
6
6
|
useAudioSync,
|
|
7
7
|
useDocPlayback,
|
|
@@ -2693,7 +2693,8 @@ function DocPlayerContent({
|
|
|
2693
2693
|
globalKeyboardShortcuts = false,
|
|
2694
2694
|
showCodeCopyButton = false,
|
|
2695
2695
|
onCopyCode,
|
|
2696
|
-
fenceRenderers
|
|
2696
|
+
fenceRenderers,
|
|
2697
|
+
tableContentRenderer
|
|
2697
2698
|
}) {
|
|
2698
2699
|
const isSlideshowMode = displayMode === "slideshow";
|
|
2699
2700
|
const isLinearMode = displayMode === "linear";
|
|
@@ -3600,7 +3601,9 @@ function DocPlayerContent({
|
|
|
3600
3601
|
viewport: activeViewport,
|
|
3601
3602
|
muted: renderMode || muted,
|
|
3602
3603
|
animationsEnabled,
|
|
3603
|
-
theme: effectiveTheme
|
|
3604
|
+
theme: effectiveTheme,
|
|
3605
|
+
tableContentRenderer,
|
|
3606
|
+
growTextToFit: isSlideshowMode
|
|
3604
3607
|
}
|
|
3605
3608
|
) }),
|
|
3606
3609
|
animationsEnabled && !showCoverBlock && previousBlock && isExiting && // Keyed by block id so each block is its own DOM subtree: React never
|
|
@@ -3618,7 +3621,9 @@ function DocPlayerContent({
|
|
|
3618
3621
|
viewport: activeViewport,
|
|
3619
3622
|
muted: renderMode || muted,
|
|
3620
3623
|
animationsEnabled,
|
|
3621
|
-
theme: effectiveTheme
|
|
3624
|
+
theme: effectiveTheme,
|
|
3625
|
+
tableContentRenderer,
|
|
3626
|
+
growTextToFit: isSlideshowMode
|
|
3622
3627
|
}
|
|
3623
3628
|
) }, previousBlock.id),
|
|
3624
3629
|
currentBlock && /* @__PURE__ */ jsx9(
|
|
@@ -3641,7 +3646,9 @@ function DocPlayerContent({
|
|
|
3641
3646
|
isPlaying,
|
|
3642
3647
|
muted: renderMode || muted,
|
|
3643
3648
|
animationsEnabled,
|
|
3644
|
-
theme: effectiveTheme
|
|
3649
|
+
theme: effectiveTheme,
|
|
3650
|
+
tableContentRenderer,
|
|
3651
|
+
growTextToFit: isSlideshowMode
|
|
3645
3652
|
}
|
|
3646
3653
|
)
|
|
3647
3654
|
},
|