@pixodesk/svg-animator-react 1.0.29 โ 1.0.34
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/README.md +11 -9
- package/dist/index.cjs +8 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +8 -7
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +266 -103
- package/dist/index.umd.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# animator-react
|
|
2
2
|
|
|
3
|
+
> ๐ Full user guide: [docs/library/react.md](../../docs/library/react.md) ยท [all docs](../../README.md#documentation)
|
|
4
|
+
|
|
3
5
|
[](https://github.com/pixodesk/pixodesk-svg-animator/actions/workflows/ci.yml)
|
|
4
6
|
[](https://opensource.org/licenses/MIT)
|
|
5
7
|
|
|
@@ -64,18 +66,18 @@ const api = useRef<ReactAnimatorApi>(null);
|
|
|
64
66
|
|
|
65
67
|
### Controlled time
|
|
66
68
|
|
|
67
|
-
|
|
69
|
+
Move through the animation with a slider, or show one fixed frame:
|
|
68
70
|
|
|
69
71
|
```tsx
|
|
70
|
-
const [
|
|
72
|
+
const [time, setTime] = useState(0);
|
|
71
73
|
|
|
72
|
-
<PixodeskSvgAnimator doc={animation}
|
|
74
|
+
<PixodeskSvgAnimator doc={animation} time={time} />
|
|
73
75
|
<input
|
|
74
76
|
type="range"
|
|
75
77
|
min={0}
|
|
76
78
|
max={2000}
|
|
77
|
-
value={
|
|
78
|
-
onChange={e =>
|
|
79
|
+
value={time}
|
|
80
|
+
onChange={e => setTime(Number(e.target.value))}
|
|
79
81
|
/>
|
|
80
82
|
```
|
|
81
83
|
|
|
@@ -89,8 +91,8 @@ const [timeMs, setTimeMs] = useState(0);
|
|
|
89
91
|
| `play` | `boolean` | Start playback, ignoring document triggers |
|
|
90
92
|
| `pause` | `boolean` | Pause current playback |
|
|
91
93
|
| `apiRef` | `RefObject<ReactAnimatorApi>` | Ref for imperative control |
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
+
| `progress` | `number` | show the frame at this position in the whole timeline (duration ร iterations): `0` is the first frame, `0.5` the middle, `1` the last |
|
|
95
|
+
| `time` | `number` | show the frame at that time, in milliseconds from the start |
|
|
94
96
|
| `mode` | `'auto' \| 'waapi' \| 'frames'` | Animation engine |
|
|
95
97
|
| `duration` | `number` | Duration override (ms) |
|
|
96
98
|
| `delay` | `number` | Delay before start (ms) |
|
|
@@ -109,6 +111,6 @@ const [timeMs, setTimeMs] = useState(0);
|
|
|
109
111
|
| `className` | `string` | CSS class applied to the rendered root `<svg>` |
|
|
110
112
|
| `style` | `CSSProperties` | Inline styles applied to the rendered root `<svg>` |
|
|
111
113
|
|
|
112
|
-
With none of `autoplay` / `play` / `pause` / `apiRef` / `
|
|
114
|
+
With none of `autoplay` / `play` / `pause` / `apiRef` / `progress` / `time` set, the component renders the animation statically (initial state, no playback).
|
|
113
115
|
|
|
114
|
-
Note:
|
|
116
|
+
Note: passing a different `doc` (or unmounting) throws the old animator away and builds a new one; the old instance emits `onCancel`, `onRemove`, and `onStop` on its way out. Changing `progress` / `time` does **not** recreate the animator โ it just jumps the existing one to the new time.
|
package/dist/index.cjs
CHANGED
|
@@ -141,10 +141,11 @@ var PixodeskSvgAnimatorImpl = ({
|
|
|
141
141
|
}) => {
|
|
142
142
|
doc = (0, import_svg_animator_web.generateNewIds)(doc);
|
|
143
143
|
const elementRefs = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
144
|
-
const renderNode = (node, isRoot = false) => {
|
|
144
|
+
const renderNode = (node, isRoot = false, key) => {
|
|
145
145
|
if (!node) return null;
|
|
146
146
|
const { type, animate, meta, children, ...props } = node;
|
|
147
147
|
const normProps = (0, import_svg_animator_web.getNormalizedProps)(props);
|
|
148
|
+
if (key !== void 0) normProps["key"] = key;
|
|
148
149
|
normProps["ref"] = (domEl) => {
|
|
149
150
|
if (node["id"]) elementRefs.current.set(node["id"], domEl);
|
|
150
151
|
};
|
|
@@ -154,7 +155,7 @@ var PixodeskSvgAnimatorImpl = ({
|
|
|
154
155
|
}
|
|
155
156
|
if (style) normProps["style"] = style;
|
|
156
157
|
}
|
|
157
|
-
return (0, import_react2.createElement)(type, normProps, children?.map((child) => renderNode(child)));
|
|
158
|
+
return (0, import_react2.createElement)(type, normProps, children?.map((child, i) => renderNode(child, false, child["id"] ?? i)));
|
|
158
159
|
};
|
|
159
160
|
const root = doc ? renderNode(doc, true) : null;
|
|
160
161
|
(0, import_react2.useEffect)(() => {
|
|
@@ -190,8 +191,8 @@ var PixodeskSvgAnimator = ({
|
|
|
190
191
|
autoplay,
|
|
191
192
|
play,
|
|
192
193
|
pause,
|
|
194
|
+
progress,
|
|
193
195
|
time,
|
|
194
|
-
timeMs,
|
|
195
196
|
apiRef,
|
|
196
197
|
// Overrides
|
|
197
198
|
mode,
|
|
@@ -216,7 +217,7 @@ var PixodeskSvgAnimator = ({
|
|
|
216
217
|
compMode = "imperativeApi" /* imperativeApi */;
|
|
217
218
|
} else if (autoplay) {
|
|
218
219
|
compMode = "autoplay" /* autoplay */;
|
|
219
|
-
} else if (
|
|
220
|
+
} else if (progress !== void 0 || time !== void 0) {
|
|
220
221
|
compMode = "fixedTime" /* fixedTime */;
|
|
221
222
|
} else if (play !== void 0 || pause !== void 0) {
|
|
222
223
|
compMode = "play" /* play */;
|
|
@@ -271,13 +272,13 @@ var PixodeskSvgAnimator = ({
|
|
|
271
272
|
let seekMs;
|
|
272
273
|
if (compMode === "fixedTime" /* fixedTime */) {
|
|
273
274
|
const animator = doc.animator || {};
|
|
274
|
-
if (
|
|
275
|
+
if (progress !== void 0) {
|
|
275
276
|
const iterationsValue = iterations ?? animator.iterations;
|
|
276
277
|
const iterationsCount = typeof iterationsValue === "number" && iterationsValue >= 1 ? iterationsValue : 1;
|
|
277
278
|
const singleDuration = duration ?? animator.duration ?? 1e3;
|
|
278
|
-
seekMs =
|
|
279
|
+
seekMs = progress * singleDuration * iterationsCount;
|
|
279
280
|
}
|
|
280
|
-
if (
|
|
281
|
+
if (time !== void 0) seekMs = time;
|
|
281
282
|
}
|
|
282
283
|
const apiHolderRef = (0, import_react2.useRef)(null);
|
|
283
284
|
const callbacksRef = (0, import_react2.useRef)({});
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/PixodeskSvgAnimator.tsx","../src/Utils.ts","../src/PixodeskSvgCssAnimator.tsx"],"sourcesContent":["/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport PixodeskSvgAnimator from './PixodeskSvgAnimator';\nexport type { PixodeskSvgAnimatorCallbacks, PixodeskSvgAnimatorProps, ReactAnimatorApi } from './PixodeskSvgAnimator';\nexport { PixodeskSvgAnimator };\n\nimport PixodeskSvgCssAnimator from './PixodeskSvgCssAnimator';\nexport { PixodeskSvgCssAnimator };\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport type { JsMode, OutAction, PxAnimatedSvgDocument, PxAnimatorAPI, PxNode, PxPlatformAdapter, PxTrigger, StartOn } from '@pixodesk/svg-animator-web';\nimport { camelCaseToKebabWordIfNeeded, createAnimator, FillMode, generateNewIds, getNormalizedProps, STYLE_ATTR_NAMES } from '@pixodesk/svg-animator-web';\nimport type { CSSProperties, FC, ReactElement } from 'react';\nimport React, { createElement, useEffect, useImperativeHandle, useRef } from 'react';\nimport { useDepsVersion } from './Utils';\n\n\n// -- Public types -----------------------------------------------------------\n\nexport interface ReactAnimatorApi {\n /** Returns true if the animation is currently running. */\n isPlaying(): boolean;\n\n /** Starts or resumes the animation. */\n play(): void;\n\n /** Pauses the animation at its current state. */\n pause(): void;\n\n /** Stops the animation and resets it to its initial state. */\n cancel(): void;\n\n /** Jumps to the end of the animation and holds the final state. */\n finish(): void;\n\n /** Changes the speed of the animation. 1 is normal, 2 is double, -1 is reverse. */\n setPlaybackRate(rate: number): void;\n\n /** Returns the current playback time in milliseconds. */\n getCurrentTime(): number | null;\n\n /** Jumps to a specific time (in milliseconds) in the animation. */\n setCurrentTime(time: number): void;\n}\n\nexport interface PixodeskSvgAnimatorImplProps {\n className?: string;\n style?: CSSProperties;\n doc: PxAnimatedSvgDocument;\n compMode: PixodeskSvgAnimatorCompMode;\n\n /** Imperative API handle populated by the inner component. */\n apiHolderRef: React.RefObject<PxAnimatorAPI | null>;\n\n /**\n * Latest lifecycle callbacks, read at invocation time so the memoised\n * inner component always calls the current props even though it never\n * re-renders.\n */\n callbacksRef: React.RefObject<PixodeskSvgAnimatorCallbacks>;\n}\n\n/** Lifecycle callback props (subset of {@link PixodeskSvgAnimatorProps}). */\nexport interface PixodeskSvgAnimatorCallbacks {\n onPlay?: () => void;\n onStop?: () => void;\n onPause?: () => void;\n onCancel?: () => void;\n onFinish?: () => void;\n onRemove?: () => void;\n}\n\nexport interface PixodeskSvgAnimatorProps {\n\n className?: string;\n\n style?: CSSProperties;\n\n // -- Source ---------------------------------------------------------------\n\n /**\n * The animation document to render.\n *\n * TODO: Accept PxFileConfig | string to support URL-based loading, e.g.\n * <PixodeskSvgAnimator doc=\"/animation.json\" />\n */\n doc: PxAnimatedSvgDocument;\n\n // -- Rendering mode ------------------------------------------------------\n\n /** Forces a specific rendering engine. Defaults to 'auto'. */\n mode?: JsMode;\n\n // -- Timing overrides ----------------------------------------------------\n\n /** Delay before the animation starts, in milliseconds. */\n delay?: number;\n\n /** Defines the element's style when the animation is not active. */\n fill?: FillMode;\n\n /** Number of iterations, or 'infinite' for endless looping. */\n iterations?: number | 'infinite';\n\n /** Duration of a single iteration in milliseconds. */\n duration?: number;\n\n /** Playback direction. */\n direction?: PlaybackDirection;\n\n /** Target frame rate (frames per second). */\n frameRate?: number;\n\n // -- Trigger overrides ---------------------------------------------------\n\n /** The event that starts the animation. When omitted, uses the value from the document config. */\n startOn?: StartOn;\n\n /** Behaviour when the trigger condition ends (e.g. mouse-out, second click). */\n outAction?: OutAction;\n\n /** Visibility ratio (0.0โ1.0) required to trigger a scrollIntoView animation. Defaults to 0.5. */\n scrollIntoViewThreshold?: number;\n\n // -- Declarative control -------------------------------------------------\n\n /** When true, uses triggers defined in the animation document. */\n autoplay?: boolean;\n\n /**\n * Starts the animation unconditionally, ignoring document triggers.\n * Equivalent to `startOn=\"load\"` / `outAction=\"continue\"`.\n */\n play?: boolean;\n\n /** Pauses current playback. Only meaningful when `play` or `autoplay` is set. */\n pause?: boolean;\n\n // -- Imperative control --------------------------------------------------\n\n /** Ref populated with the imperative playback API. */\n apiRef?: React.RefObject<ReactAnimatorApi | null>;\n\n // -- Controlled (external) time ------------------------------------------\n\n /** Seek to a specific point in the animation, as a fraction (0โ1) of the whole timeline (duration ร iterations). */\n time?: number;\n\n /** Seek to a specific point in the animation (milliseconds). */\n timeMs?: number;\n\n // -- Callbacks -----------------------------------------------------------\n\n /** Called when the animation starts or resumes. */\n onPlay?: () => void;\n\n /** Called when the animation stops for any reason (pause / cancel / finish / removal). */\n onStop?: () => void;\n\n /** Called when the animation is paused. */\n onPause?: () => void;\n\n /** Called when the animation is cancelled. */\n onCancel?: () => void;\n\n /** Called when the animation finishes naturally. */\n onFinish?: () => void;\n\n /** Called when the animation is removed. */\n onRemove?: () => void;\n}\n\n\n// -- Internal types ---------------------------------------------------------\n\nenum PixodeskSvgAnimatorCompMode {\n static = 'static',\n autoplay = 'autoplay',\n play = 'play',\n imperativeApi = 'imperativeApi',\n fixedTime = 'fixedTime'\n}\n\n\n// -- React โ Animator bridge ------------------------------------------------\n\n/**\n * Creates a platform adapter that routes animator attribute updates\n * to the corresponding React-managed DOM refs.\n */\nexport function createReactAdapter(elementRefs: React.RefObject<Map<string, any>>) {\n const warnedSelectors = new Set<string>();\n\n const adapter: PxPlatformAdapter = {\n isConnected: () => {\n return true;\n },\n setAttribute: (id, attrName, value) => {\n\n attrName = camelCaseToKebabWordIfNeeded(attrName);\n\n const selector = getSelector(id);\n\n const element = elementRefs.current.get(id);\n\n if (!element && !warnedSelectors.has(selector)) {\n warnedSelectors.add(selector);\n console.warn('setAttribute: No elements found for selector \"' + selector + '\"');\n console.warn(elementRefs.current);\n }\n\n if (element) {\n element.setAttribute(attrName, value);\n if (STYLE_ATTR_NAMES.has(attrName)) {\n (element as HTMLElement).style[attrName as any] = value;\n }\n }\n },\n };\n return adapter;\n}\n\nexport function getSelector(id: string) {\n return '#' + id;\n}\n\n// FIXME: add model validation (e.g. isElementFileJson check)\n\n\n// -- Inner component (memoised, never re-renders) ---------------------------\n\nconst PixodeskSvgAnimatorImpl: FC<PixodeskSvgAnimatorImplProps> = ({\n className, style, doc, compMode, apiHolderRef, callbacksRef\n}) => {\n\n doc = generateNewIds(doc);\n\n const elementRefs = useRef(new Map<string, any>());\n\n const renderNode = (node: PxNode | undefined, isRoot = false): ReactElement | null => {\n if (!node) return null;\n\n const { type, animate, meta, children, ...props } = node;\n\n const normProps = getNormalizedProps(props);\n\n normProps['ref'] = (domEl: any) => {\n if (node['id']) elementRefs.current.set(node['id'], domEl);\n // return () => {};\n };\n\n // Apply the component's className/style props to the root SVG element.\n if (isRoot) {\n if (className) {\n normProps['className'] = normProps['className']\n ? normProps['className'] + ' ' + className\n : className;\n }\n if (style) normProps['style'] = style;\n }\n\n return createElement(type, normProps, children?.map(child => renderNode(child)));\n };\n\n const root = doc ? renderNode(doc, true) : null;\n\n // Create the animator once per document and tear it down on unmount.\n useEffect(() => {\n\n // Route lifecycle events through `callbacksRef` so the latest callback\n // props are invoked even though this component never re-renders.\n const cb = (name: keyof PixodeskSvgAnimatorCallbacks, alsoStop = false) => () => {\n callbacksRef.current?.[name]?.();\n if (alsoStop) callbacksRef.current?.onStop?.();\n };\n const callbacks = {\n onPlay: cb('onPlay'),\n onPause: cb('onPause', true),\n onCancel: cb('onCancel', true),\n onFinish: cb('onFinish', true),\n onRemove: cb('onRemove', true),\n };\n\n let api: PxAnimatorAPI | undefined = createAnimator({ data: doc, adapter: createReactAdapter(elementRefs), callbacks });\n apiHolderRef.current = api;\n\n return () => {\n api?.destroy();\n apiHolderRef.current = null;\n };\n }, [doc, apiHolderRef, callbacksRef]);\n\n return root;\n};\n\n/** Memoised wrapper โ the inner component never re-renders (props are stable by design). */\nconst PixodeskSvgAnimatorImplOnce = React.memo(\n PixodeskSvgAnimatorImpl,\n () => true // Don't re-render\n);\n\n\n// -- Main public component --------------------------------------------------\n\n/**\n * React component for rendering and controlling Pixodesk SVG animations.\n *\n * Supports four mutually-exclusive control modes:\n *\n * 1. **Autoplay** โ uses triggers from the animation document.\n * ```tsx\n * <PixodeskSvgAnimator doc={animation} autoplay />\n * ```\n *\n * 2. **Declarative play/pause** โ controlled via boolean props.\n * ```tsx\n * <PixodeskSvgAnimator doc={animation} play pause={false} />\n * ```\n *\n * 3. **Imperative** โ exposes a ref-based API for full programmatic control.\n * ```tsx\n * const api = useRef<ReactAnimatorApi>(null);\n * <PixodeskSvgAnimator doc={animation} apiRef={api} />\n * <button onClick={() => api.current?.play()}>Play</button>\n * ```\n *\n * 4. **Controlled time** โ renders a single frame at a given time.\n * ```tsx\n * <PixodeskSvgAnimator doc={animation} time={0.5} />\n * <PixodeskSvgAnimator doc={animation} timeMs={500} />\n * ```\n */\nconst PixodeskSvgAnimator: FC<PixodeskSvgAnimatorProps> = ({\n className, style,\n doc, autoplay, play, pause, time, timeMs, apiRef,\n\n // Overrides\n mode, delay, fill, iterations, duration, direction, frameRate,\n\n startOn, outAction, scrollIntoViewThreshold,\n\n onPlay, onStop, onPause, onCancel, onFinish, onRemove\n}) => {\n\n // Determine which control mode is active.\n let compMode = PixodeskSvgAnimatorCompMode.static;\n if (apiRef) {\n compMode = PixodeskSvgAnimatorCompMode.imperativeApi;\n } else if (autoplay) {\n compMode = PixodeskSvgAnimatorCompMode.autoplay;\n } else if (time !== undefined || timeMs !== undefined) {\n compMode = PixodeskSvgAnimatorCompMode.fixedTime;\n } else if (play !== undefined || pause !== undefined) {\n compMode = PixodeskSvgAnimatorCompMode.play;\n }\n\n // In non-autoplay modes, override the document trigger to 'programmatic'\n // so the component can manage playback itself.\n if (compMode !== PixodeskSvgAnimatorCompMode.autoplay) {\n const startOn = doc.animator?.trigger?.startOn;\n if (\n startOn &&\n startOn !== 'programmatic' // FIXME: use enum\n ) {\n doc = {\n ...doc,\n animator: {\n ...doc.animator,\n trigger: {\n ...doc.animator?.trigger,\n startOn: 'programmatic' // FIXME: use enum\n }\n }\n };\n }\n }\n\n // Apply timing overrides from props onto the document config.\n if (\n mode !== undefined ||\n duration !== undefined ||\n delay !== undefined ||\n iterations !== undefined ||\n fill !== undefined ||\n direction !== undefined ||\n frameRate !== undefined\n ) {\n const animator = doc.animator || {};\n doc = {\n ...doc,\n animator: {\n ...animator,\n mode: mode !== undefined ? mode : animator.mode,\n duration: duration !== undefined ? duration : animator.duration,\n delay: delay !== undefined ? delay : animator.delay,\n iterations: iterations !== undefined ? iterations : animator.iterations,\n fill: fill !== undefined ? fill : animator.fill,\n direction: direction !== undefined ? direction : animator.direction,\n frameRate: frameRate !== undefined ? frameRate : animator.frameRate\n }\n };\n }\n\n // Apply trigger overrides from props.\n if (\n startOn !== undefined ||\n outAction !== undefined ||\n scrollIntoViewThreshold !== undefined\n ) {\n const trigger: PxTrigger = doc.animator?.trigger || {};\n doc = {\n ...doc,\n animator: {\n ...doc.animator,\n trigger: {\n ...trigger,\n startOn: startOn !== undefined ? startOn : trigger.startOn,\n outAction: outAction !== undefined ? outAction : trigger.outAction,\n scrollIntoViewThreshold: scrollIntoViewThreshold !== undefined ? scrollIntoViewThreshold : trigger.scrollIntoViewThreshold\n }\n }\n };\n }\n\n // Controlled-time mode: compute the absolute seek target. `time` is a\n // fraction (0โ1) of the WHOLE timeline (duration ร iterations); `timeMs`\n // is absolute milliseconds. The seek is applied through the animator API\n // (setCurrentTime) below โ the document itself stays stable, so scrubbing\n // does NOT recreate the animator.\n let seekMs: number | undefined;\n if (compMode === PixodeskSvgAnimatorCompMode.fixedTime) {\n const animator = doc.animator || {};\n if (time !== undefined) {\n const iterationsValue = iterations ?? animator.iterations;\n const iterationsCount = typeof iterationsValue === 'number' && iterationsValue >= 1 ? iterationsValue : 1;\n const singleDuration = duration ?? animator.duration ?? 1000; // engine default duration\n seekMs = time * singleDuration * iterationsCount;\n }\n if (timeMs !== undefined) seekMs = timeMs;\n }\n\n const apiHolderRef = useRef<PxAnimatorAPI | null>(null);\n\n // Keep the latest callback props readable by the memoised inner component.\n const callbacksRef = useRef<PixodeskSvgAnimatorCallbacks>({});\n callbacksRef.current = { onPlay, onStop, onPause, onCancel, onFinish, onRemove };\n\n // Expose the imperative API via the consumer-provided ref.\n useImperativeHandle(apiRef, () => {\n return {\n isPlaying: () => apiHolderRef.current?.isPlaying() || false,\n play: () => apiHolderRef.current?.play(),\n pause: () => apiHolderRef.current?.pause(),\n cancel: () => apiHolderRef.current?.cancel(),\n finish: () => apiHolderRef.current?.finish(),\n setPlaybackRate: (rate: number) => apiHolderRef.current?.setPlaybackRate(rate),\n getCurrentTime: () => apiHolderRef.current?.getCurrentTime() ?? null,\n setCurrentTime: (time: number) => apiHolderRef.current?.setCurrentTime(time),\n };\n }, []);\n\n // Increment key when the document, mode, or root styling changes to force\n // a full remount (the inner component is memoised and never re-renders).\n const key = useDepsVersion(compMode, doc, className, style);\n\n // Sync declarative play/pause props with the animator. Re-runs after a\n // remount (`key` in deps) so a doc swap re-applies the current state.\n useEffect(() => {\n\n if (compMode === PixodeskSvgAnimatorCompMode.play) {\n if (play && !pause) {\n apiHolderRef.current?.play();\n } else if (pause) {\n apiHolderRef.current?.pause();\n } else if (play === false) {\n // explicit play=false โ jump to the end state\n apiHolderRef.current?.finish();\n } else {\n // pause-only usage: pause switched off โ resume\n apiHolderRef.current?.play();\n }\n }\n\n return () => {\n if (compMode === PixodeskSvgAnimatorCompMode.play) {\n // Intentionally read at cleanup time (NOT snapshotted at effect\n // time): the inner component nulls the ref when it destroys the\n // animator, so this pauses only a still-live instance. A\n // snapshot would call pause() on a destroyed animator and emit\n // a spurious onPause after teardown.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n apiHolderRef.current?.pause();\n }\n };\n }, [compMode, play, pause, key]);\n\n // Controlled-time mode: seek through the animator API. Scrubbing `time` /\n // `timeMs` only re-runs this effect โ the animator is NOT recreated.\n useEffect(() => {\n if (compMode === PixodeskSvgAnimatorCompMode.fixedTime && seekMs !== undefined) {\n apiHolderRef.current?.setCurrentTime(seekMs);\n apiHolderRef.current?.pause();\n }\n }, [compMode, seekMs, key]);\n\n return (\n <PixodeskSvgAnimatorImplOnce\n key={key}\n className={className}\n style={style}\n compMode={compMode}\n doc={doc}\n apiHolderRef={apiHolderRef}\n callbacksRef={callbacksRef}\n />\n );\n};\n\nexport default PixodeskSvgAnimator;","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport { useRef } from 'react';\n\nexport function deepEquals(a: any, b: any): boolean {\n // Handle primitives and null\n if (a === b) return true;\n if (a == null || b == null) return false;\n if (typeof a !== 'object' || typeof b !== 'object') return false;\n\n // Check for cycles using WeakSet\n const seen = new WeakSet();\n\n function equals(x: any, y: any): boolean {\n // Primitive/null check\n if (x === y) return true;\n if (x == null || y == null) return false;\n if (typeof x !== 'object' || typeof y !== 'object') return false;\n\n // Cycle detection\n if (seen.has(x)) {\n throw new Error('Circular reference detected');\n }\n seen.add(x);\n\n // Array check\n const xIsArray = Array.isArray(x);\n const yIsArray = Array.isArray(y);\n if (xIsArray !== yIsArray) return false;\n\n if (xIsArray) {\n if (x.length !== y.length) return false;\n for (let i = 0; i < x.length; i++) {\n if (!equals(x[i], y[i])) return false;\n }\n return true;\n }\n\n // Object check\n const xKeys = Object.keys(x);\n const yKeys = Object.keys(y);\n if (xKeys.length !== yKeys.length) return false;\n\n for (const key of xKeys) {\n if (!yKeys.includes(key)) return false;\n if (!equals(x[key], y[key])) return false;\n }\n\n return true;\n }\n\n return equals(a, b);\n}\n\n\n/**\n * Compares two arrays of dependencies using identity check first (===),\n * falling back to deep equality if references differ.\n * This is optimized for immutable data that may be recreated with same values.\n */\nfunction areDepsEqual(prevDeps: Array<any>, nextDeps: Array<any>): boolean {\n if (prevDeps.length !== nextDeps.length) return false;\n\n for (let i = 0; i < prevDeps.length; i++) {\n // Fast path: same reference (common for immutable data)\n if (prevDeps[i] === nextDeps[i]) continue;\n\n // Slow path: different reference but possibly same content\n if (!deepEquals(prevDeps[i], nextDeps[i])) return false;\n }\n\n return true;\n}\n\n/**\n * Returns a stable numeric version ID that only increments when dependencies\n * actually change (by reference or deep equality).\n */\nexport function useDepsVersion(...deps: Array<any>): number {\n const cache = useRef<{\n versionId: number;\n deps: Array<any>;\n } | null>(null);\n\n // First render - initialize with version 1\n if (cache.current === null) {\n cache.current = {\n versionId: 1,\n deps: deps\n };\n return 1;\n }\n\n // Check if any dependency changed (by reference or value)\n if (!areDepsEqual(cache.current.deps, deps)) {\n // Dependencies changed - increment version and update cache\n cache.current = {\n versionId: cache.current.versionId + 1,\n deps: deps\n };\n }\n\n return cache.current.versionId;\n}","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport { OutAction, StartOn } from \"@pixodesk/svg-animator-web\";\nimport { CSSProperties, FC, ReactNode, useEffect, useRef, useState } from \"react\";\n\n\ntype AnimState = 'idle' | 'paused' | 'playing';\n\n/**\n * Controls playback of a SVG+CSS animated files by toggling class names on a wrapper div.\n *\n * Intended for use with SVG files exported from the Pixodesk editor using the\n * **CSS Keyframes** (no `<script>` tag). Import the SVG as a React component\n * via SVGR (`vite-plugin-svgr` or `@svgr/webpack`) and pass it as `children`:\n *\n * ```tsx\n * import AnimationSvg from './animation.svg?react'; // SVGR\n *\n * <PixodeskSvgCssAnimator startOn=\"mouseOver\" outAction=\"pause\">\n * <AnimationSvg />\n * </PixodeskSvgCssAnimator>\n * ```\n *\n * The wrapper div carries one of three animation states via CSS class names:\n * - *(no class)* โ idle, animation not started\n * - `px-anim-enabled` โ started but paused\n * - `px-anim-enabled px-anim-playing` โ actively playing\n *\n * @param children - The SVGR-imported SVG component to animate.\n * @param startOn - What triggers the animation to start:\n * - `'load'` โ plays immediately on mount (default)\n * - `'mouseOver'` โ plays on hover\n * - `'click'` โ plays on click, toggles on second click\n * - `'scrollIntoView'` โ plays when the element enters the viewport\n * @param outAction - What happens when the trigger ends (hover/scroll out, second click):\n * - `'continue'` โ keeps playing (default)\n * - `'pause'` โ pauses at the current frame\n * - `'reset'` โ resets to the beginning\n * @param className - Additional CSS class names to apply to the wrapper div.\n * @param style - Inline styles for the wrapper div (e.g. `{ width: 400, height: 400 }`).\n */\nconst PixodeskSvgCssAnimator: FC<{\n className?: string;\n style?: CSSProperties;\n children: ReactNode;\n startOn?: StartOn;\n outAction?: OutAction;\n}> = ({ className, style, children, startOn = 'load', outAction = 'continue' }) => {\n\n const [state, setState] = useState<AnimState>(startOn === 'load' ? 'playing' : 'idle');\n\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (startOn !== 'scrollIntoView') return;\n const el = ref.current;\n if (!el) return;\n const outState: AnimState = (\n outAction === 'reset' ? 'idle' :\n outAction === 'pause' ? 'paused' : 'playing'\n );\n const observer = new IntersectionObserver(\n ([entry]) => setState(entry.isIntersecting ? 'playing' : outState),\n { threshold: 0.1 }\n );\n observer.observe(el);\n return () => observer.disconnect();\n }, [startOn, outAction]);\n\n const goOut = () => setState(\n outAction === 'reset' ? 'idle' :\n outAction === 'pause' ? 'paused' : 'playing'\n );\n\n const handlers =\n startOn === 'mouseOver' ? { onMouseEnter: () => setState('playing'), onMouseLeave: goOut } :\n startOn === 'click' ? { onClick: () => state === 'playing' ? goOut() : setState('playing') } :\n {};\n\n return (\n <div\n ref={ref}\n className={[\n className,\n state === 'playing' ? 'px-anim-enabled px-anim-playing' :\n state === 'paused' ? 'px-anim-enabled' : ''\n ].filter(Boolean).join(' ')}\n style={style}\n {...handlers}\n >\n {children}\n </div>\n );\n};\nexport default PixodeskSvgCssAnimator;"],"mappings":"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","names":["import_react","React","startOn","time","import_react","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/PixodeskSvgAnimator.tsx","../src/Utils.ts","../src/PixodeskSvgCssAnimator.tsx"],"sourcesContent":["/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport PixodeskSvgAnimator from './PixodeskSvgAnimator';\nexport type { PixodeskSvgAnimatorCallbacks, PixodeskSvgAnimatorProps, ReactAnimatorApi } from './PixodeskSvgAnimator';\nexport { PixodeskSvgAnimator };\n\nimport PixodeskSvgCssAnimator from './PixodeskSvgCssAnimator';\nexport { PixodeskSvgCssAnimator };\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport type { JsMode, OutAction, PxAnimatedSvgDocument, PxAnimatorAPI, PxNode, PxPlatformAdapter, PxTrigger, StartOn } from '@pixodesk/svg-animator-web';\nimport { camelCaseToKebabWordIfNeeded, createAnimator, FillMode, generateNewIds, getNormalizedProps, STYLE_ATTR_NAMES } from '@pixodesk/svg-animator-web';\nimport type { CSSProperties, FC, ReactElement } from 'react';\nimport React, { createElement, useEffect, useImperativeHandle, useRef } from 'react';\nimport { useDepsVersion } from './Utils';\n\n\n// -- Public types -----------------------------------------------------------\n\nexport interface ReactAnimatorApi {\n /** Returns true if the animation is currently running. */\n isPlaying(): boolean;\n\n /** Starts or resumes the animation. */\n play(): void;\n\n /** Pauses the animation at its current state. */\n pause(): void;\n\n /** Stops the animation and resets it to its initial state. */\n cancel(): void;\n\n /** Jumps to the end of the animation and holds the final state. */\n finish(): void;\n\n /** Changes the speed of the animation. 1 is normal, 2 is double, -1 is reverse. */\n setPlaybackRate(rate: number): void;\n\n /** Returns the current playback time in milliseconds. */\n getCurrentTime(): number | null;\n\n /** Jumps to a specific time (in milliseconds) in the animation. */\n setCurrentTime(time: number): void;\n}\n\nexport interface PixodeskSvgAnimatorImplProps {\n className?: string;\n style?: CSSProperties;\n doc: PxAnimatedSvgDocument;\n compMode: PixodeskSvgAnimatorCompMode;\n\n /** Imperative API handle populated by the inner component. */\n apiHolderRef: React.RefObject<PxAnimatorAPI | null>;\n\n /**\n * Latest lifecycle callbacks, read at invocation time so the memoised\n * inner component always calls the current props even though it never\n * re-renders.\n */\n callbacksRef: React.RefObject<PixodeskSvgAnimatorCallbacks>;\n}\n\n/** Lifecycle callback props (subset of {@link PixodeskSvgAnimatorProps}). */\nexport interface PixodeskSvgAnimatorCallbacks {\n onPlay?: () => void;\n onStop?: () => void;\n onPause?: () => void;\n onCancel?: () => void;\n onFinish?: () => void;\n onRemove?: () => void;\n}\n\nexport interface PixodeskSvgAnimatorProps {\n\n className?: string;\n\n style?: CSSProperties;\n\n // -- Source ---------------------------------------------------------------\n\n /**\n * The animation document to render.\n *\n * TODO: Accept PxFileConfig | string to support URL-based loading, e.g.\n * <PixodeskSvgAnimator doc=\"/animation.json\" />\n */\n doc: PxAnimatedSvgDocument;\n\n // -- Rendering mode ------------------------------------------------------\n\n /** Forces a specific rendering engine. Defaults to 'auto'. */\n mode?: JsMode;\n\n // -- Timing overrides ----------------------------------------------------\n\n /** Delay before the animation starts, in milliseconds. */\n delay?: number;\n\n /** Defines the element's style when the animation is not active. */\n fill?: FillMode;\n\n /** Number of iterations, or 'infinite' for endless looping. */\n iterations?: number | 'infinite';\n\n /** Duration of a single iteration in milliseconds. */\n duration?: number;\n\n /** Playback direction. */\n direction?: PlaybackDirection;\n\n /** Target frame rate (frames per second). */\n frameRate?: number;\n\n // -- Trigger overrides ---------------------------------------------------\n\n /** The event that starts the animation. When omitted, uses the value from the document config. */\n startOn?: StartOn;\n\n /** Behaviour when the trigger condition ends (e.g. mouse-out, second click). */\n outAction?: OutAction;\n\n /** Visibility ratio (0.0โ1.0) required to trigger a scrollIntoView animation. Defaults to 0.5. */\n scrollIntoViewThreshold?: number;\n\n // -- Declarative control -------------------------------------------------\n\n /** When true, uses triggers defined in the animation document. */\n autoplay?: boolean;\n\n /**\n * Starts the animation unconditionally, ignoring document triggers.\n * Equivalent to `startOn=\"load\"` / `outAction=\"continue\"`.\n */\n play?: boolean;\n\n /** Pauses current playback. Only meaningful when `play` or `autoplay` is set. */\n pause?: boolean;\n\n // -- Imperative control --------------------------------------------------\n\n /** Ref populated with the imperative playback API. */\n apiRef?: React.RefObject<ReactAnimatorApi | null>;\n\n // -- Controlled (external) time ------------------------------------------\n\n /** Seek to a specific point in the animation, as a fraction (0โ1) of the whole timeline (duration ร iterations). */\n progress?: number;\n\n /** Seek to a specific point in the animation (milliseconds). */\n time?: number;\n\n // -- Callbacks -----------------------------------------------------------\n\n /** Called when the animation starts or resumes. */\n onPlay?: () => void;\n\n /** Called when the animation stops for any reason (pause / cancel / finish / removal). */\n onStop?: () => void;\n\n /** Called when the animation is paused. */\n onPause?: () => void;\n\n /** Called when the animation is cancelled. */\n onCancel?: () => void;\n\n /** Called when the animation finishes naturally. */\n onFinish?: () => void;\n\n /** Called when the animation is removed. */\n onRemove?: () => void;\n}\n\n\n// -- Internal types ---------------------------------------------------------\n\nenum PixodeskSvgAnimatorCompMode {\n static = 'static',\n autoplay = 'autoplay',\n play = 'play',\n imperativeApi = 'imperativeApi',\n fixedTime = 'fixedTime'\n}\n\n\n// -- React โ Animator bridge ------------------------------------------------\n\n/**\n * Creates a platform adapter that routes animator attribute updates\n * to the corresponding React-managed DOM refs.\n */\nexport function createReactAdapter(elementRefs: React.RefObject<Map<string, any>>) {\n const warnedSelectors = new Set<string>();\n\n const adapter: PxPlatformAdapter = {\n isConnected: () => {\n return true;\n },\n setAttribute: (id, attrName, value) => {\n\n attrName = camelCaseToKebabWordIfNeeded(attrName);\n\n const selector = getSelector(id);\n\n const element = elementRefs.current.get(id);\n\n if (!element && !warnedSelectors.has(selector)) {\n warnedSelectors.add(selector);\n console.warn('setAttribute: No elements found for selector \"' + selector + '\"');\n console.warn(elementRefs.current);\n }\n\n if (element) {\n element.setAttribute(attrName, value);\n if (STYLE_ATTR_NAMES.has(attrName)) {\n (element as HTMLElement).style[attrName as any] = value;\n }\n }\n },\n };\n return adapter;\n}\n\nexport function getSelector(id: string) {\n return '#' + id;\n}\n\n// FIXME: add model validation (e.g. isElementFileJson check)\n\n\n// -- Inner component (memoised, never re-renders) ---------------------------\n\nconst PixodeskSvgAnimatorImpl: FC<PixodeskSvgAnimatorImplProps> = ({\n className, style, doc, compMode, apiHolderRef, callbacksRef\n}) => {\n\n doc = generateNewIds(doc);\n\n const elementRefs = useRef(new Map<string, any>());\n\n const renderNode = (node: PxNode | undefined, isRoot = false, key?: React.Key): ReactElement | null => {\n if (!node) return null;\n\n const { type, animate, meta, children, ...props } = node;\n\n const normProps = getNormalizedProps(props);\n if (key !== undefined) normProps['key'] = key;\n\n normProps['ref'] = (domEl: any) => {\n if (node['id']) elementRefs.current.set(node['id'], domEl);\n // return () => {};\n };\n\n // Apply the component's className/style props to the root SVG element.\n if (isRoot) {\n if (className) {\n normProps['className'] = normProps['className']\n ? normProps['className'] + ' ' + className\n : className;\n }\n if (style) normProps['style'] = style;\n }\n\n return createElement(type, normProps, children?.map((child, i) => renderNode(child, false, child['id'] ?? i)));\n };\n\n const root = doc ? renderNode(doc, true) : null;\n\n // Create the animator once per document and tear it down on unmount.\n useEffect(() => {\n\n // Route lifecycle events through `callbacksRef` so the latest callback\n // props are invoked even though this component never re-renders.\n const cb = (name: keyof PixodeskSvgAnimatorCallbacks, alsoStop = false) => () => {\n callbacksRef.current?.[name]?.();\n if (alsoStop) callbacksRef.current?.onStop?.();\n };\n const callbacks = {\n onPlay: cb('onPlay'),\n onPause: cb('onPause', true),\n onCancel: cb('onCancel', true),\n onFinish: cb('onFinish', true),\n onRemove: cb('onRemove', true),\n };\n\n let api: PxAnimatorAPI | undefined = createAnimator({ data: doc, adapter: createReactAdapter(elementRefs), callbacks });\n apiHolderRef.current = api;\n\n return () => {\n api?.destroy();\n apiHolderRef.current = null;\n };\n }, [doc, apiHolderRef, callbacksRef]);\n\n return root;\n};\n\n/** Memoised wrapper โ the inner component never re-renders (props are stable by design). */\nconst PixodeskSvgAnimatorImplOnce = React.memo(\n PixodeskSvgAnimatorImpl,\n () => true // Don't re-render\n);\n\n\n// -- Main public component --------------------------------------------------\n\n/**\n * React component for rendering and controlling Pixodesk SVG animations.\n *\n * Supports four mutually-exclusive control modes:\n *\n * 1. **Autoplay** โ uses triggers from the animation document.\n * ```tsx\n * <PixodeskSvgAnimator doc={animation} autoplay />\n * ```\n *\n * 2. **Declarative play/pause** โ controlled via boolean props.\n * ```tsx\n * <PixodeskSvgAnimator doc={animation} play pause={false} />\n * ```\n *\n * 3. **Imperative** โ exposes a ref-based API for full programmatic control.\n * ```tsx\n * const api = useRef<ReactAnimatorApi>(null);\n * <PixodeskSvgAnimator doc={animation} apiRef={api} />\n * <button onClick={() => api.current?.play()}>Play</button>\n * ```\n *\n * 4. **Controlled time** โ renders a single frame at a given time.\n * ```tsx\n * <PixodeskSvgAnimator doc={animation} progress={0.5} />\n * <PixodeskSvgAnimator doc={animation} time={500} />\n * ```\n */\nconst PixodeskSvgAnimator: FC<PixodeskSvgAnimatorProps> = ({\n className, style,\n doc, autoplay, play, pause, progress, time, apiRef,\n\n // Overrides\n mode, delay, fill, iterations, duration, direction, frameRate,\n\n startOn, outAction, scrollIntoViewThreshold,\n\n onPlay, onStop, onPause, onCancel, onFinish, onRemove\n}) => {\n\n // Determine which control mode is active.\n let compMode = PixodeskSvgAnimatorCompMode.static;\n if (apiRef) {\n compMode = PixodeskSvgAnimatorCompMode.imperativeApi;\n } else if (autoplay) {\n compMode = PixodeskSvgAnimatorCompMode.autoplay;\n } else if (progress !== undefined || time !== undefined) {\n compMode = PixodeskSvgAnimatorCompMode.fixedTime;\n } else if (play !== undefined || pause !== undefined) {\n compMode = PixodeskSvgAnimatorCompMode.play;\n }\n\n // In non-autoplay modes, override the document trigger to 'programmatic'\n // so the component can manage playback itself.\n if (compMode !== PixodeskSvgAnimatorCompMode.autoplay) {\n const startOn = doc.animator?.trigger?.startOn;\n if (\n startOn &&\n startOn !== 'programmatic' // FIXME: use enum\n ) {\n doc = {\n ...doc,\n animator: {\n ...doc.animator,\n trigger: {\n ...doc.animator?.trigger,\n startOn: 'programmatic' // FIXME: use enum\n }\n }\n };\n }\n }\n\n // Apply timing overrides from props onto the document config.\n if (\n mode !== undefined ||\n duration !== undefined ||\n delay !== undefined ||\n iterations !== undefined ||\n fill !== undefined ||\n direction !== undefined ||\n frameRate !== undefined\n ) {\n const animator = doc.animator || {};\n doc = {\n ...doc,\n animator: {\n ...animator,\n mode: mode !== undefined ? mode : animator.mode,\n duration: duration !== undefined ? duration : animator.duration,\n delay: delay !== undefined ? delay : animator.delay,\n iterations: iterations !== undefined ? iterations : animator.iterations,\n fill: fill !== undefined ? fill : animator.fill,\n direction: direction !== undefined ? direction : animator.direction,\n frameRate: frameRate !== undefined ? frameRate : animator.frameRate\n }\n };\n }\n\n // Apply trigger overrides from props.\n if (\n startOn !== undefined ||\n outAction !== undefined ||\n scrollIntoViewThreshold !== undefined\n ) {\n const trigger: PxTrigger = doc.animator?.trigger || {};\n doc = {\n ...doc,\n animator: {\n ...doc.animator,\n trigger: {\n ...trigger,\n startOn: startOn !== undefined ? startOn : trigger.startOn,\n outAction: outAction !== undefined ? outAction : trigger.outAction,\n scrollIntoViewThreshold: scrollIntoViewThreshold !== undefined ? scrollIntoViewThreshold : trigger.scrollIntoViewThreshold\n }\n }\n };\n }\n\n // Controlled-time mode: compute the absolute seek target. `progress` is a\n // fraction (0โ1) of the WHOLE timeline (duration ร iterations); `time`\n // is absolute milliseconds. The seek is applied through the animator API\n // (setCurrentTime) below โ the document itself stays stable, so scrubbing\n // does NOT recreate the animator.\n let seekMs: number | undefined;\n if (compMode === PixodeskSvgAnimatorCompMode.fixedTime) {\n const animator = doc.animator || {};\n if (progress !== undefined) {\n const iterationsValue = iterations ?? animator.iterations;\n const iterationsCount = typeof iterationsValue === 'number' && iterationsValue >= 1 ? iterationsValue : 1;\n const singleDuration = duration ?? animator.duration ?? 1000; // engine default duration\n seekMs = progress * singleDuration * iterationsCount;\n }\n if (time !== undefined) seekMs = time;\n }\n\n const apiHolderRef = useRef<PxAnimatorAPI | null>(null);\n\n // Keep the latest callback props readable by the memoised inner component.\n const callbacksRef = useRef<PixodeskSvgAnimatorCallbacks>({});\n callbacksRef.current = { onPlay, onStop, onPause, onCancel, onFinish, onRemove };\n\n // Expose the imperative API via the consumer-provided ref.\n useImperativeHandle(apiRef, () => {\n return {\n isPlaying: () => apiHolderRef.current?.isPlaying() || false,\n play: () => apiHolderRef.current?.play(),\n pause: () => apiHolderRef.current?.pause(),\n cancel: () => apiHolderRef.current?.cancel(),\n finish: () => apiHolderRef.current?.finish(),\n setPlaybackRate: (rate: number) => apiHolderRef.current?.setPlaybackRate(rate),\n getCurrentTime: () => apiHolderRef.current?.getCurrentTime() ?? null,\n setCurrentTime: (time: number) => apiHolderRef.current?.setCurrentTime(time),\n };\n }, []);\n\n // Increment key when the document, mode, or root styling changes to force\n // a full remount (the inner component is memoised and never re-renders).\n const key = useDepsVersion(compMode, doc, className, style);\n\n // Sync declarative play/pause props with the animator. Re-runs after a\n // remount (`key` in deps) so a doc swap re-applies the current state.\n useEffect(() => {\n\n if (compMode === PixodeskSvgAnimatorCompMode.play) {\n if (play && !pause) {\n apiHolderRef.current?.play();\n } else if (pause) {\n apiHolderRef.current?.pause();\n } else if (play === false) {\n // explicit play=false โ jump to the end state\n apiHolderRef.current?.finish();\n } else {\n // pause-only usage: pause switched off โ resume\n apiHolderRef.current?.play();\n }\n }\n\n return () => {\n if (compMode === PixodeskSvgAnimatorCompMode.play) {\n // Intentionally read at cleanup time (NOT snapshotted at effect\n // time): the inner component nulls the ref when it destroys the\n // animator, so this pauses only a still-live instance. A\n // snapshot would call pause() on a destroyed animator and emit\n // a spurious onPause after teardown.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n apiHolderRef.current?.pause();\n }\n };\n }, [compMode, play, pause, key]);\n\n // Controlled-time mode: seek through the animator API. Scrubbing `progress` /\n // `time` only re-runs this effect โ the animator is NOT recreated.\n useEffect(() => {\n if (compMode === PixodeskSvgAnimatorCompMode.fixedTime && seekMs !== undefined) {\n apiHolderRef.current?.setCurrentTime(seekMs);\n apiHolderRef.current?.pause();\n }\n }, [compMode, seekMs, key]);\n\n return (\n <PixodeskSvgAnimatorImplOnce\n key={key}\n className={className}\n style={style}\n compMode={compMode}\n doc={doc}\n apiHolderRef={apiHolderRef}\n callbacksRef={callbacksRef}\n />\n );\n};\n\nexport default PixodeskSvgAnimator;","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport { useRef } from 'react';\n\nexport function deepEquals(a: any, b: any): boolean {\n // Handle primitives and null\n if (a === b) return true;\n if (a == null || b == null) return false;\n if (typeof a !== 'object' || typeof b !== 'object') return false;\n\n // Check for cycles using WeakSet\n const seen = new WeakSet();\n\n function equals(x: any, y: any): boolean {\n // Primitive/null check\n if (x === y) return true;\n if (x == null || y == null) return false;\n if (typeof x !== 'object' || typeof y !== 'object') return false;\n\n // Cycle detection\n if (seen.has(x)) {\n throw new Error('Circular reference detected');\n }\n seen.add(x);\n\n // Array check\n const xIsArray = Array.isArray(x);\n const yIsArray = Array.isArray(y);\n if (xIsArray !== yIsArray) return false;\n\n if (xIsArray) {\n if (x.length !== y.length) return false;\n for (let i = 0; i < x.length; i++) {\n if (!equals(x[i], y[i])) return false;\n }\n return true;\n }\n\n // Object check\n const xKeys = Object.keys(x);\n const yKeys = Object.keys(y);\n if (xKeys.length !== yKeys.length) return false;\n\n for (const key of xKeys) {\n if (!yKeys.includes(key)) return false;\n if (!equals(x[key], y[key])) return false;\n }\n\n return true;\n }\n\n return equals(a, b);\n}\n\n\n/**\n * Compares two arrays of dependencies using identity check first (===),\n * falling back to deep equality if references differ.\n * This is optimized for immutable data that may be recreated with same values.\n */\nfunction areDepsEqual(prevDeps: Array<any>, nextDeps: Array<any>): boolean {\n if (prevDeps.length !== nextDeps.length) return false;\n\n for (let i = 0; i < prevDeps.length; i++) {\n // Fast path: same reference (common for immutable data)\n if (prevDeps[i] === nextDeps[i]) continue;\n\n // Slow path: different reference but possibly same content\n if (!deepEquals(prevDeps[i], nextDeps[i])) return false;\n }\n\n return true;\n}\n\n/**\n * Returns a stable numeric version ID that only increments when dependencies\n * actually change (by reference or deep equality).\n */\nexport function useDepsVersion(...deps: Array<any>): number {\n const cache = useRef<{\n versionId: number;\n deps: Array<any>;\n } | null>(null);\n\n // First render - initialize with version 1\n if (cache.current === null) {\n cache.current = {\n versionId: 1,\n deps: deps\n };\n return 1;\n }\n\n // Check if any dependency changed (by reference or value)\n if (!areDepsEqual(cache.current.deps, deps)) {\n // Dependencies changed - increment version and update cache\n cache.current = {\n versionId: cache.current.versionId + 1,\n deps: deps\n };\n }\n\n return cache.current.versionId;\n}","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport { OutAction, StartOn } from \"@pixodesk/svg-animator-web\";\nimport { CSSProperties, FC, ReactNode, useEffect, useRef, useState } from \"react\";\n\n\ntype AnimState = 'idle' | 'paused' | 'playing';\n\n/**\n * Controls playback of a SVG+CSS animated files by toggling class names on a wrapper div.\n *\n * Intended for use with SVG files exported from the Pixodesk editor using the\n * **CSS Keyframes** (no `<script>` tag). Import the SVG as a React component\n * via SVGR (`vite-plugin-svgr` or `@svgr/webpack`) and pass it as `children`:\n *\n * ```tsx\n * import AnimationSvg from './animation.svg?react'; // SVGR\n *\n * <PixodeskSvgCssAnimator startOn=\"mouseOver\" outAction=\"pause\">\n * <AnimationSvg />\n * </PixodeskSvgCssAnimator>\n * ```\n *\n * The wrapper div carries one of three animation states via CSS class names:\n * - *(no class)* โ idle, animation not started\n * - `px-anim-enabled` โ started but paused\n * - `px-anim-enabled px-anim-playing` โ actively playing\n *\n * @param children - The SVGR-imported SVG component to animate.\n * @param startOn - What triggers the animation to start:\n * - `'load'` โ plays immediately on mount (default)\n * - `'mouseOver'` โ plays on hover\n * - `'click'` โ plays on click, toggles on second click\n * - `'scrollIntoView'` โ plays when the element enters the viewport\n * @param outAction - What happens when the trigger ends (hover/scroll out, second click):\n * - `'continue'` โ keeps playing (default)\n * - `'pause'` โ pauses at the current frame\n * - `'reset'` โ resets to the beginning\n * @param className - Additional CSS class names to apply to the wrapper div.\n * @param style - Inline styles for the wrapper div (e.g. `{ width: 400, height: 400 }`).\n */\nconst PixodeskSvgCssAnimator: FC<{\n className?: string;\n style?: CSSProperties;\n children: ReactNode;\n startOn?: StartOn;\n outAction?: OutAction;\n}> = ({ className, style, children, startOn = 'load', outAction = 'continue' }) => {\n\n const [state, setState] = useState<AnimState>(startOn === 'load' ? 'playing' : 'idle');\n\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (startOn !== 'scrollIntoView') return;\n const el = ref.current;\n if (!el) return;\n const outState: AnimState = (\n outAction === 'reset' ? 'idle' :\n outAction === 'pause' ? 'paused' : 'playing'\n );\n const observer = new IntersectionObserver(\n ([entry]) => setState(entry.isIntersecting ? 'playing' : outState),\n { threshold: 0.1 }\n );\n observer.observe(el);\n return () => observer.disconnect();\n }, [startOn, outAction]);\n\n const goOut = () => setState(\n outAction === 'reset' ? 'idle' :\n outAction === 'pause' ? 'paused' : 'playing'\n );\n\n const handlers =\n startOn === 'mouseOver' ? { onMouseEnter: () => setState('playing'), onMouseLeave: goOut } :\n startOn === 'click' ? { onClick: () => state === 'playing' ? goOut() : setState('playing') } :\n {};\n\n return (\n <div\n ref={ref}\n className={[\n className,\n state === 'playing' ? 'px-anim-enabled px-anim-playing' :\n state === 'paused' ? 'px-anim-enabled' : ''\n ].filter(Boolean).join(' ')}\n style={style}\n {...handlers}\n >\n {children}\n </div>\n );\n};\nexport default PixodeskSvgCssAnimator;"],"mappings":"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","names":["import_react","React","startOn","time","import_react","import_jsx_runtime"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -70,9 +70,9 @@ interface PixodeskSvgAnimatorProps {
|
|
|
70
70
|
/** Ref populated with the imperative playback API. */
|
|
71
71
|
apiRef?: React.RefObject<ReactAnimatorApi | null>;
|
|
72
72
|
/** Seek to a specific point in the animation, as a fraction (0โ1) of the whole timeline (duration ร iterations). */
|
|
73
|
-
|
|
73
|
+
progress?: number;
|
|
74
74
|
/** Seek to a specific point in the animation (milliseconds). */
|
|
75
|
-
|
|
75
|
+
time?: number;
|
|
76
76
|
/** Called when the animation starts or resumes. */
|
|
77
77
|
onPlay?: () => void;
|
|
78
78
|
/** Called when the animation stops for any reason (pause / cancel / finish / removal). */
|
|
@@ -110,8 +110,8 @@ interface PixodeskSvgAnimatorProps {
|
|
|
110
110
|
*
|
|
111
111
|
* 4. **Controlled time** โ renders a single frame at a given time.
|
|
112
112
|
* ```tsx
|
|
113
|
-
* <PixodeskSvgAnimator doc={animation}
|
|
114
|
-
* <PixodeskSvgAnimator doc={animation}
|
|
113
|
+
* <PixodeskSvgAnimator doc={animation} progress={0.5} />
|
|
114
|
+
* <PixodeskSvgAnimator doc={animation} time={500} />
|
|
115
115
|
* ```
|
|
116
116
|
*/
|
|
117
117
|
declare const PixodeskSvgAnimator: FC<PixodeskSvgAnimatorProps>;
|
package/dist/index.d.ts
CHANGED
|
@@ -70,9 +70,9 @@ interface PixodeskSvgAnimatorProps {
|
|
|
70
70
|
/** Ref populated with the imperative playback API. */
|
|
71
71
|
apiRef?: React.RefObject<ReactAnimatorApi | null>;
|
|
72
72
|
/** Seek to a specific point in the animation, as a fraction (0โ1) of the whole timeline (duration ร iterations). */
|
|
73
|
-
|
|
73
|
+
progress?: number;
|
|
74
74
|
/** Seek to a specific point in the animation (milliseconds). */
|
|
75
|
-
|
|
75
|
+
time?: number;
|
|
76
76
|
/** Called when the animation starts or resumes. */
|
|
77
77
|
onPlay?: () => void;
|
|
78
78
|
/** Called when the animation stops for any reason (pause / cancel / finish / removal). */
|
|
@@ -110,8 +110,8 @@ interface PixodeskSvgAnimatorProps {
|
|
|
110
110
|
*
|
|
111
111
|
* 4. **Controlled time** โ renders a single frame at a given time.
|
|
112
112
|
* ```tsx
|
|
113
|
-
* <PixodeskSvgAnimator doc={animation}
|
|
114
|
-
* <PixodeskSvgAnimator doc={animation}
|
|
113
|
+
* <PixodeskSvgAnimator doc={animation} progress={0.5} />
|
|
114
|
+
* <PixodeskSvgAnimator doc={animation} time={500} />
|
|
115
115
|
* ```
|
|
116
116
|
*/
|
|
117
117
|
declare const PixodeskSvgAnimator: FC<PixodeskSvgAnimatorProps>;
|
package/dist/index.js
CHANGED
|
@@ -104,10 +104,11 @@ var PixodeskSvgAnimatorImpl = ({
|
|
|
104
104
|
}) => {
|
|
105
105
|
doc = generateNewIds(doc);
|
|
106
106
|
const elementRefs = useRef2(/* @__PURE__ */ new Map());
|
|
107
|
-
const renderNode = (node, isRoot = false) => {
|
|
107
|
+
const renderNode = (node, isRoot = false, key) => {
|
|
108
108
|
if (!node) return null;
|
|
109
109
|
const { type, animate, meta, children, ...props } = node;
|
|
110
110
|
const normProps = getNormalizedProps(props);
|
|
111
|
+
if (key !== void 0) normProps["key"] = key;
|
|
111
112
|
normProps["ref"] = (domEl) => {
|
|
112
113
|
if (node["id"]) elementRefs.current.set(node["id"], domEl);
|
|
113
114
|
};
|
|
@@ -117,7 +118,7 @@ var PixodeskSvgAnimatorImpl = ({
|
|
|
117
118
|
}
|
|
118
119
|
if (style) normProps["style"] = style;
|
|
119
120
|
}
|
|
120
|
-
return createElement(type, normProps, children?.map((child) => renderNode(child)));
|
|
121
|
+
return createElement(type, normProps, children?.map((child, i) => renderNode(child, false, child["id"] ?? i)));
|
|
121
122
|
};
|
|
122
123
|
const root = doc ? renderNode(doc, true) : null;
|
|
123
124
|
useEffect(() => {
|
|
@@ -153,8 +154,8 @@ var PixodeskSvgAnimator = ({
|
|
|
153
154
|
autoplay,
|
|
154
155
|
play,
|
|
155
156
|
pause,
|
|
157
|
+
progress,
|
|
156
158
|
time,
|
|
157
|
-
timeMs,
|
|
158
159
|
apiRef,
|
|
159
160
|
// Overrides
|
|
160
161
|
mode,
|
|
@@ -179,7 +180,7 @@ var PixodeskSvgAnimator = ({
|
|
|
179
180
|
compMode = "imperativeApi" /* imperativeApi */;
|
|
180
181
|
} else if (autoplay) {
|
|
181
182
|
compMode = "autoplay" /* autoplay */;
|
|
182
|
-
} else if (
|
|
183
|
+
} else if (progress !== void 0 || time !== void 0) {
|
|
183
184
|
compMode = "fixedTime" /* fixedTime */;
|
|
184
185
|
} else if (play !== void 0 || pause !== void 0) {
|
|
185
186
|
compMode = "play" /* play */;
|
|
@@ -234,13 +235,13 @@ var PixodeskSvgAnimator = ({
|
|
|
234
235
|
let seekMs;
|
|
235
236
|
if (compMode === "fixedTime" /* fixedTime */) {
|
|
236
237
|
const animator = doc.animator || {};
|
|
237
|
-
if (
|
|
238
|
+
if (progress !== void 0) {
|
|
238
239
|
const iterationsValue = iterations ?? animator.iterations;
|
|
239
240
|
const iterationsCount = typeof iterationsValue === "number" && iterationsValue >= 1 ? iterationsValue : 1;
|
|
240
241
|
const singleDuration = duration ?? animator.duration ?? 1e3;
|
|
241
|
-
seekMs =
|
|
242
|
+
seekMs = progress * singleDuration * iterationsCount;
|
|
242
243
|
}
|
|
243
|
-
if (
|
|
244
|
+
if (time !== void 0) seekMs = time;
|
|
244
245
|
}
|
|
245
246
|
const apiHolderRef = useRef2(null);
|
|
246
247
|
const callbacksRef = useRef2({});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/PixodeskSvgAnimator.tsx","../src/Utils.ts","../src/PixodeskSvgCssAnimator.tsx"],"sourcesContent":["/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport type { JsMode, OutAction, PxAnimatedSvgDocument, PxAnimatorAPI, PxNode, PxPlatformAdapter, PxTrigger, StartOn } from '@pixodesk/svg-animator-web';\nimport { camelCaseToKebabWordIfNeeded, createAnimator, FillMode, generateNewIds, getNormalizedProps, STYLE_ATTR_NAMES } from '@pixodesk/svg-animator-web';\nimport type { CSSProperties, FC, ReactElement } from 'react';\nimport React, { createElement, useEffect, useImperativeHandle, useRef } from 'react';\nimport { useDepsVersion } from './Utils';\n\n\n// -- Public types -----------------------------------------------------------\n\nexport interface ReactAnimatorApi {\n /** Returns true if the animation is currently running. */\n isPlaying(): boolean;\n\n /** Starts or resumes the animation. */\n play(): void;\n\n /** Pauses the animation at its current state. */\n pause(): void;\n\n /** Stops the animation and resets it to its initial state. */\n cancel(): void;\n\n /** Jumps to the end of the animation and holds the final state. */\n finish(): void;\n\n /** Changes the speed of the animation. 1 is normal, 2 is double, -1 is reverse. */\n setPlaybackRate(rate: number): void;\n\n /** Returns the current playback time in milliseconds. */\n getCurrentTime(): number | null;\n\n /** Jumps to a specific time (in milliseconds) in the animation. */\n setCurrentTime(time: number): void;\n}\n\nexport interface PixodeskSvgAnimatorImplProps {\n className?: string;\n style?: CSSProperties;\n doc: PxAnimatedSvgDocument;\n compMode: PixodeskSvgAnimatorCompMode;\n\n /** Imperative API handle populated by the inner component. */\n apiHolderRef: React.RefObject<PxAnimatorAPI | null>;\n\n /**\n * Latest lifecycle callbacks, read at invocation time so the memoised\n * inner component always calls the current props even though it never\n * re-renders.\n */\n callbacksRef: React.RefObject<PixodeskSvgAnimatorCallbacks>;\n}\n\n/** Lifecycle callback props (subset of {@link PixodeskSvgAnimatorProps}). */\nexport interface PixodeskSvgAnimatorCallbacks {\n onPlay?: () => void;\n onStop?: () => void;\n onPause?: () => void;\n onCancel?: () => void;\n onFinish?: () => void;\n onRemove?: () => void;\n}\n\nexport interface PixodeskSvgAnimatorProps {\n\n className?: string;\n\n style?: CSSProperties;\n\n // -- Source ---------------------------------------------------------------\n\n /**\n * The animation document to render.\n *\n * TODO: Accept PxFileConfig | string to support URL-based loading, e.g.\n * <PixodeskSvgAnimator doc=\"/animation.json\" />\n */\n doc: PxAnimatedSvgDocument;\n\n // -- Rendering mode ------------------------------------------------------\n\n /** Forces a specific rendering engine. Defaults to 'auto'. */\n mode?: JsMode;\n\n // -- Timing overrides ----------------------------------------------------\n\n /** Delay before the animation starts, in milliseconds. */\n delay?: number;\n\n /** Defines the element's style when the animation is not active. */\n fill?: FillMode;\n\n /** Number of iterations, or 'infinite' for endless looping. */\n iterations?: number | 'infinite';\n\n /** Duration of a single iteration in milliseconds. */\n duration?: number;\n\n /** Playback direction. */\n direction?: PlaybackDirection;\n\n /** Target frame rate (frames per second). */\n frameRate?: number;\n\n // -- Trigger overrides ---------------------------------------------------\n\n /** The event that starts the animation. When omitted, uses the value from the document config. */\n startOn?: StartOn;\n\n /** Behaviour when the trigger condition ends (e.g. mouse-out, second click). */\n outAction?: OutAction;\n\n /** Visibility ratio (0.0โ1.0) required to trigger a scrollIntoView animation. Defaults to 0.5. */\n scrollIntoViewThreshold?: number;\n\n // -- Declarative control -------------------------------------------------\n\n /** When true, uses triggers defined in the animation document. */\n autoplay?: boolean;\n\n /**\n * Starts the animation unconditionally, ignoring document triggers.\n * Equivalent to `startOn=\"load\"` / `outAction=\"continue\"`.\n */\n play?: boolean;\n\n /** Pauses current playback. Only meaningful when `play` or `autoplay` is set. */\n pause?: boolean;\n\n // -- Imperative control --------------------------------------------------\n\n /** Ref populated with the imperative playback API. */\n apiRef?: React.RefObject<ReactAnimatorApi | null>;\n\n // -- Controlled (external) time ------------------------------------------\n\n /** Seek to a specific point in the animation, as a fraction (0โ1) of the whole timeline (duration ร iterations). */\n time?: number;\n\n /** Seek to a specific point in the animation (milliseconds). */\n timeMs?: number;\n\n // -- Callbacks -----------------------------------------------------------\n\n /** Called when the animation starts or resumes. */\n onPlay?: () => void;\n\n /** Called when the animation stops for any reason (pause / cancel / finish / removal). */\n onStop?: () => void;\n\n /** Called when the animation is paused. */\n onPause?: () => void;\n\n /** Called when the animation is cancelled. */\n onCancel?: () => void;\n\n /** Called when the animation finishes naturally. */\n onFinish?: () => void;\n\n /** Called when the animation is removed. */\n onRemove?: () => void;\n}\n\n\n// -- Internal types ---------------------------------------------------------\n\nenum PixodeskSvgAnimatorCompMode {\n static = 'static',\n autoplay = 'autoplay',\n play = 'play',\n imperativeApi = 'imperativeApi',\n fixedTime = 'fixedTime'\n}\n\n\n// -- React โ Animator bridge ------------------------------------------------\n\n/**\n * Creates a platform adapter that routes animator attribute updates\n * to the corresponding React-managed DOM refs.\n */\nexport function createReactAdapter(elementRefs: React.RefObject<Map<string, any>>) {\n const warnedSelectors = new Set<string>();\n\n const adapter: PxPlatformAdapter = {\n isConnected: () => {\n return true;\n },\n setAttribute: (id, attrName, value) => {\n\n attrName = camelCaseToKebabWordIfNeeded(attrName);\n\n const selector = getSelector(id);\n\n const element = elementRefs.current.get(id);\n\n if (!element && !warnedSelectors.has(selector)) {\n warnedSelectors.add(selector);\n console.warn('setAttribute: No elements found for selector \"' + selector + '\"');\n console.warn(elementRefs.current);\n }\n\n if (element) {\n element.setAttribute(attrName, value);\n if (STYLE_ATTR_NAMES.has(attrName)) {\n (element as HTMLElement).style[attrName as any] = value;\n }\n }\n },\n };\n return adapter;\n}\n\nexport function getSelector(id: string) {\n return '#' + id;\n}\n\n// FIXME: add model validation (e.g. isElementFileJson check)\n\n\n// -- Inner component (memoised, never re-renders) ---------------------------\n\nconst PixodeskSvgAnimatorImpl: FC<PixodeskSvgAnimatorImplProps> = ({\n className, style, doc, compMode, apiHolderRef, callbacksRef\n}) => {\n\n doc = generateNewIds(doc);\n\n const elementRefs = useRef(new Map<string, any>());\n\n const renderNode = (node: PxNode | undefined, isRoot = false): ReactElement | null => {\n if (!node) return null;\n\n const { type, animate, meta, children, ...props } = node;\n\n const normProps = getNormalizedProps(props);\n\n normProps['ref'] = (domEl: any) => {\n if (node['id']) elementRefs.current.set(node['id'], domEl);\n // return () => {};\n };\n\n // Apply the component's className/style props to the root SVG element.\n if (isRoot) {\n if (className) {\n normProps['className'] = normProps['className']\n ? normProps['className'] + ' ' + className\n : className;\n }\n if (style) normProps['style'] = style;\n }\n\n return createElement(type, normProps, children?.map(child => renderNode(child)));\n };\n\n const root = doc ? renderNode(doc, true) : null;\n\n // Create the animator once per document and tear it down on unmount.\n useEffect(() => {\n\n // Route lifecycle events through `callbacksRef` so the latest callback\n // props are invoked even though this component never re-renders.\n const cb = (name: keyof PixodeskSvgAnimatorCallbacks, alsoStop = false) => () => {\n callbacksRef.current?.[name]?.();\n if (alsoStop) callbacksRef.current?.onStop?.();\n };\n const callbacks = {\n onPlay: cb('onPlay'),\n onPause: cb('onPause', true),\n onCancel: cb('onCancel', true),\n onFinish: cb('onFinish', true),\n onRemove: cb('onRemove', true),\n };\n\n let api: PxAnimatorAPI | undefined = createAnimator({ data: doc, adapter: createReactAdapter(elementRefs), callbacks });\n apiHolderRef.current = api;\n\n return () => {\n api?.destroy();\n apiHolderRef.current = null;\n };\n }, [doc, apiHolderRef, callbacksRef]);\n\n return root;\n};\n\n/** Memoised wrapper โ the inner component never re-renders (props are stable by design). */\nconst PixodeskSvgAnimatorImplOnce = React.memo(\n PixodeskSvgAnimatorImpl,\n () => true // Don't re-render\n);\n\n\n// -- Main public component --------------------------------------------------\n\n/**\n * React component for rendering and controlling Pixodesk SVG animations.\n *\n * Supports four mutually-exclusive control modes:\n *\n * 1. **Autoplay** โ uses triggers from the animation document.\n * ```tsx\n * <PixodeskSvgAnimator doc={animation} autoplay />\n * ```\n *\n * 2. **Declarative play/pause** โ controlled via boolean props.\n * ```tsx\n * <PixodeskSvgAnimator doc={animation} play pause={false} />\n * ```\n *\n * 3. **Imperative** โ exposes a ref-based API for full programmatic control.\n * ```tsx\n * const api = useRef<ReactAnimatorApi>(null);\n * <PixodeskSvgAnimator doc={animation} apiRef={api} />\n * <button onClick={() => api.current?.play()}>Play</button>\n * ```\n *\n * 4. **Controlled time** โ renders a single frame at a given time.\n * ```tsx\n * <PixodeskSvgAnimator doc={animation} time={0.5} />\n * <PixodeskSvgAnimator doc={animation} timeMs={500} />\n * ```\n */\nconst PixodeskSvgAnimator: FC<PixodeskSvgAnimatorProps> = ({\n className, style,\n doc, autoplay, play, pause, time, timeMs, apiRef,\n\n // Overrides\n mode, delay, fill, iterations, duration, direction, frameRate,\n\n startOn, outAction, scrollIntoViewThreshold,\n\n onPlay, onStop, onPause, onCancel, onFinish, onRemove\n}) => {\n\n // Determine which control mode is active.\n let compMode = PixodeskSvgAnimatorCompMode.static;\n if (apiRef) {\n compMode = PixodeskSvgAnimatorCompMode.imperativeApi;\n } else if (autoplay) {\n compMode = PixodeskSvgAnimatorCompMode.autoplay;\n } else if (time !== undefined || timeMs !== undefined) {\n compMode = PixodeskSvgAnimatorCompMode.fixedTime;\n } else if (play !== undefined || pause !== undefined) {\n compMode = PixodeskSvgAnimatorCompMode.play;\n }\n\n // In non-autoplay modes, override the document trigger to 'programmatic'\n // so the component can manage playback itself.\n if (compMode !== PixodeskSvgAnimatorCompMode.autoplay) {\n const startOn = doc.animator?.trigger?.startOn;\n if (\n startOn &&\n startOn !== 'programmatic' // FIXME: use enum\n ) {\n doc = {\n ...doc,\n animator: {\n ...doc.animator,\n trigger: {\n ...doc.animator?.trigger,\n startOn: 'programmatic' // FIXME: use enum\n }\n }\n };\n }\n }\n\n // Apply timing overrides from props onto the document config.\n if (\n mode !== undefined ||\n duration !== undefined ||\n delay !== undefined ||\n iterations !== undefined ||\n fill !== undefined ||\n direction !== undefined ||\n frameRate !== undefined\n ) {\n const animator = doc.animator || {};\n doc = {\n ...doc,\n animator: {\n ...animator,\n mode: mode !== undefined ? mode : animator.mode,\n duration: duration !== undefined ? duration : animator.duration,\n delay: delay !== undefined ? delay : animator.delay,\n iterations: iterations !== undefined ? iterations : animator.iterations,\n fill: fill !== undefined ? fill : animator.fill,\n direction: direction !== undefined ? direction : animator.direction,\n frameRate: frameRate !== undefined ? frameRate : animator.frameRate\n }\n };\n }\n\n // Apply trigger overrides from props.\n if (\n startOn !== undefined ||\n outAction !== undefined ||\n scrollIntoViewThreshold !== undefined\n ) {\n const trigger: PxTrigger = doc.animator?.trigger || {};\n doc = {\n ...doc,\n animator: {\n ...doc.animator,\n trigger: {\n ...trigger,\n startOn: startOn !== undefined ? startOn : trigger.startOn,\n outAction: outAction !== undefined ? outAction : trigger.outAction,\n scrollIntoViewThreshold: scrollIntoViewThreshold !== undefined ? scrollIntoViewThreshold : trigger.scrollIntoViewThreshold\n }\n }\n };\n }\n\n // Controlled-time mode: compute the absolute seek target. `time` is a\n // fraction (0โ1) of the WHOLE timeline (duration ร iterations); `timeMs`\n // is absolute milliseconds. The seek is applied through the animator API\n // (setCurrentTime) below โ the document itself stays stable, so scrubbing\n // does NOT recreate the animator.\n let seekMs: number | undefined;\n if (compMode === PixodeskSvgAnimatorCompMode.fixedTime) {\n const animator = doc.animator || {};\n if (time !== undefined) {\n const iterationsValue = iterations ?? animator.iterations;\n const iterationsCount = typeof iterationsValue === 'number' && iterationsValue >= 1 ? iterationsValue : 1;\n const singleDuration = duration ?? animator.duration ?? 1000; // engine default duration\n seekMs = time * singleDuration * iterationsCount;\n }\n if (timeMs !== undefined) seekMs = timeMs;\n }\n\n const apiHolderRef = useRef<PxAnimatorAPI | null>(null);\n\n // Keep the latest callback props readable by the memoised inner component.\n const callbacksRef = useRef<PixodeskSvgAnimatorCallbacks>({});\n callbacksRef.current = { onPlay, onStop, onPause, onCancel, onFinish, onRemove };\n\n // Expose the imperative API via the consumer-provided ref.\n useImperativeHandle(apiRef, () => {\n return {\n isPlaying: () => apiHolderRef.current?.isPlaying() || false,\n play: () => apiHolderRef.current?.play(),\n pause: () => apiHolderRef.current?.pause(),\n cancel: () => apiHolderRef.current?.cancel(),\n finish: () => apiHolderRef.current?.finish(),\n setPlaybackRate: (rate: number) => apiHolderRef.current?.setPlaybackRate(rate),\n getCurrentTime: () => apiHolderRef.current?.getCurrentTime() ?? null,\n setCurrentTime: (time: number) => apiHolderRef.current?.setCurrentTime(time),\n };\n }, []);\n\n // Increment key when the document, mode, or root styling changes to force\n // a full remount (the inner component is memoised and never re-renders).\n const key = useDepsVersion(compMode, doc, className, style);\n\n // Sync declarative play/pause props with the animator. Re-runs after a\n // remount (`key` in deps) so a doc swap re-applies the current state.\n useEffect(() => {\n\n if (compMode === PixodeskSvgAnimatorCompMode.play) {\n if (play && !pause) {\n apiHolderRef.current?.play();\n } else if (pause) {\n apiHolderRef.current?.pause();\n } else if (play === false) {\n // explicit play=false โ jump to the end state\n apiHolderRef.current?.finish();\n } else {\n // pause-only usage: pause switched off โ resume\n apiHolderRef.current?.play();\n }\n }\n\n return () => {\n if (compMode === PixodeskSvgAnimatorCompMode.play) {\n // Intentionally read at cleanup time (NOT snapshotted at effect\n // time): the inner component nulls the ref when it destroys the\n // animator, so this pauses only a still-live instance. A\n // snapshot would call pause() on a destroyed animator and emit\n // a spurious onPause after teardown.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n apiHolderRef.current?.pause();\n }\n };\n }, [compMode, play, pause, key]);\n\n // Controlled-time mode: seek through the animator API. Scrubbing `time` /\n // `timeMs` only re-runs this effect โ the animator is NOT recreated.\n useEffect(() => {\n if (compMode === PixodeskSvgAnimatorCompMode.fixedTime && seekMs !== undefined) {\n apiHolderRef.current?.setCurrentTime(seekMs);\n apiHolderRef.current?.pause();\n }\n }, [compMode, seekMs, key]);\n\n return (\n <PixodeskSvgAnimatorImplOnce\n key={key}\n className={className}\n style={style}\n compMode={compMode}\n doc={doc}\n apiHolderRef={apiHolderRef}\n callbacksRef={callbacksRef}\n />\n );\n};\n\nexport default PixodeskSvgAnimator;","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport { useRef } from 'react';\n\nexport function deepEquals(a: any, b: any): boolean {\n // Handle primitives and null\n if (a === b) return true;\n if (a == null || b == null) return false;\n if (typeof a !== 'object' || typeof b !== 'object') return false;\n\n // Check for cycles using WeakSet\n const seen = new WeakSet();\n\n function equals(x: any, y: any): boolean {\n // Primitive/null check\n if (x === y) return true;\n if (x == null || y == null) return false;\n if (typeof x !== 'object' || typeof y !== 'object') return false;\n\n // Cycle detection\n if (seen.has(x)) {\n throw new Error('Circular reference detected');\n }\n seen.add(x);\n\n // Array check\n const xIsArray = Array.isArray(x);\n const yIsArray = Array.isArray(y);\n if (xIsArray !== yIsArray) return false;\n\n if (xIsArray) {\n if (x.length !== y.length) return false;\n for (let i = 0; i < x.length; i++) {\n if (!equals(x[i], y[i])) return false;\n }\n return true;\n }\n\n // Object check\n const xKeys = Object.keys(x);\n const yKeys = Object.keys(y);\n if (xKeys.length !== yKeys.length) return false;\n\n for (const key of xKeys) {\n if (!yKeys.includes(key)) return false;\n if (!equals(x[key], y[key])) return false;\n }\n\n return true;\n }\n\n return equals(a, b);\n}\n\n\n/**\n * Compares two arrays of dependencies using identity check first (===),\n * falling back to deep equality if references differ.\n * This is optimized for immutable data that may be recreated with same values.\n */\nfunction areDepsEqual(prevDeps: Array<any>, nextDeps: Array<any>): boolean {\n if (prevDeps.length !== nextDeps.length) return false;\n\n for (let i = 0; i < prevDeps.length; i++) {\n // Fast path: same reference (common for immutable data)\n if (prevDeps[i] === nextDeps[i]) continue;\n\n // Slow path: different reference but possibly same content\n if (!deepEquals(prevDeps[i], nextDeps[i])) return false;\n }\n\n return true;\n}\n\n/**\n * Returns a stable numeric version ID that only increments when dependencies\n * actually change (by reference or deep equality).\n */\nexport function useDepsVersion(...deps: Array<any>): number {\n const cache = useRef<{\n versionId: number;\n deps: Array<any>;\n } | null>(null);\n\n // First render - initialize with version 1\n if (cache.current === null) {\n cache.current = {\n versionId: 1,\n deps: deps\n };\n return 1;\n }\n\n // Check if any dependency changed (by reference or value)\n if (!areDepsEqual(cache.current.deps, deps)) {\n // Dependencies changed - increment version and update cache\n cache.current = {\n versionId: cache.current.versionId + 1,\n deps: deps\n };\n }\n\n return cache.current.versionId;\n}","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport { OutAction, StartOn } from \"@pixodesk/svg-animator-web\";\nimport { CSSProperties, FC, ReactNode, useEffect, useRef, useState } from \"react\";\n\n\ntype AnimState = 'idle' | 'paused' | 'playing';\n\n/**\n * Controls playback of a SVG+CSS animated files by toggling class names on a wrapper div.\n *\n * Intended for use with SVG files exported from the Pixodesk editor using the\n * **CSS Keyframes** (no `<script>` tag). Import the SVG as a React component\n * via SVGR (`vite-plugin-svgr` or `@svgr/webpack`) and pass it as `children`:\n *\n * ```tsx\n * import AnimationSvg from './animation.svg?react'; // SVGR\n *\n * <PixodeskSvgCssAnimator startOn=\"mouseOver\" outAction=\"pause\">\n * <AnimationSvg />\n * </PixodeskSvgCssAnimator>\n * ```\n *\n * The wrapper div carries one of three animation states via CSS class names:\n * - *(no class)* โ idle, animation not started\n * - `px-anim-enabled` โ started but paused\n * - `px-anim-enabled px-anim-playing` โ actively playing\n *\n * @param children - The SVGR-imported SVG component to animate.\n * @param startOn - What triggers the animation to start:\n * - `'load'` โ plays immediately on mount (default)\n * - `'mouseOver'` โ plays on hover\n * - `'click'` โ plays on click, toggles on second click\n * - `'scrollIntoView'` โ plays when the element enters the viewport\n * @param outAction - What happens when the trigger ends (hover/scroll out, second click):\n * - `'continue'` โ keeps playing (default)\n * - `'pause'` โ pauses at the current frame\n * - `'reset'` โ resets to the beginning\n * @param className - Additional CSS class names to apply to the wrapper div.\n * @param style - Inline styles for the wrapper div (e.g. `{ width: 400, height: 400 }`).\n */\nconst PixodeskSvgCssAnimator: FC<{\n className?: string;\n style?: CSSProperties;\n children: ReactNode;\n startOn?: StartOn;\n outAction?: OutAction;\n}> = ({ className, style, children, startOn = 'load', outAction = 'continue' }) => {\n\n const [state, setState] = useState<AnimState>(startOn === 'load' ? 'playing' : 'idle');\n\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (startOn !== 'scrollIntoView') return;\n const el = ref.current;\n if (!el) return;\n const outState: AnimState = (\n outAction === 'reset' ? 'idle' :\n outAction === 'pause' ? 'paused' : 'playing'\n );\n const observer = new IntersectionObserver(\n ([entry]) => setState(entry.isIntersecting ? 'playing' : outState),\n { threshold: 0.1 }\n );\n observer.observe(el);\n return () => observer.disconnect();\n }, [startOn, outAction]);\n\n const goOut = () => setState(\n outAction === 'reset' ? 'idle' :\n outAction === 'pause' ? 'paused' : 'playing'\n );\n\n const handlers =\n startOn === 'mouseOver' ? { onMouseEnter: () => setState('playing'), onMouseLeave: goOut } :\n startOn === 'click' ? { onClick: () => state === 'playing' ? goOut() : setState('playing') } :\n {};\n\n return (\n <div\n ref={ref}\n className={[\n className,\n state === 'playing' ? 'px-anim-enabled px-anim-playing' :\n state === 'paused' ? 'px-anim-enabled' : ''\n ].filter(Boolean).join(' ')}\n style={style}\n {...handlers}\n >\n {children}\n </div>\n );\n};\nexport default PixodeskSvgCssAnimator;"],"mappings":"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","names":["useRef","useRef","startOn","time","useEffect","useRef","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../src/PixodeskSvgAnimator.tsx","../src/Utils.ts","../src/PixodeskSvgCssAnimator.tsx"],"sourcesContent":["/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport type { JsMode, OutAction, PxAnimatedSvgDocument, PxAnimatorAPI, PxNode, PxPlatformAdapter, PxTrigger, StartOn } from '@pixodesk/svg-animator-web';\nimport { camelCaseToKebabWordIfNeeded, createAnimator, FillMode, generateNewIds, getNormalizedProps, STYLE_ATTR_NAMES } from '@pixodesk/svg-animator-web';\nimport type { CSSProperties, FC, ReactElement } from 'react';\nimport React, { createElement, useEffect, useImperativeHandle, useRef } from 'react';\nimport { useDepsVersion } from './Utils';\n\n\n// -- Public types -----------------------------------------------------------\n\nexport interface ReactAnimatorApi {\n /** Returns true if the animation is currently running. */\n isPlaying(): boolean;\n\n /** Starts or resumes the animation. */\n play(): void;\n\n /** Pauses the animation at its current state. */\n pause(): void;\n\n /** Stops the animation and resets it to its initial state. */\n cancel(): void;\n\n /** Jumps to the end of the animation and holds the final state. */\n finish(): void;\n\n /** Changes the speed of the animation. 1 is normal, 2 is double, -1 is reverse. */\n setPlaybackRate(rate: number): void;\n\n /** Returns the current playback time in milliseconds. */\n getCurrentTime(): number | null;\n\n /** Jumps to a specific time (in milliseconds) in the animation. */\n setCurrentTime(time: number): void;\n}\n\nexport interface PixodeskSvgAnimatorImplProps {\n className?: string;\n style?: CSSProperties;\n doc: PxAnimatedSvgDocument;\n compMode: PixodeskSvgAnimatorCompMode;\n\n /** Imperative API handle populated by the inner component. */\n apiHolderRef: React.RefObject<PxAnimatorAPI | null>;\n\n /**\n * Latest lifecycle callbacks, read at invocation time so the memoised\n * inner component always calls the current props even though it never\n * re-renders.\n */\n callbacksRef: React.RefObject<PixodeskSvgAnimatorCallbacks>;\n}\n\n/** Lifecycle callback props (subset of {@link PixodeskSvgAnimatorProps}). */\nexport interface PixodeskSvgAnimatorCallbacks {\n onPlay?: () => void;\n onStop?: () => void;\n onPause?: () => void;\n onCancel?: () => void;\n onFinish?: () => void;\n onRemove?: () => void;\n}\n\nexport interface PixodeskSvgAnimatorProps {\n\n className?: string;\n\n style?: CSSProperties;\n\n // -- Source ---------------------------------------------------------------\n\n /**\n * The animation document to render.\n *\n * TODO: Accept PxFileConfig | string to support URL-based loading, e.g.\n * <PixodeskSvgAnimator doc=\"/animation.json\" />\n */\n doc: PxAnimatedSvgDocument;\n\n // -- Rendering mode ------------------------------------------------------\n\n /** Forces a specific rendering engine. Defaults to 'auto'. */\n mode?: JsMode;\n\n // -- Timing overrides ----------------------------------------------------\n\n /** Delay before the animation starts, in milliseconds. */\n delay?: number;\n\n /** Defines the element's style when the animation is not active. */\n fill?: FillMode;\n\n /** Number of iterations, or 'infinite' for endless looping. */\n iterations?: number | 'infinite';\n\n /** Duration of a single iteration in milliseconds. */\n duration?: number;\n\n /** Playback direction. */\n direction?: PlaybackDirection;\n\n /** Target frame rate (frames per second). */\n frameRate?: number;\n\n // -- Trigger overrides ---------------------------------------------------\n\n /** The event that starts the animation. When omitted, uses the value from the document config. */\n startOn?: StartOn;\n\n /** Behaviour when the trigger condition ends (e.g. mouse-out, second click). */\n outAction?: OutAction;\n\n /** Visibility ratio (0.0โ1.0) required to trigger a scrollIntoView animation. Defaults to 0.5. */\n scrollIntoViewThreshold?: number;\n\n // -- Declarative control -------------------------------------------------\n\n /** When true, uses triggers defined in the animation document. */\n autoplay?: boolean;\n\n /**\n * Starts the animation unconditionally, ignoring document triggers.\n * Equivalent to `startOn=\"load\"` / `outAction=\"continue\"`.\n */\n play?: boolean;\n\n /** Pauses current playback. Only meaningful when `play` or `autoplay` is set. */\n pause?: boolean;\n\n // -- Imperative control --------------------------------------------------\n\n /** Ref populated with the imperative playback API. */\n apiRef?: React.RefObject<ReactAnimatorApi | null>;\n\n // -- Controlled (external) time ------------------------------------------\n\n /** Seek to a specific point in the animation, as a fraction (0โ1) of the whole timeline (duration ร iterations). */\n progress?: number;\n\n /** Seek to a specific point in the animation (milliseconds). */\n time?: number;\n\n // -- Callbacks -----------------------------------------------------------\n\n /** Called when the animation starts or resumes. */\n onPlay?: () => void;\n\n /** Called when the animation stops for any reason (pause / cancel / finish / removal). */\n onStop?: () => void;\n\n /** Called when the animation is paused. */\n onPause?: () => void;\n\n /** Called when the animation is cancelled. */\n onCancel?: () => void;\n\n /** Called when the animation finishes naturally. */\n onFinish?: () => void;\n\n /** Called when the animation is removed. */\n onRemove?: () => void;\n}\n\n\n// -- Internal types ---------------------------------------------------------\n\nenum PixodeskSvgAnimatorCompMode {\n static = 'static',\n autoplay = 'autoplay',\n play = 'play',\n imperativeApi = 'imperativeApi',\n fixedTime = 'fixedTime'\n}\n\n\n// -- React โ Animator bridge ------------------------------------------------\n\n/**\n * Creates a platform adapter that routes animator attribute updates\n * to the corresponding React-managed DOM refs.\n */\nexport function createReactAdapter(elementRefs: React.RefObject<Map<string, any>>) {\n const warnedSelectors = new Set<string>();\n\n const adapter: PxPlatformAdapter = {\n isConnected: () => {\n return true;\n },\n setAttribute: (id, attrName, value) => {\n\n attrName = camelCaseToKebabWordIfNeeded(attrName);\n\n const selector = getSelector(id);\n\n const element = elementRefs.current.get(id);\n\n if (!element && !warnedSelectors.has(selector)) {\n warnedSelectors.add(selector);\n console.warn('setAttribute: No elements found for selector \"' + selector + '\"');\n console.warn(elementRefs.current);\n }\n\n if (element) {\n element.setAttribute(attrName, value);\n if (STYLE_ATTR_NAMES.has(attrName)) {\n (element as HTMLElement).style[attrName as any] = value;\n }\n }\n },\n };\n return adapter;\n}\n\nexport function getSelector(id: string) {\n return '#' + id;\n}\n\n// FIXME: add model validation (e.g. isElementFileJson check)\n\n\n// -- Inner component (memoised, never re-renders) ---------------------------\n\nconst PixodeskSvgAnimatorImpl: FC<PixodeskSvgAnimatorImplProps> = ({\n className, style, doc, compMode, apiHolderRef, callbacksRef\n}) => {\n\n doc = generateNewIds(doc);\n\n const elementRefs = useRef(new Map<string, any>());\n\n const renderNode = (node: PxNode | undefined, isRoot = false, key?: React.Key): ReactElement | null => {\n if (!node) return null;\n\n const { type, animate, meta, children, ...props } = node;\n\n const normProps = getNormalizedProps(props);\n if (key !== undefined) normProps['key'] = key;\n\n normProps['ref'] = (domEl: any) => {\n if (node['id']) elementRefs.current.set(node['id'], domEl);\n // return () => {};\n };\n\n // Apply the component's className/style props to the root SVG element.\n if (isRoot) {\n if (className) {\n normProps['className'] = normProps['className']\n ? normProps['className'] + ' ' + className\n : className;\n }\n if (style) normProps['style'] = style;\n }\n\n return createElement(type, normProps, children?.map((child, i) => renderNode(child, false, child['id'] ?? i)));\n };\n\n const root = doc ? renderNode(doc, true) : null;\n\n // Create the animator once per document and tear it down on unmount.\n useEffect(() => {\n\n // Route lifecycle events through `callbacksRef` so the latest callback\n // props are invoked even though this component never re-renders.\n const cb = (name: keyof PixodeskSvgAnimatorCallbacks, alsoStop = false) => () => {\n callbacksRef.current?.[name]?.();\n if (alsoStop) callbacksRef.current?.onStop?.();\n };\n const callbacks = {\n onPlay: cb('onPlay'),\n onPause: cb('onPause', true),\n onCancel: cb('onCancel', true),\n onFinish: cb('onFinish', true),\n onRemove: cb('onRemove', true),\n };\n\n let api: PxAnimatorAPI | undefined = createAnimator({ data: doc, adapter: createReactAdapter(elementRefs), callbacks });\n apiHolderRef.current = api;\n\n return () => {\n api?.destroy();\n apiHolderRef.current = null;\n };\n }, [doc, apiHolderRef, callbacksRef]);\n\n return root;\n};\n\n/** Memoised wrapper โ the inner component never re-renders (props are stable by design). */\nconst PixodeskSvgAnimatorImplOnce = React.memo(\n PixodeskSvgAnimatorImpl,\n () => true // Don't re-render\n);\n\n\n// -- Main public component --------------------------------------------------\n\n/**\n * React component for rendering and controlling Pixodesk SVG animations.\n *\n * Supports four mutually-exclusive control modes:\n *\n * 1. **Autoplay** โ uses triggers from the animation document.\n * ```tsx\n * <PixodeskSvgAnimator doc={animation} autoplay />\n * ```\n *\n * 2. **Declarative play/pause** โ controlled via boolean props.\n * ```tsx\n * <PixodeskSvgAnimator doc={animation} play pause={false} />\n * ```\n *\n * 3. **Imperative** โ exposes a ref-based API for full programmatic control.\n * ```tsx\n * const api = useRef<ReactAnimatorApi>(null);\n * <PixodeskSvgAnimator doc={animation} apiRef={api} />\n * <button onClick={() => api.current?.play()}>Play</button>\n * ```\n *\n * 4. **Controlled time** โ renders a single frame at a given time.\n * ```tsx\n * <PixodeskSvgAnimator doc={animation} progress={0.5} />\n * <PixodeskSvgAnimator doc={animation} time={500} />\n * ```\n */\nconst PixodeskSvgAnimator: FC<PixodeskSvgAnimatorProps> = ({\n className, style,\n doc, autoplay, play, pause, progress, time, apiRef,\n\n // Overrides\n mode, delay, fill, iterations, duration, direction, frameRate,\n\n startOn, outAction, scrollIntoViewThreshold,\n\n onPlay, onStop, onPause, onCancel, onFinish, onRemove\n}) => {\n\n // Determine which control mode is active.\n let compMode = PixodeskSvgAnimatorCompMode.static;\n if (apiRef) {\n compMode = PixodeskSvgAnimatorCompMode.imperativeApi;\n } else if (autoplay) {\n compMode = PixodeskSvgAnimatorCompMode.autoplay;\n } else if (progress !== undefined || time !== undefined) {\n compMode = PixodeskSvgAnimatorCompMode.fixedTime;\n } else if (play !== undefined || pause !== undefined) {\n compMode = PixodeskSvgAnimatorCompMode.play;\n }\n\n // In non-autoplay modes, override the document trigger to 'programmatic'\n // so the component can manage playback itself.\n if (compMode !== PixodeskSvgAnimatorCompMode.autoplay) {\n const startOn = doc.animator?.trigger?.startOn;\n if (\n startOn &&\n startOn !== 'programmatic' // FIXME: use enum\n ) {\n doc = {\n ...doc,\n animator: {\n ...doc.animator,\n trigger: {\n ...doc.animator?.trigger,\n startOn: 'programmatic' // FIXME: use enum\n }\n }\n };\n }\n }\n\n // Apply timing overrides from props onto the document config.\n if (\n mode !== undefined ||\n duration !== undefined ||\n delay !== undefined ||\n iterations !== undefined ||\n fill !== undefined ||\n direction !== undefined ||\n frameRate !== undefined\n ) {\n const animator = doc.animator || {};\n doc = {\n ...doc,\n animator: {\n ...animator,\n mode: mode !== undefined ? mode : animator.mode,\n duration: duration !== undefined ? duration : animator.duration,\n delay: delay !== undefined ? delay : animator.delay,\n iterations: iterations !== undefined ? iterations : animator.iterations,\n fill: fill !== undefined ? fill : animator.fill,\n direction: direction !== undefined ? direction : animator.direction,\n frameRate: frameRate !== undefined ? frameRate : animator.frameRate\n }\n };\n }\n\n // Apply trigger overrides from props.\n if (\n startOn !== undefined ||\n outAction !== undefined ||\n scrollIntoViewThreshold !== undefined\n ) {\n const trigger: PxTrigger = doc.animator?.trigger || {};\n doc = {\n ...doc,\n animator: {\n ...doc.animator,\n trigger: {\n ...trigger,\n startOn: startOn !== undefined ? startOn : trigger.startOn,\n outAction: outAction !== undefined ? outAction : trigger.outAction,\n scrollIntoViewThreshold: scrollIntoViewThreshold !== undefined ? scrollIntoViewThreshold : trigger.scrollIntoViewThreshold\n }\n }\n };\n }\n\n // Controlled-time mode: compute the absolute seek target. `progress` is a\n // fraction (0โ1) of the WHOLE timeline (duration ร iterations); `time`\n // is absolute milliseconds. The seek is applied through the animator API\n // (setCurrentTime) below โ the document itself stays stable, so scrubbing\n // does NOT recreate the animator.\n let seekMs: number | undefined;\n if (compMode === PixodeskSvgAnimatorCompMode.fixedTime) {\n const animator = doc.animator || {};\n if (progress !== undefined) {\n const iterationsValue = iterations ?? animator.iterations;\n const iterationsCount = typeof iterationsValue === 'number' && iterationsValue >= 1 ? iterationsValue : 1;\n const singleDuration = duration ?? animator.duration ?? 1000; // engine default duration\n seekMs = progress * singleDuration * iterationsCount;\n }\n if (time !== undefined) seekMs = time;\n }\n\n const apiHolderRef = useRef<PxAnimatorAPI | null>(null);\n\n // Keep the latest callback props readable by the memoised inner component.\n const callbacksRef = useRef<PixodeskSvgAnimatorCallbacks>({});\n callbacksRef.current = { onPlay, onStop, onPause, onCancel, onFinish, onRemove };\n\n // Expose the imperative API via the consumer-provided ref.\n useImperativeHandle(apiRef, () => {\n return {\n isPlaying: () => apiHolderRef.current?.isPlaying() || false,\n play: () => apiHolderRef.current?.play(),\n pause: () => apiHolderRef.current?.pause(),\n cancel: () => apiHolderRef.current?.cancel(),\n finish: () => apiHolderRef.current?.finish(),\n setPlaybackRate: (rate: number) => apiHolderRef.current?.setPlaybackRate(rate),\n getCurrentTime: () => apiHolderRef.current?.getCurrentTime() ?? null,\n setCurrentTime: (time: number) => apiHolderRef.current?.setCurrentTime(time),\n };\n }, []);\n\n // Increment key when the document, mode, or root styling changes to force\n // a full remount (the inner component is memoised and never re-renders).\n const key = useDepsVersion(compMode, doc, className, style);\n\n // Sync declarative play/pause props with the animator. Re-runs after a\n // remount (`key` in deps) so a doc swap re-applies the current state.\n useEffect(() => {\n\n if (compMode === PixodeskSvgAnimatorCompMode.play) {\n if (play && !pause) {\n apiHolderRef.current?.play();\n } else if (pause) {\n apiHolderRef.current?.pause();\n } else if (play === false) {\n // explicit play=false โ jump to the end state\n apiHolderRef.current?.finish();\n } else {\n // pause-only usage: pause switched off โ resume\n apiHolderRef.current?.play();\n }\n }\n\n return () => {\n if (compMode === PixodeskSvgAnimatorCompMode.play) {\n // Intentionally read at cleanup time (NOT snapshotted at effect\n // time): the inner component nulls the ref when it destroys the\n // animator, so this pauses only a still-live instance. A\n // snapshot would call pause() on a destroyed animator and emit\n // a spurious onPause after teardown.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n apiHolderRef.current?.pause();\n }\n };\n }, [compMode, play, pause, key]);\n\n // Controlled-time mode: seek through the animator API. Scrubbing `progress` /\n // `time` only re-runs this effect โ the animator is NOT recreated.\n useEffect(() => {\n if (compMode === PixodeskSvgAnimatorCompMode.fixedTime && seekMs !== undefined) {\n apiHolderRef.current?.setCurrentTime(seekMs);\n apiHolderRef.current?.pause();\n }\n }, [compMode, seekMs, key]);\n\n return (\n <PixodeskSvgAnimatorImplOnce\n key={key}\n className={className}\n style={style}\n compMode={compMode}\n doc={doc}\n apiHolderRef={apiHolderRef}\n callbacksRef={callbacksRef}\n />\n );\n};\n\nexport default PixodeskSvgAnimator;","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport { useRef } from 'react';\n\nexport function deepEquals(a: any, b: any): boolean {\n // Handle primitives and null\n if (a === b) return true;\n if (a == null || b == null) return false;\n if (typeof a !== 'object' || typeof b !== 'object') return false;\n\n // Check for cycles using WeakSet\n const seen = new WeakSet();\n\n function equals(x: any, y: any): boolean {\n // Primitive/null check\n if (x === y) return true;\n if (x == null || y == null) return false;\n if (typeof x !== 'object' || typeof y !== 'object') return false;\n\n // Cycle detection\n if (seen.has(x)) {\n throw new Error('Circular reference detected');\n }\n seen.add(x);\n\n // Array check\n const xIsArray = Array.isArray(x);\n const yIsArray = Array.isArray(y);\n if (xIsArray !== yIsArray) return false;\n\n if (xIsArray) {\n if (x.length !== y.length) return false;\n for (let i = 0; i < x.length; i++) {\n if (!equals(x[i], y[i])) return false;\n }\n return true;\n }\n\n // Object check\n const xKeys = Object.keys(x);\n const yKeys = Object.keys(y);\n if (xKeys.length !== yKeys.length) return false;\n\n for (const key of xKeys) {\n if (!yKeys.includes(key)) return false;\n if (!equals(x[key], y[key])) return false;\n }\n\n return true;\n }\n\n return equals(a, b);\n}\n\n\n/**\n * Compares two arrays of dependencies using identity check first (===),\n * falling back to deep equality if references differ.\n * This is optimized for immutable data that may be recreated with same values.\n */\nfunction areDepsEqual(prevDeps: Array<any>, nextDeps: Array<any>): boolean {\n if (prevDeps.length !== nextDeps.length) return false;\n\n for (let i = 0; i < prevDeps.length; i++) {\n // Fast path: same reference (common for immutable data)\n if (prevDeps[i] === nextDeps[i]) continue;\n\n // Slow path: different reference but possibly same content\n if (!deepEquals(prevDeps[i], nextDeps[i])) return false;\n }\n\n return true;\n}\n\n/**\n * Returns a stable numeric version ID that only increments when dependencies\n * actually change (by reference or deep equality).\n */\nexport function useDepsVersion(...deps: Array<any>): number {\n const cache = useRef<{\n versionId: number;\n deps: Array<any>;\n } | null>(null);\n\n // First render - initialize with version 1\n if (cache.current === null) {\n cache.current = {\n versionId: 1,\n deps: deps\n };\n return 1;\n }\n\n // Check if any dependency changed (by reference or value)\n if (!areDepsEqual(cache.current.deps, deps)) {\n // Dependencies changed - increment version and update cache\n cache.current = {\n versionId: cache.current.versionId + 1,\n deps: deps\n };\n }\n\n return cache.current.versionId;\n}","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport { OutAction, StartOn } from \"@pixodesk/svg-animator-web\";\nimport { CSSProperties, FC, ReactNode, useEffect, useRef, useState } from \"react\";\n\n\ntype AnimState = 'idle' | 'paused' | 'playing';\n\n/**\n * Controls playback of a SVG+CSS animated files by toggling class names on a wrapper div.\n *\n * Intended for use with SVG files exported from the Pixodesk editor using the\n * **CSS Keyframes** (no `<script>` tag). Import the SVG as a React component\n * via SVGR (`vite-plugin-svgr` or `@svgr/webpack`) and pass it as `children`:\n *\n * ```tsx\n * import AnimationSvg from './animation.svg?react'; // SVGR\n *\n * <PixodeskSvgCssAnimator startOn=\"mouseOver\" outAction=\"pause\">\n * <AnimationSvg />\n * </PixodeskSvgCssAnimator>\n * ```\n *\n * The wrapper div carries one of three animation states via CSS class names:\n * - *(no class)* โ idle, animation not started\n * - `px-anim-enabled` โ started but paused\n * - `px-anim-enabled px-anim-playing` โ actively playing\n *\n * @param children - The SVGR-imported SVG component to animate.\n * @param startOn - What triggers the animation to start:\n * - `'load'` โ plays immediately on mount (default)\n * - `'mouseOver'` โ plays on hover\n * - `'click'` โ plays on click, toggles on second click\n * - `'scrollIntoView'` โ plays when the element enters the viewport\n * @param outAction - What happens when the trigger ends (hover/scroll out, second click):\n * - `'continue'` โ keeps playing (default)\n * - `'pause'` โ pauses at the current frame\n * - `'reset'` โ resets to the beginning\n * @param className - Additional CSS class names to apply to the wrapper div.\n * @param style - Inline styles for the wrapper div (e.g. `{ width: 400, height: 400 }`).\n */\nconst PixodeskSvgCssAnimator: FC<{\n className?: string;\n style?: CSSProperties;\n children: ReactNode;\n startOn?: StartOn;\n outAction?: OutAction;\n}> = ({ className, style, children, startOn = 'load', outAction = 'continue' }) => {\n\n const [state, setState] = useState<AnimState>(startOn === 'load' ? 'playing' : 'idle');\n\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (startOn !== 'scrollIntoView') return;\n const el = ref.current;\n if (!el) return;\n const outState: AnimState = (\n outAction === 'reset' ? 'idle' :\n outAction === 'pause' ? 'paused' : 'playing'\n );\n const observer = new IntersectionObserver(\n ([entry]) => setState(entry.isIntersecting ? 'playing' : outState),\n { threshold: 0.1 }\n );\n observer.observe(el);\n return () => observer.disconnect();\n }, [startOn, outAction]);\n\n const goOut = () => setState(\n outAction === 'reset' ? 'idle' :\n outAction === 'pause' ? 'paused' : 'playing'\n );\n\n const handlers =\n startOn === 'mouseOver' ? { onMouseEnter: () => setState('playing'), onMouseLeave: goOut } :\n startOn === 'click' ? { onClick: () => state === 'playing' ? goOut() : setState('playing') } :\n {};\n\n return (\n <div\n ref={ref}\n className={[\n className,\n state === 'playing' ? 'px-anim-enabled px-anim-playing' :\n state === 'paused' ? 'px-anim-enabled' : ''\n ].filter(Boolean).join(' ')}\n style={style}\n {...handlers}\n >\n {children}\n </div>\n );\n};\nexport default PixodeskSvgCssAnimator;"],"mappings":"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","names":["useRef","useRef","startOn","time","useEffect","useRef","jsx"]}
|