@bendyline/squisq-react 0.1.3 → 1.0.1
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/BlockRenderer.d.ts +37 -0
- package/dist/BlockRenderer.d.ts.map +1 -0
- package/dist/BlockRenderer.js +51 -0
- package/dist/BlockRenderer.js.map +1 -0
- package/dist/CaptionOverlay.d.ts +24 -0
- package/dist/CaptionOverlay.d.ts.map +1 -0
- package/dist/CaptionOverlay.js +40 -0
- package/dist/CaptionOverlay.js.map +1 -0
- package/dist/DocControlsBottom.d.ts +28 -0
- package/dist/DocControlsBottom.d.ts.map +1 -0
- package/dist/DocControlsBottom.js +23 -0
- package/dist/DocControlsBottom.js.map +1 -0
- package/dist/DocControlsOverlay.d.ts +28 -0
- package/dist/DocControlsOverlay.d.ts.map +1 -0
- package/dist/DocControlsOverlay.js +75 -0
- package/dist/DocControlsOverlay.js.map +1 -0
- package/dist/DocControlsSidebar.d.ts +27 -0
- package/dist/DocControlsSidebar.d.ts.map +1 -0
- package/dist/DocControlsSidebar.js +6 -0
- package/dist/DocControlsSidebar.js.map +1 -0
- package/dist/DocControlsSlideshow.d.ts +20 -0
- package/dist/DocControlsSlideshow.d.ts.map +1 -0
- package/dist/DocControlsSlideshow.js +69 -0
- package/dist/DocControlsSlideshow.js.map +1 -0
- package/dist/DocPlayer.d.ts +87 -0
- package/dist/DocPlayer.d.ts.map +1 -0
- package/dist/DocPlayer.js +604 -0
- package/dist/DocPlayer.js.map +1 -0
- package/dist/DocPlayerWithSidebar.d.ts +41 -0
- package/dist/DocPlayerWithSidebar.d.ts.map +1 -0
- package/dist/DocPlayerWithSidebar.js +68 -0
- package/dist/DocPlayerWithSidebar.js.map +1 -0
- package/dist/DocProgressBar.d.ts +31 -0
- package/dist/DocProgressBar.d.ts.map +1 -0
- package/dist/DocProgressBar.js +122 -0
- package/dist/DocProgressBar.js.map +1 -0
- package/dist/LinearDocView.d.ts +47 -0
- package/dist/LinearDocView.d.ts.map +1 -0
- package/dist/LinearDocView.js +184 -0
- package/dist/LinearDocView.js.map +1 -0
- package/dist/MarkdownRenderer.d.ts +32 -0
- package/dist/MarkdownRenderer.d.ts.map +1 -0
- package/dist/MarkdownRenderer.js +133 -0
- package/dist/MarkdownRenderer.js.map +1 -0
- package/dist/__tests__/BlockRenderer.test.d.ts +2 -0
- package/dist/__tests__/BlockRenderer.test.d.ts.map +1 -0
- package/dist/__tests__/BlockRenderer.test.js +82 -0
- package/dist/__tests__/BlockRenderer.test.js.map +1 -0
- package/dist/__tests__/DocControlsSlideshow.test.d.ts +2 -0
- package/dist/__tests__/DocControlsSlideshow.test.d.ts.map +1 -0
- package/dist/__tests__/DocControlsSlideshow.test.js +88 -0
- package/dist/__tests__/DocControlsSlideshow.test.js.map +1 -0
- package/dist/__tests__/LinearDocView.test.d.ts +2 -0
- package/dist/__tests__/LinearDocView.test.d.ts.map +1 -0
- package/dist/__tests__/LinearDocView.test.js +160 -0
- package/dist/__tests__/LinearDocView.test.js.map +1 -0
- package/dist/__tests__/MarkdownRenderer.test.d.ts +2 -0
- package/dist/__tests__/MarkdownRenderer.test.d.ts.map +1 -0
- package/dist/__tests__/MarkdownRenderer.test.js +208 -0
- package/dist/__tests__/MarkdownRenderer.test.js.map +1 -0
- package/dist/__tests__/exports.test.d.ts +2 -0
- package/dist/__tests__/exports.test.d.ts.map +1 -0
- package/dist/__tests__/exports.test.js +48 -0
- package/dist/__tests__/exports.test.js.map +1 -0
- package/dist/hooks/AudioProvider.d.ts +71 -0
- package/dist/hooks/AudioProvider.d.ts.map +1 -0
- package/dist/hooks/AudioProvider.js +52 -0
- package/dist/hooks/AudioProvider.js.map +1 -0
- package/dist/hooks/MediaContext.d.ts +35 -0
- package/dist/hooks/MediaContext.d.ts.map +1 -0
- package/dist/hooks/MediaContext.js +66 -0
- package/dist/hooks/MediaContext.js.map +1 -0
- package/dist/hooks/index.d.ts +6 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +5 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/useAudioSync.d.ts +18 -0
- package/dist/hooks/useAudioSync.d.ts.map +1 -0
- package/dist/hooks/useAudioSync.js +338 -0
- package/dist/hooks/useAudioSync.js.map +1 -0
- package/dist/hooks/useDocPlayback.d.ts +47 -0
- package/dist/hooks/useDocPlayback.d.ts.map +1 -0
- package/dist/hooks/useDocPlayback.js +176 -0
- package/dist/hooks/useDocPlayback.js.map +1 -0
- package/dist/hooks/useViewportOrientation.d.ts +31 -0
- package/dist/hooks/useViewportOrientation.d.ts.map +1 -0
- package/dist/hooks/useViewportOrientation.js +92 -0
- package/dist/hooks/useViewportOrientation.js.map +1 -0
- package/dist/index.d.ts +25 -563
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -3179
- package/dist/index.js.map +1 -1
- package/dist/layers/ImageLayer.d.ts +23 -0
- package/dist/layers/ImageLayer.d.ts.map +1 -0
- package/dist/layers/ImageLayer.js +104 -0
- package/dist/layers/ImageLayer.js.map +1 -0
- package/dist/layers/MapLayer.d.ts +29 -0
- package/dist/layers/MapLayer.d.ts.map +1 -0
- package/dist/layers/MapLayer.js +97 -0
- package/dist/layers/MapLayer.js.map +1 -0
- package/dist/layers/ShapeLayer.d.ts +20 -0
- package/dist/layers/ShapeLayer.d.ts.map +1 -0
- package/dist/layers/ShapeLayer.js +35 -0
- package/dist/layers/ShapeLayer.js.map +1 -0
- package/dist/layers/TextLayer.d.ts +23 -0
- package/dist/layers/TextLayer.d.ts.map +1 -0
- package/dist/layers/TextLayer.js +126 -0
- package/dist/layers/TextLayer.js.map +1 -0
- package/dist/layers/VideoLayer.d.ts +40 -0
- package/dist/layers/VideoLayer.d.ts.map +1 -0
- package/dist/layers/VideoLayer.js +105 -0
- package/dist/layers/VideoLayer.js.map +1 -0
- package/dist/layers/index.d.ts +6 -0
- package/dist/layers/index.d.ts.map +1 -0
- package/dist/layers/index.js +6 -0
- package/dist/layers/index.js.map +1 -0
- package/dist/squisq-player.global.js +1 -1
- package/dist/standalone-entry.d.ts +61 -0
- package/dist/standalone-entry.d.ts.map +1 -0
- package/dist/standalone-entry.js +169 -0
- package/dist/standalone-entry.js.map +1 -0
- package/dist/standalone-source.js +1 -1
- package/dist/types.d.ts +131 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +21 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/animationUtils.d.ts +8 -0
- package/dist/utils/animationUtils.d.ts.map +1 -0
- package/dist/utils/animationUtils.js +8 -0
- package/dist/utils/animationUtils.js.map +1 -0
- package/dist/utils/layerUtils.d.ts +17 -0
- package/dist/utils/layerUtils.d.ts.map +1 -0
- package/dist/utils/layerUtils.js +37 -0
- package/dist/utils/layerUtils.js.map +1 -0
- package/dist/utils/mapTileUtils.d.ts +105 -0
- package/dist/utils/mapTileUtils.d.ts.map +1 -0
- package/dist/utils/mapTileUtils.js +268 -0
- package/dist/utils/mapTileUtils.js.map +1 -0
- package/package.json +3 -4
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ImageLayer Component
|
|
3
|
+
*
|
|
4
|
+
* Renders an image layer within an SVG block. Supports Ken Burns and other
|
|
5
|
+
* animations via CSS classes. Images are rendered using SVG <image> element
|
|
6
|
+
* with proper aspect ratio handling.
|
|
7
|
+
*/
|
|
8
|
+
import type { ImageLayer as ImageLayerType } from '@bendyline/squisq/schemas';
|
|
9
|
+
interface ImageLayerProps {
|
|
10
|
+
layer: ImageLayerType;
|
|
11
|
+
/** Base path for resolving relative image URLs */
|
|
12
|
+
basePath: string;
|
|
13
|
+
/** Viewport dimensions for percentage calculations */
|
|
14
|
+
viewport: {
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
};
|
|
18
|
+
/** Current time relative to block start (for animation timing) */
|
|
19
|
+
blockTime: number;
|
|
20
|
+
}
|
|
21
|
+
export declare function ImageLayer({ layer, basePath, viewport, blockTime }: ImageLayerProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export default ImageLayer;
|
|
23
|
+
//# sourceMappingURL=ImageLayer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageLayer.d.ts","sourceRoot":"","sources":["../../src/layers/ImageLayer.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAa,MAAM,2BAA2B,CAAC;AAKzF,UAAU,eAAe;IACvB,KAAK,EAAE,cAAc,CAAC;IACtB,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,eAAe,2CA+GnF;AA+CD,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { getAnimationStyle } from '../utils/animationUtils';
|
|
3
|
+
import { resolveValue, getAnchorOffset } from '../utils/layerUtils';
|
|
4
|
+
import { useMediaUrl } from '../hooks/MediaContext';
|
|
5
|
+
export function ImageLayer({ layer, basePath, viewport, blockTime }) {
|
|
6
|
+
const { content, position, animation } = layer;
|
|
7
|
+
// Resolve position values to pixels
|
|
8
|
+
const x = resolveValue(position.x, viewport.width);
|
|
9
|
+
const y = resolveValue(position.y, viewport.height);
|
|
10
|
+
const width = position.width ? resolveValue(position.width, viewport.width) : viewport.width;
|
|
11
|
+
const height = position.height ? resolveValue(position.height, viewport.height) : viewport.height;
|
|
12
|
+
// Apply anchor offset
|
|
13
|
+
const offset = getAnchorOffset(position.anchor, width, height);
|
|
14
|
+
const finalX = x + offset.x;
|
|
15
|
+
const finalY = y + offset.y;
|
|
16
|
+
// Resolve image URL via MediaProvider (if available), falling back to basePath
|
|
17
|
+
const src = useMediaUrl(content.src, basePath);
|
|
18
|
+
// Get animation styles
|
|
19
|
+
const animStyle = getAnimationStyle(animation, blockTime);
|
|
20
|
+
// SVG preserveAspectRatio based on fit mode
|
|
21
|
+
const preserveAspectRatio = getPreserveAspectRatio(content.fit);
|
|
22
|
+
const isCover = content.fit === 'cover';
|
|
23
|
+
// Detect spatial (transform-based) animations that need Ken Burns treatment.
|
|
24
|
+
// These animations should move the image *content* within fixed bounds rather
|
|
25
|
+
// than shifting the entire image container.
|
|
26
|
+
const isSpatialAnim = animation && SPATIAL_ANIMATION_TYPES.has(animation.type);
|
|
27
|
+
// Ken Burns mode: cover image with spatial animation.
|
|
28
|
+
// Keep the container static, animate the inner <img> within clipped bounds.
|
|
29
|
+
if (isCover && isSpatialAnim && animation) {
|
|
30
|
+
const kbAnim = remapToKenBurns(animation);
|
|
31
|
+
const kbStyle = getAnimationStyle(kbAnim, blockTime);
|
|
32
|
+
return (_jsx("g", { className: "block-layer block-layer--image", "data-layer-id": layer.id, children: _jsx("foreignObject", { x: finalX, y: finalY, width: width, height: height, children: _jsx("div", { style: {
|
|
33
|
+
width: '100%',
|
|
34
|
+
height: '100%',
|
|
35
|
+
overflow: 'hidden',
|
|
36
|
+
}, children: _jsx("img", { src: src, alt: content.alt || '', className: kbStyle.className, style: {
|
|
37
|
+
width: '100%',
|
|
38
|
+
height: '100%',
|
|
39
|
+
objectFit: 'cover',
|
|
40
|
+
objectPosition: 'center',
|
|
41
|
+
display: 'block',
|
|
42
|
+
pointerEvents: 'none',
|
|
43
|
+
transformOrigin: 'center center',
|
|
44
|
+
...kbStyle.style,
|
|
45
|
+
} }) }) }) }));
|
|
46
|
+
}
|
|
47
|
+
// For cover mode, use foreignObject with CSS object-fit for more reliable coverage
|
|
48
|
+
// SVG's preserveAspectRatio can be inconsistent across browsers
|
|
49
|
+
if (isCover) {
|
|
50
|
+
return (_jsx("g", { className: `block-layer block-layer--image ${animStyle.className}`, style: animStyle.style, "data-layer-id": layer.id, children: _jsx("foreignObject", { x: finalX, y: finalY, width: width, height: height, children: _jsx("img", { src: src, alt: content.alt || '', style: {
|
|
51
|
+
width: '100%',
|
|
52
|
+
height: '100%',
|
|
53
|
+
objectFit: 'cover',
|
|
54
|
+
objectPosition: 'center',
|
|
55
|
+
display: 'block',
|
|
56
|
+
pointerEvents: 'none',
|
|
57
|
+
} }) }) }));
|
|
58
|
+
}
|
|
59
|
+
// For contain/fill modes, use SVG image element
|
|
60
|
+
return (_jsx("g", { className: `block-layer block-layer--image ${animStyle.className}`, style: animStyle.style, "data-layer-id": layer.id, children: _jsx("image", { href: src, x: finalX, y: finalY, width: width, height: height, preserveAspectRatio: preserveAspectRatio, style: { pointerEvents: 'none' } }) }));
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Map fit mode to SVG preserveAspectRatio.
|
|
64
|
+
*/
|
|
65
|
+
function getPreserveAspectRatio(fit) {
|
|
66
|
+
switch (fit) {
|
|
67
|
+
case 'cover':
|
|
68
|
+
return 'xMidYMid slice';
|
|
69
|
+
case 'fill':
|
|
70
|
+
return 'none';
|
|
71
|
+
case 'contain':
|
|
72
|
+
default:
|
|
73
|
+
return 'xMidYMid meet';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// ============================================
|
|
77
|
+
// Ken Burns Helpers
|
|
78
|
+
// ============================================
|
|
79
|
+
/** Animation types that use CSS transforms (translate/scale). */
|
|
80
|
+
const SPATIAL_ANIMATION_TYPES = new Set(['panLeft', 'panRight', 'slowZoom', 'zoomIn', 'zoomOut']);
|
|
81
|
+
/**
|
|
82
|
+
* Remap animation for Ken Burns inner-image rendering.
|
|
83
|
+
*
|
|
84
|
+
* Pure pan animations (panLeft/panRight) get remapped to combined slowZoom+pan
|
|
85
|
+
* to maintain minimum scale and avoid revealing gaps at image edges.
|
|
86
|
+
* zoomIn/zoomOut get remapped to slowZoom variants (no opacity change, which
|
|
87
|
+
* is inappropriate for sustained ambient motion).
|
|
88
|
+
*/
|
|
89
|
+
function remapToKenBurns(anim) {
|
|
90
|
+
switch (anim.type) {
|
|
91
|
+
case 'panLeft':
|
|
92
|
+
return { ...anim, type: 'slowZoom', panDirection: 'left' };
|
|
93
|
+
case 'panRight':
|
|
94
|
+
return { ...anim, type: 'slowZoom', panDirection: 'right' };
|
|
95
|
+
case 'zoomIn':
|
|
96
|
+
return { ...anim, type: 'slowZoom', direction: 'in' };
|
|
97
|
+
case 'zoomOut':
|
|
98
|
+
return { ...anim, type: 'slowZoom', direction: 'out' };
|
|
99
|
+
default:
|
|
100
|
+
return anim; // slowZoom variants are already correct
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export default ImageLayer;
|
|
104
|
+
//# sourceMappingURL=ImageLayer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageLayer.js","sourceRoot":"","sources":["../../src/layers/ImageLayer.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MapLayer Component
|
|
3
|
+
*
|
|
4
|
+
* Renders a geographic map layer within an SVG block. Maps are composed from
|
|
5
|
+
* static tile images fetched from free/open-source providers.
|
|
6
|
+
*
|
|
7
|
+
* For video export reliability, maps can use pre-rendered static images via
|
|
8
|
+
* the staticSrc property, avoiding tile loading race conditions during capture.
|
|
9
|
+
*
|
|
10
|
+
* Tile fetching: Tiles are loaded on mount and composited into a data URL
|
|
11
|
+
* for SVG embedding. This ensures correct rendering in both browser and
|
|
12
|
+
* Playwright screenshot contexts.
|
|
13
|
+
*/
|
|
14
|
+
import type { MapLayer as MapLayerType } from '@bendyline/squisq/schemas';
|
|
15
|
+
interface MapLayerProps {
|
|
16
|
+
layer: MapLayerType;
|
|
17
|
+
/** Base path for resolving relative image URLs */
|
|
18
|
+
basePath: string;
|
|
19
|
+
/** Viewport dimensions for percentage calculations */
|
|
20
|
+
viewport: {
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
};
|
|
24
|
+
/** Current time relative to block start (for animation timing) */
|
|
25
|
+
blockTime: number;
|
|
26
|
+
}
|
|
27
|
+
export declare function MapLayer({ layer, basePath, viewport, blockTime }: MapLayerProps): import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
export default MapLayer;
|
|
29
|
+
//# sourceMappingURL=MapLayer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MapLayer.d.ts","sourceRoot":"","sources":["../../src/layers/MapLayer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAK1E,UAAU,aAAa;IACrB,KAAK,EAAE,YAAY,CAAC;IACpB,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,aAAa,2CAuJ/E;AAED,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* MapLayer Component
|
|
4
|
+
*
|
|
5
|
+
* Renders a geographic map layer within an SVG block. Maps are composed from
|
|
6
|
+
* static tile images fetched from free/open-source providers.
|
|
7
|
+
*
|
|
8
|
+
* For video export reliability, maps can use pre-rendered static images via
|
|
9
|
+
* the staticSrc property, avoiding tile loading race conditions during capture.
|
|
10
|
+
*
|
|
11
|
+
* Tile fetching: Tiles are loaded on mount and composited into a data URL
|
|
12
|
+
* for SVG embedding. This ensures correct rendering in both browser and
|
|
13
|
+
* Playwright screenshot contexts.
|
|
14
|
+
*/
|
|
15
|
+
import { useState, useEffect } from 'react';
|
|
16
|
+
import { getAnimationStyle } from '../utils/animationUtils';
|
|
17
|
+
import { resolveValue, getAnchorOffset } from '../utils/layerUtils';
|
|
18
|
+
import { composeMapImage } from '../utils/mapTileUtils';
|
|
19
|
+
export function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
20
|
+
const { content, position, animation } = layer;
|
|
21
|
+
const [mapImageUrl, setMapImageUrl] = useState(null);
|
|
22
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
23
|
+
const [error, setError] = useState(null);
|
|
24
|
+
// Resolve position values to pixels
|
|
25
|
+
const x = resolveValue(position.x, viewport.width);
|
|
26
|
+
const y = resolveValue(position.y, viewport.height);
|
|
27
|
+
const width = position.width ? resolveValue(position.width, viewport.width) : viewport.width;
|
|
28
|
+
const height = position.height ? resolveValue(position.height, viewport.height) : viewport.height;
|
|
29
|
+
// Apply anchor offset
|
|
30
|
+
const offset = getAnchorOffset(position.anchor, width, height);
|
|
31
|
+
const finalX = x + offset.x;
|
|
32
|
+
const finalY = y + offset.y;
|
|
33
|
+
// Use static image if provided, otherwise fetch and compose tiles
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
let cancelled = false;
|
|
36
|
+
if (content.staticSrc) {
|
|
37
|
+
// Use pre-rendered static image
|
|
38
|
+
const src = content.staticSrc.startsWith('http')
|
|
39
|
+
? content.staticSrc
|
|
40
|
+
: `${basePath}/${content.staticSrc}`;
|
|
41
|
+
setMapImageUrl(src);
|
|
42
|
+
setIsLoading(false);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
// Compose map from tiles
|
|
46
|
+
setIsLoading(true);
|
|
47
|
+
setError(null);
|
|
48
|
+
composeMapImage({
|
|
49
|
+
center: content.center,
|
|
50
|
+
zoom: content.zoom,
|
|
51
|
+
style: content.style,
|
|
52
|
+
width,
|
|
53
|
+
height,
|
|
54
|
+
markers: content.markers,
|
|
55
|
+
showAttribution: content.showAttribution !== false,
|
|
56
|
+
})
|
|
57
|
+
.then((dataUrl) => {
|
|
58
|
+
if (!cancelled) {
|
|
59
|
+
setMapImageUrl(dataUrl);
|
|
60
|
+
setIsLoading(false);
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
.catch((err) => {
|
|
64
|
+
if (!cancelled) {
|
|
65
|
+
console.error('Failed to compose map:', err);
|
|
66
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
67
|
+
setIsLoading(false);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
return () => {
|
|
71
|
+
cancelled = true;
|
|
72
|
+
};
|
|
73
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- content properties are destructured below; center/markers/showAttribution are stable per-render
|
|
74
|
+
}, [
|
|
75
|
+
content.center.lat,
|
|
76
|
+
content.center.lng,
|
|
77
|
+
content.zoom,
|
|
78
|
+
content.style,
|
|
79
|
+
content.staticSrc,
|
|
80
|
+
width,
|
|
81
|
+
height,
|
|
82
|
+
basePath,
|
|
83
|
+
]);
|
|
84
|
+
// Get animation styles
|
|
85
|
+
const animStyle = getAnimationStyle(animation, blockTime);
|
|
86
|
+
// Render loading state
|
|
87
|
+
if (isLoading) {
|
|
88
|
+
return (_jsxs("g", { className: `block-layer block-layer--map ${animStyle.className}`, style: animStyle.style, "data-layer-id": layer.id, children: [_jsx("rect", { x: finalX, y: finalY, width: width, height: height, fill: "#e5e7eb" }), _jsx("text", { x: finalX + width / 2, y: finalY + height / 2, textAnchor: "middle", dominantBaseline: "middle", fill: "#9ca3af", fontSize: "24", fontFamily: "system-ui, sans-serif", children: "Loading map..." })] }));
|
|
89
|
+
}
|
|
90
|
+
// Render error state
|
|
91
|
+
if (error || !mapImageUrl) {
|
|
92
|
+
return (_jsxs("g", { className: `block-layer block-layer--map ${animStyle.className}`, style: animStyle.style, "data-layer-id": layer.id, children: [_jsx("rect", { x: finalX, y: finalY, width: width, height: height, fill: "#fef2f2" }), _jsx("text", { x: finalX + width / 2, y: finalY + height / 2, textAnchor: "middle", dominantBaseline: "middle", fill: "#dc2626", fontSize: "18", fontFamily: "system-ui, sans-serif", children: "Map failed to load" })] }));
|
|
93
|
+
}
|
|
94
|
+
return (_jsxs("g", { className: `block-layer block-layer--map ${animStyle.className}`, style: animStyle.style, "data-layer-id": layer.id, children: [_jsx("defs", { children: _jsx("clipPath", { id: `clip-${layer.id}`, children: _jsx("rect", { x: finalX, y: finalY, width: width, height: height }) }) }), _jsx("g", { clipPath: `url(#clip-${layer.id})`, children: _jsx("image", { href: mapImageUrl, x: finalX, y: finalY, width: width, height: height, preserveAspectRatio: "xMidYMid slice", style: { pointerEvents: 'none' } }) })] }));
|
|
95
|
+
}
|
|
96
|
+
export default MapLayer;
|
|
97
|
+
//# sourceMappingURL=MapLayer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MapLayer.js","sourceRoot":"","sources":["../../src/layers/MapLayer.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAYxD,MAAM,UAAU,QAAQ,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAiB;IAC9E,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAC/C,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACpE,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAExD,oCAAoC;IACpC,MAAM,CAAC,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnD,MAAM,CAAC,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;IAC7F,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;IAElG,sBAAsB;IACtB,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IAC5B,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IAE5B,kEAAkE;IAClE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,SAAS,GAAG,KAAK,CAAC;QAEtB,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,gCAAgC;YAChC,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC;gBAC9C,CAAC,CAAC,OAAO,CAAC,SAAS;gBACnB,CAAC,CAAC,GAAG,QAAQ,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACvC,cAAc,CAAC,GAAG,CAAC,CAAC;YACpB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO;QACT,CAAC;QAED,yBAAyB;QACzB,YAAY,CAAC,IAAI,CAAC,CAAC;QACnB,QAAQ,CAAC,IAAI,CAAC,CAAC;QAEf,eAAe,CAAC;YACd,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,KAAK;YACL,MAAM;YACN,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,eAAe,EAAE,OAAO,CAAC,eAAe,KAAK,KAAK;SACnD,CAAC;aACC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;YAChB,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,cAAc,CAAC,OAAO,CAAC,CAAC;gBACxB,YAAY,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;QACH,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;YACtB,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAC;gBAC7C,QAAQ,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC3D,YAAY,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;QACH,CAAC,CAAC,CAAC;QAEL,OAAO,GAAG,EAAE;YACV,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC,CAAC;QACF,0JAA0J;IAC5J,CAAC,EAAE;QACD,OAAO,CAAC,MAAM,CAAC,GAAG;QAClB,OAAO,CAAC,MAAM,CAAC,GAAG;QAClB,OAAO,CAAC,IAAI;QACZ,OAAO,CAAC,KAAK;QACb,OAAO,CAAC,SAAS;QACjB,KAAK;QACL,MAAM;QACN,QAAQ;KACT,CAAC,CAAC;IAEH,uBAAuB;IACvB,MAAM,SAAS,GAAG,iBAAiB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAE1D,uBAAuB;IACvB,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,CACL,aACE,SAAS,EAAE,gCAAgC,SAAS,CAAC,SAAS,EAAE,EAChE,KAAK,EAAE,SAAS,CAAC,KAAK,mBACP,KAAK,CAAC,EAAE,aAEvB,eAAM,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAC,SAAS,GAAG,EAC3E,eACE,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,CAAC,EACrB,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,CAAC,EACtB,UAAU,EAAC,QAAQ,EACnB,gBAAgB,EAAC,QAAQ,EACzB,IAAI,EAAC,SAAS,EACd,QAAQ,EAAC,IAAI,EACb,UAAU,EAAC,uBAAuB,+BAG7B,IACL,CACL,CAAC;IACJ,CAAC;IAED,qBAAqB;IACrB,IAAI,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;QAC1B,OAAO,CACL,aACE,SAAS,EAAE,gCAAgC,SAAS,CAAC,SAAS,EAAE,EAChE,KAAK,EAAE,SAAS,CAAC,KAAK,mBACP,KAAK,CAAC,EAAE,aAEvB,eAAM,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAC,SAAS,GAAG,EAC3E,eACE,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,CAAC,EACrB,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,CAAC,EACtB,UAAU,EAAC,QAAQ,EACnB,gBAAgB,EAAC,QAAQ,EACzB,IAAI,EAAC,SAAS,EACd,QAAQ,EAAC,IAAI,EACb,UAAU,EAAC,uBAAuB,mCAG7B,IACL,CACL,CAAC;IACJ,CAAC;IAED,OAAO,CACL,aACE,SAAS,EAAE,gCAAgC,SAAS,CAAC,SAAS,EAAE,EAChE,KAAK,EAAE,SAAS,CAAC,KAAK,mBACP,KAAK,CAAC,EAAE,aAGvB,yBACE,mBAAU,EAAE,EAAE,QAAQ,KAAK,CAAC,EAAE,EAAE,YAC9B,eAAM,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAI,GACnD,GACN,EAGP,YAAG,QAAQ,EAAE,aAAa,KAAK,CAAC,EAAE,GAAG,YACnC,gBACE,IAAI,EAAE,WAAW,EACjB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,mBAAmB,EAAC,gBAAgB,EACpC,KAAK,EAAE,EAAE,aAAa,EAAE,MAAM,EAAE,GAChC,GACA,IACF,CACL,CAAC;AACJ,CAAC;AAED,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ShapeLayer Component
|
|
3
|
+
*
|
|
4
|
+
* Renders simple geometric shapes (rect, circle, line) within an SVG block.
|
|
5
|
+
* Useful for visual accents, dividers, and background elements.
|
|
6
|
+
*/
|
|
7
|
+
import type { ShapeLayer as ShapeLayerType } from '@bendyline/squisq/schemas';
|
|
8
|
+
interface ShapeLayerProps {
|
|
9
|
+
layer: ShapeLayerType;
|
|
10
|
+
/** Viewport dimensions for percentage calculations */
|
|
11
|
+
viewport: {
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
};
|
|
15
|
+
/** Current time relative to block start */
|
|
16
|
+
blockTime: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function ShapeLayer({ layer, viewport, blockTime }: ShapeLayerProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export default ShapeLayer;
|
|
20
|
+
//# sourceMappingURL=ShapeLayer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShapeLayer.d.ts","sourceRoot":"","sources":["../../src/layers/ShapeLayer.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAI9E,UAAU,eAAe;IACvB,KAAK,EAAE,cAAc,CAAC;IACtB,sDAAsD;IACtD,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,eAAe,2CAqFzE;AAED,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { getAnimationStyle } from '../utils/animationUtils';
|
|
3
|
+
import { resolveValue } from '../utils/layerUtils';
|
|
4
|
+
export function ShapeLayer({ layer, viewport, blockTime }) {
|
|
5
|
+
const { content, position, animation } = layer;
|
|
6
|
+
// Resolve position values to pixels
|
|
7
|
+
const x = resolveValue(position.x, viewport.width);
|
|
8
|
+
const y = resolveValue(position.y, viewport.height);
|
|
9
|
+
const width = position.width ? resolveValue(position.width, viewport.width) : 100;
|
|
10
|
+
const height = position.height ? resolveValue(position.height, viewport.height) : 100;
|
|
11
|
+
// Get animation styles
|
|
12
|
+
const animStyle = getAnimationStyle(animation, blockTime);
|
|
13
|
+
// Check if fill is a CSS gradient (SVG rect doesn't support CSS gradients natively)
|
|
14
|
+
const fill = content.fill || 'none';
|
|
15
|
+
const isCSSGradient = typeof fill === 'string' && fill.includes('gradient(');
|
|
16
|
+
// For CSS gradients on rect, use foreignObject with an HTML div
|
|
17
|
+
if (content.shape === 'rect' && isCSSGradient) {
|
|
18
|
+
return (_jsx("g", { className: `block-layer block-layer--shape ${animStyle.className}`, style: animStyle.style, "data-layer-id": layer.id, children: _jsx("foreignObject", { x: x, y: y, width: width, height: height, children: _jsx("div", { style: {
|
|
19
|
+
width: '100%',
|
|
20
|
+
height: '100%',
|
|
21
|
+
background: fill,
|
|
22
|
+
borderRadius: content.borderRadius ? `${content.borderRadius}px` : undefined,
|
|
23
|
+
pointerEvents: 'none',
|
|
24
|
+
} }) }) }));
|
|
25
|
+
}
|
|
26
|
+
// Common style props for native SVG shapes
|
|
27
|
+
const shapeProps = {
|
|
28
|
+
fill: fill,
|
|
29
|
+
stroke: content.stroke,
|
|
30
|
+
strokeWidth: content.strokeWidth,
|
|
31
|
+
};
|
|
32
|
+
return (_jsxs("g", { className: `block-layer block-layer--shape ${animStyle.className}`, style: animStyle.style, "data-layer-id": layer.id, children: [content.shape === 'rect' && (_jsx("rect", { x: x, y: y, width: width, height: height, rx: content.borderRadius, ry: content.borderRadius, ...shapeProps })), content.shape === 'circle' && (_jsx("circle", { cx: x + width / 2, cy: y + height / 2, r: Math.min(width, height) / 2, ...shapeProps })), content.shape === 'line' && (_jsx("line", { x1: x, y1: y, x2: x + width, y2: y + height, stroke: content.stroke || '#ffffff', strokeWidth: content.strokeWidth || 2 }))] }));
|
|
33
|
+
}
|
|
34
|
+
export default ShapeLayer;
|
|
35
|
+
//# sourceMappingURL=ShapeLayer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShapeLayer.js","sourceRoot":"","sources":["../../src/layers/ShapeLayer.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TextLayer Component
|
|
3
|
+
*
|
|
4
|
+
* Renders a text layer within an SVG block. Supports multi-line text,
|
|
5
|
+
* styling options (font, color, shadow), and animations like fadeIn
|
|
6
|
+
* and typewriter effects.
|
|
7
|
+
*
|
|
8
|
+
* Text is rendered using SVG <text> elements with <tspan> for line breaks.
|
|
9
|
+
*/
|
|
10
|
+
import type { TextLayer as TextLayerType } from '@bendyline/squisq/schemas';
|
|
11
|
+
interface TextLayerProps {
|
|
12
|
+
layer: TextLayerType;
|
|
13
|
+
/** Viewport dimensions for percentage calculations */
|
|
14
|
+
viewport: {
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
};
|
|
18
|
+
/** Current time relative to block start */
|
|
19
|
+
blockTime: number;
|
|
20
|
+
}
|
|
21
|
+
export declare function TextLayer({ layer, viewport, blockTime }: TextLayerProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export default TextLayer;
|
|
23
|
+
//# sourceMappingURL=TextLayer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TextLayer.d.ts","sourceRoot":"","sources":["../../src/layers/TextLayer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAK5E,UAAU,cAAc;IACtB,KAAK,EAAE,aAAa,CAAC;IACrB,sDAAsD;IACtD,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,cAAc,2CAwFvE;AAqFD,eAAe,SAAS,CAAC"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { DEFAULT_DOC_FONT } from '@bendyline/squisq/schemas';
|
|
3
|
+
import { getAnimationStyle } from '../utils/animationUtils';
|
|
4
|
+
import { resolveValue } from '../utils/layerUtils';
|
|
5
|
+
export function TextLayer({ layer, viewport, blockTime }) {
|
|
6
|
+
const { content, position, animation } = layer;
|
|
7
|
+
const { text, style } = content;
|
|
8
|
+
// Resolve position values to pixels
|
|
9
|
+
const x = resolveValue(position.x, viewport.width);
|
|
10
|
+
const y = resolveValue(position.y, viewport.height);
|
|
11
|
+
const maxWidth = position.width ? resolveValue(position.width, viewport.width) : undefined;
|
|
12
|
+
// Apply anchor offset for text alignment
|
|
13
|
+
const textAnchor = getTextAnchor(style.textAlign, position.anchor);
|
|
14
|
+
const dominantBaseline = getDominantBaseline(position.anchor);
|
|
15
|
+
// Get animation styles
|
|
16
|
+
const animStyle = getAnimationStyle(animation, blockTime);
|
|
17
|
+
// Split text into lines, and wrap if maxWidth is specified
|
|
18
|
+
const rawLines = text.split('\n');
|
|
19
|
+
let lines = maxWidth
|
|
20
|
+
? rawLines.reduce((acc, line) => acc.concat(wrapText(line, style.fontSize, maxWidth)), [])
|
|
21
|
+
: rawLines;
|
|
22
|
+
// Truncate to maxLines if specified
|
|
23
|
+
if (style.maxLines && lines.length > style.maxLines) {
|
|
24
|
+
lines = lines.slice(0, style.maxLines);
|
|
25
|
+
// Add ellipsis to last visible line
|
|
26
|
+
const last = lines[lines.length - 1];
|
|
27
|
+
lines[lines.length - 1] = last.replace(/\s*$/, '') + '...';
|
|
28
|
+
}
|
|
29
|
+
const lineHeight = style.lineHeight || 1.4;
|
|
30
|
+
const lineHeightPx = style.fontSize * lineHeight;
|
|
31
|
+
// Build text styles
|
|
32
|
+
const textStyles = {
|
|
33
|
+
fontSize: `${style.fontSize}px`,
|
|
34
|
+
fontFamily: style.fontFamily || DEFAULT_DOC_FONT,
|
|
35
|
+
fontWeight: style.fontWeight || 'normal',
|
|
36
|
+
fill: style.color,
|
|
37
|
+
...animStyle.style,
|
|
38
|
+
};
|
|
39
|
+
// Add shadow filter if requested
|
|
40
|
+
const filterId = style.shadow ? `shadow-${layer.id}` : undefined;
|
|
41
|
+
return (_jsxs("g", { className: `block-layer block-layer--text ${animStyle.className}`, "data-layer-id": layer.id, children: [style.shadow && (_jsx("defs", { children: _jsx("filter", { id: filterId, x: "-20%", y: "-20%", width: "140%", height: "140%", children: _jsx("feDropShadow", { dx: "0", dy: "2", stdDeviation: "3", floodColor: "rgba(0,0,0,0.7)" }) }) })), style.background && (_jsx("rect", { x: x - (style.padding || 16), y: y - style.fontSize - (style.padding || 16), width: getTextBoxWidth(lines, style) + (style.padding || 16) * 2, height: lines.length * lineHeightPx + (style.padding || 16) * 2, fill: style.background, rx: 4, ry: 4 })), _jsx("text", { x: x, y: y, textAnchor: textAnchor, dominantBaseline: dominantBaseline, style: textStyles, filter: filterId ? `url(#${filterId})` : undefined, children: lines.map((line, i) => (_jsxs("tspan", { x: x, dy: i === 0 ? 0 : lineHeightPx, children: [line || '\u00A0', " "] }, i))) })] }));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Map text alignment to SVG text-anchor.
|
|
45
|
+
*/
|
|
46
|
+
function getTextAnchor(align, anchor) {
|
|
47
|
+
// Explicit alignment takes precedence
|
|
48
|
+
if (align === 'center')
|
|
49
|
+
return 'middle';
|
|
50
|
+
if (align === 'right')
|
|
51
|
+
return 'end';
|
|
52
|
+
if (align === 'left')
|
|
53
|
+
return 'start';
|
|
54
|
+
// Otherwise infer from position anchor
|
|
55
|
+
if (anchor?.includes('right'))
|
|
56
|
+
return 'end';
|
|
57
|
+
if (anchor === 'center')
|
|
58
|
+
return 'middle';
|
|
59
|
+
return 'start';
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Map position anchor to SVG dominant-baseline.
|
|
63
|
+
*/
|
|
64
|
+
function getDominantBaseline(anchor) {
|
|
65
|
+
if (anchor?.includes('bottom'))
|
|
66
|
+
return 'text-after-edge';
|
|
67
|
+
if (anchor === 'center')
|
|
68
|
+
return 'middle';
|
|
69
|
+
return 'text-before-edge';
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Estimate text box width based on content (rough approximation).
|
|
73
|
+
*/
|
|
74
|
+
function getTextBoxWidth(lines, style) {
|
|
75
|
+
const maxLineLength = Math.max(...lines.map((l) => l.length));
|
|
76
|
+
// Rough estimate: average character width is ~0.5 * fontSize
|
|
77
|
+
return maxLineLength * style.fontSize * 0.55;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Wrap text to fit within a maximum width.
|
|
81
|
+
* Uses character-based estimation for line breaking.
|
|
82
|
+
*/
|
|
83
|
+
function wrapText(text, fontSize, maxWidth) {
|
|
84
|
+
if (!text.trim())
|
|
85
|
+
return [''];
|
|
86
|
+
// Estimate characters per line (average char width ~0.5 * fontSize for most fonts)
|
|
87
|
+
const avgCharWidth = fontSize * 0.5;
|
|
88
|
+
const charsPerLine = Math.floor(maxWidth / avgCharWidth);
|
|
89
|
+
if (charsPerLine <= 0)
|
|
90
|
+
return [text];
|
|
91
|
+
const words = text.split(/\s+/);
|
|
92
|
+
const lines = [];
|
|
93
|
+
let currentLine = '';
|
|
94
|
+
for (const word of words) {
|
|
95
|
+
const testLine = currentLine ? `${currentLine} ${word}` : word;
|
|
96
|
+
if (testLine.length <= charsPerLine) {
|
|
97
|
+
currentLine = testLine;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// Current line is full, start new line
|
|
101
|
+
if (currentLine) {
|
|
102
|
+
lines.push(currentLine);
|
|
103
|
+
}
|
|
104
|
+
// Handle words longer than a line
|
|
105
|
+
if (word.length > charsPerLine) {
|
|
106
|
+
// Break long word
|
|
107
|
+
let remaining = word;
|
|
108
|
+
while (remaining.length > charsPerLine) {
|
|
109
|
+
lines.push(remaining.slice(0, charsPerLine));
|
|
110
|
+
remaining = remaining.slice(charsPerLine);
|
|
111
|
+
}
|
|
112
|
+
currentLine = remaining;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
currentLine = word;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Add remaining text
|
|
120
|
+
if (currentLine) {
|
|
121
|
+
lines.push(currentLine);
|
|
122
|
+
}
|
|
123
|
+
return lines.length > 0 ? lines : [''];
|
|
124
|
+
}
|
|
125
|
+
export default TextLayer;
|
|
126
|
+
//# sourceMappingURL=TextLayer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TextLayer.js","sourceRoot":"","sources":["../../src/layers/TextLayer.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VideoLayer Component
|
|
3
|
+
*
|
|
4
|
+
* Renders a video clip layer within an SVG block. Uses an HTML5 <video> element
|
|
5
|
+
* inside a <foreignObject> (same pattern as ImageLayer for cover-mode images).
|
|
6
|
+
* Videos are always muted — narration audio is the only sound track.
|
|
7
|
+
*
|
|
8
|
+
* Two modes of operation:
|
|
9
|
+
* 1. Normal playback: Video auto-plays from clipStart to clipEnd on mount,
|
|
10
|
+
* pausing when the clip ends or when the block is no longer active.
|
|
11
|
+
* 2. Render/seekTo mode (Playwright frame capture): Video is paused and seeked
|
|
12
|
+
* programmatically via data attributes read by DocPlayer's seekTo handler.
|
|
13
|
+
*
|
|
14
|
+
* The <video> element carries data-clip-start and data-clip-end attributes so
|
|
15
|
+
* the seekTo handler can calculate the correct video time for any doc time.
|
|
16
|
+
*
|
|
17
|
+
* Related Files:
|
|
18
|
+
* - schemas/Doc.ts — VideoLayer type definition
|
|
19
|
+
* - shared/doc/templates/videoWithCaption.ts — template producing VideoLayers
|
|
20
|
+
* - site/src/components/doc/DocPlayer.tsx — seekTo handler for video sync
|
|
21
|
+
* - site/src/components/doc/layers/ImageLayer.tsx — similar foreignObject pattern
|
|
22
|
+
*/
|
|
23
|
+
import type { VideoLayer as VideoLayerType } from '@bendyline/squisq/schemas';
|
|
24
|
+
interface VideoLayerProps {
|
|
25
|
+
layer: VideoLayerType;
|
|
26
|
+
/** Base path for resolving relative video URLs */
|
|
27
|
+
basePath: string;
|
|
28
|
+
/** Viewport dimensions for percentage calculations */
|
|
29
|
+
viewport: {
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
};
|
|
33
|
+
/** Current time relative to block start (for playback sync) */
|
|
34
|
+
blockTime: number;
|
|
35
|
+
/** Whether the doc is currently playing */
|
|
36
|
+
isPlaying?: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare function VideoLayer({ layer, basePath, viewport, blockTime: _blockTime, isPlaying, }: VideoLayerProps): import("react/jsx-runtime").JSX.Element;
|
|
39
|
+
export default VideoLayer;
|
|
40
|
+
//# sourceMappingURL=VideoLayer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoLayer.d.ts","sourceRoot":"","sources":["../../src/layers/VideoLayer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAI9E,UAAU,eAAe;IACvB,KAAK,EAAE,cAAc,CAAC;IACtB,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,SAAS,EAAE,UAAU,EACrB,SAAS,GACV,EAAE,eAAe,2CAqGjB;AAED,eAAe,UAAU,CAAC"}
|