@pixodesk/svg-animator-react 1.0.6 → 1.0.8
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 +4 -0
- package/dist/index.cjs +1 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -6
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +1 -6
- package/dist/index.umd.js.map +1 -1
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
# animator-react
|
|
2
|
+
|
|
3
|
+
[](https://github.com/pixodesk/pixodesk-svg-animator/actions/workflows/ci.yml)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
2
6
|
React component for rendering and controlling Pixodesk SVG animations.
|
|
3
7
|
|
|
4
8
|
|
package/dist/index.cjs
CHANGED
|
@@ -144,11 +144,7 @@ var PixodeskSvgAnimatorImpl = ({
|
|
|
144
144
|
const { type, animate, meta, children, ...props } = node;
|
|
145
145
|
const normProps = (0, import_svg_animator_web.getNormalizedProps)(props);
|
|
146
146
|
normProps["ref"] = (domEl) => {
|
|
147
|
-
console.log("MOUNTED");
|
|
148
147
|
if (node["id"]) elementRefs.current.set(node["id"], domEl);
|
|
149
|
-
return () => {
|
|
150
|
-
console.log("UN-MOUNTED");
|
|
151
|
-
};
|
|
152
148
|
};
|
|
153
149
|
return (0, import_react2.createElement)(type, normProps, children?.map((child) => renderNode(child)));
|
|
154
150
|
};
|
|
@@ -160,7 +156,7 @@ var PixodeskSvgAnimatorImpl = ({
|
|
|
160
156
|
api?.destroy();
|
|
161
157
|
apiHolderRef.current = null;
|
|
162
158
|
};
|
|
163
|
-
}, [doc]);
|
|
159
|
+
}, [doc, apiHolderRef]);
|
|
164
160
|
return root;
|
|
165
161
|
};
|
|
166
162
|
var PixodeskSvgAnimatorImplOnce = import_react2.default.memo(
|
|
@@ -261,7 +257,6 @@ var PixodeskSvgAnimator = ({
|
|
|
261
257
|
}
|
|
262
258
|
};
|
|
263
259
|
}
|
|
264
|
-
console.log("doc", doc);
|
|
265
260
|
const apiHolderRef = (0, import_react2.useRef)(null);
|
|
266
261
|
(0, import_react2.useImperativeHandle)(apiRef, () => {
|
|
267
262
|
return {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/PixodeskSvgAnimator.tsx","../src/Utils.ts"],"sourcesContent":["import PixodeskSvgAnimator from './PixodeskSvgAnimator';\nexport { PixodeskSvgAnimator as PixodeskSvgAnimator };\nexport type { ReactAnimatorApi } from './PixodeskSvgAnimator';","import type { PxAnimatedSvgDocument, PxAnimatorAPI, PxNode, PxPlatformAdapter, PxTrigger } 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 /** 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\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 // -- Timeline ------------------------------------------------------------\n\n timeline?: 'time' | 'scroll';\n\n // -- Rendering mode ------------------------------------------------------\n\n /** Forces a specific rendering engine. Defaults to 'auto'. */\n mode?: 'webapi' | 'frames' | 'auto';\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?: 'load' | 'mouseOver' | 'click' | 'scrollIntoView' | 'programmatic';\n\n /** Behaviour when the trigger condition ends (e.g. mouse-out, second click). */\n outAction?: 'continue' | 'pause' | 'reset' | 'reverse';\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 (fractional). */\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; // FIXME: consider a more descriptive name\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 // -- Diagnostics ---------------------------------------------------------\n\n onWarning?: (msg: string) => void;\n onError?: (msg: string) => 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\n}) => {\n\n doc = generateNewIds(doc);\n\n const elementRefs = useRef(new Map<string, any>());\n\n const renderNode = (node: PxNode | undefined): 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 console.log('MOUNTED');\n\n if (node['id']) elementRefs.current.set(node['id'], domEl);\n\n return () => {\n console.log('UN-MOUNTED');\n };\n };\n\n return createElement(type, normProps, children?.map(child => renderNode(child)));\n };\n\n const root = doc ? renderNode(doc) : null;\n\n // Create the animator once per document and tear it down on unmount.\n useEffect(() => {\n\n let api: PxAnimatorAPI | undefined = createAnimator(doc, createReactAdapter(elementRefs));\n apiHolderRef.current = api;\n\n return () => {\n api?.destroy();\n apiHolderRef.current = null;\n };\n }, [doc]);\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 timeline,\n\n // Overrides\n mode, delay, fill, iterations, duration, direction, frameRate,\n\n startOn, outAction, scrollIntoViewThreshold\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 {\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 // In controlled-time mode, use a negative delay to seek to the given frame.\n if (compMode === PixodeskSvgAnimatorCompMode.fixedTime) {\n let delay = 0;\n if (time !== undefined) delay = -time; // FIXME: time as a fraction of total duration?\n if (timeMs !== undefined) delay = -timeMs;\n const animator = doc.animator || {};\n doc = {\n ...doc,\n animator: {\n ...animator,\n delay\n }\n };\n }\n\n console.log('doc', doc);\n\n const apiHolderRef = useRef<PxAnimatorAPI | null>(null);\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 getCurrentTime: () => apiHolderRef.current?.getCurrentTime() || null,\n setCurrentTime: (time: number) => apiHolderRef.current?.setCurrentTime(time),\n };\n }, []);\n\n // Sync declarative play/pause props with the animator.\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 {\n apiHolderRef.current?.finish();\n }\n }\n\n return () => {\n if (compMode === PixodeskSvgAnimatorCompMode.play) {\n apiHolderRef.current?.pause(); // FIXME - implement\n }\n };\n }, [compMode, play, pause]);\n\n // Increment key when the document or mode changes to force a full remount.\n const key = useDepsVersion(compMode, doc);\n\n return (\n <PixodeskSvgAnimatorImplOnce\n key={key}\n compMode={compMode}\n doc={doc}\n apiHolderRef={apiHolderRef}\n />\n );\n};\n\nexport default PixodeskSvgAnimator;","import { 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}"],"mappings":"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","names":["import_react","React","startOn","delay","time"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/PixodeskSvgAnimator.tsx","../src/Utils.ts"],"sourcesContent":["import PixodeskSvgAnimator from './PixodeskSvgAnimator';\nexport { PixodeskSvgAnimator as PixodeskSvgAnimator };\nexport type { ReactAnimatorApi } from './PixodeskSvgAnimator';","import type { PxAnimatedSvgDocument, PxAnimatorAPI, PxNode, PxPlatformAdapter, PxTrigger } 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 /** 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\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 // -- Timeline ------------------------------------------------------------\n\n timeline?: 'time' | 'scroll';\n\n // -- Rendering mode ------------------------------------------------------\n\n /** Forces a specific rendering engine. Defaults to 'auto'. */\n mode?: 'webapi' | 'frames' | 'auto';\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?: 'load' | 'mouseOver' | 'click' | 'scrollIntoView' | 'programmatic';\n\n /** Behaviour when the trigger condition ends (e.g. mouse-out, second click). */\n outAction?: 'continue' | 'pause' | 'reset' | 'reverse';\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 (fractional). */\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; // FIXME: consider a more descriptive name\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 // -- Diagnostics ---------------------------------------------------------\n\n onWarning?: (msg: string) => void;\n onError?: (msg: string) => 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\n}) => {\n\n doc = generateNewIds(doc);\n\n const elementRefs = useRef(new Map<string, any>());\n\n const renderNode = (node: PxNode | undefined): 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 return createElement(type, normProps, children?.map(child => renderNode(child)));\n };\n\n const root = doc ? renderNode(doc) : null;\n\n // Create the animator once per document and tear it down on unmount.\n useEffect(() => {\n\n let api: PxAnimatorAPI | undefined = createAnimator(doc, createReactAdapter(elementRefs));\n apiHolderRef.current = api;\n\n return () => {\n api?.destroy();\n apiHolderRef.current = null;\n };\n }, [doc, apiHolderRef]);\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 timeline,\n\n // Overrides\n mode, delay, fill, iterations, duration, direction, frameRate,\n\n startOn, outAction, scrollIntoViewThreshold\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 {\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 // In controlled-time mode, use a negative delay to seek to the given frame.\n if (compMode === PixodeskSvgAnimatorCompMode.fixedTime) {\n let delay = 0;\n if (time !== undefined) delay = -time; // FIXME: time as a fraction of total duration?\n if (timeMs !== undefined) delay = -timeMs;\n const animator = doc.animator || {};\n doc = {\n ...doc,\n animator: {\n ...animator,\n delay\n }\n };\n }\n\n const apiHolderRef = useRef<PxAnimatorAPI | null>(null);\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 getCurrentTime: () => apiHolderRef.current?.getCurrentTime() || null,\n setCurrentTime: (time: number) => apiHolderRef.current?.setCurrentTime(time),\n };\n }, []);\n\n // Sync declarative play/pause props with the animator.\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 {\n apiHolderRef.current?.finish();\n }\n }\n\n return () => {\n if (compMode === PixodeskSvgAnimatorCompMode.play) {\n apiHolderRef.current?.pause(); // FIXME - implement\n }\n };\n }, [compMode, play, pause]);\n\n // Increment key when the document or mode changes to force a full remount.\n const key = useDepsVersion(compMode, doc);\n\n return (\n <PixodeskSvgAnimatorImplOnce\n key={key}\n compMode={compMode}\n doc={doc}\n apiHolderRef={apiHolderRef}\n />\n );\n};\n\nexport default PixodeskSvgAnimator;","import { 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}"],"mappings":"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","names":["import_react","React","startOn","delay","time"]}
|
package/dist/index.js
CHANGED
|
@@ -108,11 +108,7 @@ var PixodeskSvgAnimatorImpl = ({
|
|
|
108
108
|
const { type, animate, meta, children, ...props } = node;
|
|
109
109
|
const normProps = getNormalizedProps(props);
|
|
110
110
|
normProps["ref"] = (domEl) => {
|
|
111
|
-
console.log("MOUNTED");
|
|
112
111
|
if (node["id"]) elementRefs.current.set(node["id"], domEl);
|
|
113
|
-
return () => {
|
|
114
|
-
console.log("UN-MOUNTED");
|
|
115
|
-
};
|
|
116
112
|
};
|
|
117
113
|
return createElement(type, normProps, children?.map((child) => renderNode(child)));
|
|
118
114
|
};
|
|
@@ -124,7 +120,7 @@ var PixodeskSvgAnimatorImpl = ({
|
|
|
124
120
|
api?.destroy();
|
|
125
121
|
apiHolderRef.current = null;
|
|
126
122
|
};
|
|
127
|
-
}, [doc]);
|
|
123
|
+
}, [doc, apiHolderRef]);
|
|
128
124
|
return root;
|
|
129
125
|
};
|
|
130
126
|
var PixodeskSvgAnimatorImplOnce = React.memo(
|
|
@@ -225,7 +221,6 @@ var PixodeskSvgAnimator = ({
|
|
|
225
221
|
}
|
|
226
222
|
};
|
|
227
223
|
}
|
|
228
|
-
console.log("doc", doc);
|
|
229
224
|
const apiHolderRef = useRef2(null);
|
|
230
225
|
useImperativeHandle(apiRef, () => {
|
|
231
226
|
return {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/PixodeskSvgAnimator.tsx","../src/Utils.ts"],"sourcesContent":["import type { PxAnimatedSvgDocument, PxAnimatorAPI, PxNode, PxPlatformAdapter, PxTrigger } 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 /** 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\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 // -- Timeline ------------------------------------------------------------\n\n timeline?: 'time' | 'scroll';\n\n // -- Rendering mode ------------------------------------------------------\n\n /** Forces a specific rendering engine. Defaults to 'auto'. */\n mode?: 'webapi' | 'frames' | 'auto';\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?: 'load' | 'mouseOver' | 'click' | 'scrollIntoView' | 'programmatic';\n\n /** Behaviour when the trigger condition ends (e.g. mouse-out, second click). */\n outAction?: 'continue' | 'pause' | 'reset' | 'reverse';\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 (fractional). */\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; // FIXME: consider a more descriptive name\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 // -- Diagnostics ---------------------------------------------------------\n\n onWarning?: (msg: string) => void;\n onError?: (msg: string) => 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\n}) => {\n\n doc = generateNewIds(doc);\n\n const elementRefs = useRef(new Map<string, any>());\n\n const renderNode = (node: PxNode | undefined): 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 console.log('MOUNTED');\n\n if (node['id']) elementRefs.current.set(node['id'], domEl);\n\n return () => {\n console.log('UN-MOUNTED');\n };\n };\n\n return createElement(type, normProps, children?.map(child => renderNode(child)));\n };\n\n const root = doc ? renderNode(doc) : null;\n\n // Create the animator once per document and tear it down on unmount.\n useEffect(() => {\n\n let api: PxAnimatorAPI | undefined = createAnimator(doc, createReactAdapter(elementRefs));\n apiHolderRef.current = api;\n\n return () => {\n api?.destroy();\n apiHolderRef.current = null;\n };\n }, [doc]);\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 timeline,\n\n // Overrides\n mode, delay, fill, iterations, duration, direction, frameRate,\n\n startOn, outAction, scrollIntoViewThreshold\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 {\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 // In controlled-time mode, use a negative delay to seek to the given frame.\n if (compMode === PixodeskSvgAnimatorCompMode.fixedTime) {\n let delay = 0;\n if (time !== undefined) delay = -time; // FIXME: time as a fraction of total duration?\n if (timeMs !== undefined) delay = -timeMs;\n const animator = doc.animator || {};\n doc = {\n ...doc,\n animator: {\n ...animator,\n delay\n }\n };\n }\n\n console.log('doc', doc);\n\n const apiHolderRef = useRef<PxAnimatorAPI | null>(null);\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 getCurrentTime: () => apiHolderRef.current?.getCurrentTime() || null,\n setCurrentTime: (time: number) => apiHolderRef.current?.setCurrentTime(time),\n };\n }, []);\n\n // Sync declarative play/pause props with the animator.\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 {\n apiHolderRef.current?.finish();\n }\n }\n\n return () => {\n if (compMode === PixodeskSvgAnimatorCompMode.play) {\n apiHolderRef.current?.pause(); // FIXME - implement\n }\n };\n }, [compMode, play, pause]);\n\n // Increment key when the document or mode changes to force a full remount.\n const key = useDepsVersion(compMode, doc);\n\n return (\n <PixodeskSvgAnimatorImplOnce\n key={key}\n compMode={compMode}\n doc={doc}\n apiHolderRef={apiHolderRef}\n />\n );\n};\n\nexport default PixodeskSvgAnimator;","import { 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}"],"mappings":"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","names":["useRef","useRef","startOn","delay","time"]}
|
|
1
|
+
{"version":3,"sources":["../src/PixodeskSvgAnimator.tsx","../src/Utils.ts"],"sourcesContent":["import type { PxAnimatedSvgDocument, PxAnimatorAPI, PxNode, PxPlatformAdapter, PxTrigger } 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 /** 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\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 // -- Timeline ------------------------------------------------------------\n\n timeline?: 'time' | 'scroll';\n\n // -- Rendering mode ------------------------------------------------------\n\n /** Forces a specific rendering engine. Defaults to 'auto'. */\n mode?: 'webapi' | 'frames' | 'auto';\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?: 'load' | 'mouseOver' | 'click' | 'scrollIntoView' | 'programmatic';\n\n /** Behaviour when the trigger condition ends (e.g. mouse-out, second click). */\n outAction?: 'continue' | 'pause' | 'reset' | 'reverse';\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 (fractional). */\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; // FIXME: consider a more descriptive name\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 // -- Diagnostics ---------------------------------------------------------\n\n onWarning?: (msg: string) => void;\n onError?: (msg: string) => 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\n}) => {\n\n doc = generateNewIds(doc);\n\n const elementRefs = useRef(new Map<string, any>());\n\n const renderNode = (node: PxNode | undefined): 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 return createElement(type, normProps, children?.map(child => renderNode(child)));\n };\n\n const root = doc ? renderNode(doc) : null;\n\n // Create the animator once per document and tear it down on unmount.\n useEffect(() => {\n\n let api: PxAnimatorAPI | undefined = createAnimator(doc, createReactAdapter(elementRefs));\n apiHolderRef.current = api;\n\n return () => {\n api?.destroy();\n apiHolderRef.current = null;\n };\n }, [doc, apiHolderRef]);\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 timeline,\n\n // Overrides\n mode, delay, fill, iterations, duration, direction, frameRate,\n\n startOn, outAction, scrollIntoViewThreshold\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 {\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 // In controlled-time mode, use a negative delay to seek to the given frame.\n if (compMode === PixodeskSvgAnimatorCompMode.fixedTime) {\n let delay = 0;\n if (time !== undefined) delay = -time; // FIXME: time as a fraction of total duration?\n if (timeMs !== undefined) delay = -timeMs;\n const animator = doc.animator || {};\n doc = {\n ...doc,\n animator: {\n ...animator,\n delay\n }\n };\n }\n\n const apiHolderRef = useRef<PxAnimatorAPI | null>(null);\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 getCurrentTime: () => apiHolderRef.current?.getCurrentTime() || null,\n setCurrentTime: (time: number) => apiHolderRef.current?.setCurrentTime(time),\n };\n }, []);\n\n // Sync declarative play/pause props with the animator.\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 {\n apiHolderRef.current?.finish();\n }\n }\n\n return () => {\n if (compMode === PixodeskSvgAnimatorCompMode.play) {\n apiHolderRef.current?.pause(); // FIXME - implement\n }\n };\n }, [compMode, play, pause]);\n\n // Increment key when the document or mode changes to force a full remount.\n const key = useDepsVersion(compMode, doc);\n\n return (\n <PixodeskSvgAnimatorImplOnce\n key={key}\n compMode={compMode}\n doc={doc}\n apiHolderRef={apiHolderRef}\n />\n );\n};\n\nexport default PixodeskSvgAnimator;","import { 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}"],"mappings":";AACA,SAAS,8BAA8B,gBAA0B,gBAAgB,oBAAoB,wBAAwB;AAE7H,OAAO,SAAS,eAAe,WAAW,qBAAqB,UAAAA,eAAc;;;ACH7E,SAAS,cAAc;AAEhB,SAAS,WAAW,GAAQ,GAAiB;AAEhD,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,KAAK,QAAQ,KAAK,KAAM,QAAO;AACnC,MAAI,OAAO,MAAM,YAAY,OAAO,MAAM,SAAU,QAAO;AAG3D,QAAM,OAAO,oBAAI,QAAQ;AAEzB,WAAS,OAAO,GAAQ,GAAiB;AAErC,QAAI,MAAM,EAAG,QAAO;AACpB,QAAI,KAAK,QAAQ,KAAK,KAAM,QAAO;AACnC,QAAI,OAAO,MAAM,YAAY,OAAO,MAAM,SAAU,QAAO;AAG3D,QAAI,KAAK,IAAI,CAAC,GAAG;AACb,YAAM,IAAI,MAAM,6BAA6B;AAAA,IACjD;AACA,SAAK,IAAI,CAAC;AAGV,UAAM,WAAW,MAAM,QAAQ,CAAC;AAChC,UAAM,WAAW,MAAM,QAAQ,CAAC;AAChC,QAAI,aAAa,SAAU,QAAO;AAElC,QAAI,UAAU;AACV,UAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,eAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AAC/B,YAAI,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,EAAG,QAAO;AAAA,MACpC;AACA,aAAO;AAAA,IACX;AAGA,UAAM,QAAQ,OAAO,KAAK,CAAC;AAC3B,UAAM,QAAQ,OAAO,KAAK,CAAC;AAC3B,QAAI,MAAM,WAAW,MAAM,OAAQ,QAAO;AAE1C,eAAW,OAAO,OAAO;AACrB,UAAI,CAAC,MAAM,SAAS,GAAG,EAAG,QAAO;AACjC,UAAI,CAAC,OAAO,EAAE,GAAG,GAAG,EAAE,GAAG,CAAC,EAAG,QAAO;AAAA,IACxC;AAEA,WAAO;AAAA,EACX;AAEA,SAAO,OAAO,GAAG,CAAC;AACtB;AAQA,SAAS,aAAa,UAAsB,UAA+B;AACvE,MAAI,SAAS,WAAW,SAAS,OAAQ,QAAO;AAEhD,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AAEtC,QAAI,SAAS,CAAC,MAAM,SAAS,CAAC,EAAG;AAGjC,QAAI,CAAC,WAAW,SAAS,CAAC,GAAG,SAAS,CAAC,CAAC,EAAG,QAAO;AAAA,EACtD;AAEA,SAAO;AACX;AAMO,SAAS,kBAAkB,MAA0B;AACxD,QAAM,QAAQ,OAGJ,IAAI;AAGd,MAAI,MAAM,YAAY,MAAM;AACxB,UAAM,UAAU;AAAA,MACZ,WAAW;AAAA,MACX;AAAA,IACJ;AACA,WAAO;AAAA,EACX;AAGA,MAAI,CAAC,aAAa,MAAM,QAAQ,MAAM,IAAI,GAAG;AAEzC,UAAM,UAAU;AAAA,MACZ,WAAW,MAAM,QAAQ,YAAY;AAAA,MACrC;AAAA,IACJ;AAAA,EACJ;AAEA,SAAO,MAAM,QAAQ;AACzB;;;AD2UQ;AAvQD,SAAS,mBAAmB,aAAgD;AAC/E,QAAM,kBAAkB,oBAAI,IAAY;AAExC,QAAM,UAA6B;AAAA,IAC/B,aAAa,MAAM;AACf,aAAO;AAAA,IACX;AAAA,IACA,cAAc,CAAC,IAAI,UAAU,UAAU;AAEnC,iBAAW,6BAA6B,QAAQ;AAEhD,YAAM,WAAW,YAAY,EAAE;AAE/B,YAAM,UAAU,YAAY,QAAQ,IAAI,EAAE;AAE1C,UAAI,CAAC,WAAW,CAAC,gBAAgB,IAAI,QAAQ,GAAG;AAC5C,wBAAgB,IAAI,QAAQ;AAC5B,gBAAQ,KAAK,mDAAmD,WAAW,GAAG;AAC9E,gBAAQ,KAAK,YAAY,OAAO;AAAA,MACpC;AAEA,UAAI,SAAS;AACT,gBAAQ,aAAa,UAAU,KAAK;AACpC,YAAI,iBAAiB,IAAI,QAAQ,GAAG;AAChC,UAAC,QAAwB,MAAM,QAAe,IAAI;AAAA,QACtD;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AACA,SAAO;AACX;AAEO,SAAS,YAAY,IAAY;AACpC,SAAO,MAAM;AACjB;AAOA,IAAM,0BAA4D,CAAC;AAAA,EAC/D;AAAA,EAAW;AAAA,EAAO;AAAA,EAAK;AAAA,EAAU;AACrC,MAAM;AAEF,QAAM,eAAe,GAAG;AAExB,QAAM,cAAcC,QAAO,oBAAI,IAAiB,CAAC;AAEjD,QAAM,aAAa,CAAC,SAAkD;AAClE,QAAI,CAAC,KAAM,QAAO;AAElB,UAAM,EAAE,MAAM,SAAS,MAAM,UAAU,GAAG,MAAM,IAAI;AAEpD,UAAM,YAAY,mBAAmB,KAAK;AAE1C,cAAU,KAAK,IAAI,CAAC,UAAe;AAC/B,UAAI,KAAK,IAAI,EAAG,aAAY,QAAQ,IAAI,KAAK,IAAI,GAAG,KAAK;AAAA,IAE7D;AAEA,WAAO,cAAc,MAAM,WAAW,UAAU,IAAI,WAAS,WAAW,KAAK,CAAC,CAAC;AAAA,EACnF;AAEA,QAAM,OAAO,MAAM,WAAW,GAAG,IAAI;AAGrC,YAAU,MAAM;AAEZ,QAAI,MAAiC,eAAe,KAAK,mBAAmB,WAAW,CAAC;AACxF,iBAAa,UAAU;AAEvB,WAAO,MAAM;AACT,WAAK,QAAQ;AACb,mBAAa,UAAU;AAAA,IAC3B;AAAA,EACJ,GAAG,CAAC,KAAK,YAAY,CAAC;AAEtB,SAAO;AACX;AAGA,IAAM,8BAA8B,MAAM;AAAA,EACtC;AAAA,EACA,MAAM;AAAA;AACV;AAiCA,IAAM,sBAAoD,CAAC;AAAA,EACvD;AAAA,EAAW;AAAA,EACX;AAAA,EAAK;AAAA,EAAU;AAAA,EAAM;AAAA,EAAO;AAAA,EAAM;AAAA,EAAQ;AAAA,EAC1C;AAAA;AAAA,EAGA;AAAA,EAAM;AAAA,EAAO;AAAA,EAAM;AAAA,EAAY;AAAA,EAAU;AAAA,EAAW;AAAA,EAEpD;AAAA,EAAS;AAAA,EAAW;AACxB,MAAM;AAGF,MAAI,WAAW;AACf,MAAI,QAAQ;AACR,eAAW;AAAA,EACf,WAAW,UAAU;AACjB,eAAW;AAAA,EACf,WAAW,SAAS,UAAa,WAAW,QAAW;AACnD,eAAW;AAAA,EACf,OAAO;AACH,eAAW;AAAA,EACf;AAIA,MAAI,aAAa,2BAAsC;AACnD,UAAMC,WAAU,IAAI,UAAU,SAAS;AACvC,QACIA,YACAA,aAAY,gBACd;AACE,YAAM;AAAA,QACF,GAAG;AAAA,QACH,UAAU;AAAA,UACN,GAAG,IAAI;AAAA,UACP,SAAS;AAAA,YACL,GAAG,IAAI,UAAU;AAAA,YACjB,SAAS;AAAA;AAAA,UACb;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAGA,MACI,SAAS,UACT,aAAa,UACb,UAAU,UACV,eAAe,UACf,SAAS,UACT,cAAc,UACd,cAAc,QAChB;AACE,UAAM,WAAW,IAAI,YAAY,CAAC;AAClC,UAAM;AAAA,MACF,GAAG;AAAA,MACH,UAAU;AAAA,QACN,GAAG;AAAA,QACH,MAAM,SAAS,SAAY,OAAO,SAAS;AAAA,QAC3C,UAAU,aAAa,SAAY,WAAW,SAAS;AAAA,QACvD,OAAO,UAAU,SAAY,QAAQ,SAAS;AAAA,QAC9C,YAAY,eAAe,SAAY,aAAa,SAAS;AAAA,QAC7D,MAAM,SAAS,SAAY,OAAO,SAAS;AAAA,QAC3C,WAAW,cAAc,SAAY,YAAY,SAAS;AAAA,QAC1D,WAAW,cAAc,SAAY,YAAY,SAAS;AAAA,MAC9D;AAAA,IACJ;AAAA,EACJ;AAGA,MACI,YAAY,UACZ,cAAc,UACd,4BAA4B,QAC9B;AACE,UAAM,UAAqB,IAAI,UAAU,WAAW,CAAC;AACrD,UAAM;AAAA,MACF,GAAG;AAAA,MACH,UAAU;AAAA,QACN,GAAG,IAAI;AAAA,QACP,SAAS;AAAA,UACL,GAAG;AAAA,UACH,SAAS,YAAY,SAAY,UAAU,QAAQ;AAAA,UACnD,WAAW,cAAc,SAAY,YAAY,QAAQ;AAAA,UACzD,yBAAyB,4BAA4B,SAAY,0BAA0B,QAAQ;AAAA,QACvG;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAGA,MAAI,aAAa,6BAAuC;AACpD,QAAIC,SAAQ;AACZ,QAAI,SAAS,OAAW,CAAAA,SAAQ,CAAC;AACjC,QAAI,WAAW,OAAW,CAAAA,SAAQ,CAAC;AACnC,UAAM,WAAW,IAAI,YAAY,CAAC;AAClC,UAAM;AAAA,MACF,GAAG;AAAA,MACH,UAAU;AAAA,QACN,GAAG;AAAA,QACH,OAAAA;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAEA,QAAM,eAAeF,QAA6B,IAAI;AAGtD,sBAAoB,QAAQ,MAAM;AAC9B,WAAO;AAAA,MACH,WAAW,MAAM,aAAa,SAAS,UAAU,KAAK;AAAA,MACtD,MAAM,MAAM,aAAa,SAAS,KAAK;AAAA,MACvC,OAAO,MAAM,aAAa,SAAS,MAAM;AAAA,MACzC,QAAQ,MAAM,aAAa,SAAS,OAAO;AAAA,MAC3C,QAAQ,MAAM,aAAa,SAAS,OAAO;AAAA,MAC3C,gBAAgB,MAAM,aAAa,SAAS,eAAe,KAAK;AAAA,MAChE,gBAAgB,CAACG,UAAiB,aAAa,SAAS,eAAeA,KAAI;AAAA,IAC/E;AAAA,EACJ,GAAG,CAAC,CAAC;AAGL,YAAU,MAAM;AAEZ,QAAI,aAAa,mBAAkC;AAC/C,UAAI,QAAQ,CAAC,OAAO;AAChB,qBAAa,SAAS,KAAK;AAAA,MAC/B,WAAW,OAAO;AACd,qBAAa,SAAS,MAAM;AAAA,MAChC,OAAO;AACH,qBAAa,SAAS,OAAO;AAAA,MACjC;AAAA,IACJ;AAEA,WAAO,MAAM;AACT,UAAI,aAAa,mBAAkC;AAC/C,qBAAa,SAAS,MAAM;AAAA,MAChC;AAAA,IACJ;AAAA,EACJ,GAAG,CAAC,UAAU,MAAM,KAAK,CAAC;AAG1B,QAAM,MAAM,eAAe,UAAU,GAAG;AAExC,SACI;AAAA,IAAC;AAAA;AAAA,MAEG;AAAA,MACA;AAAA,MACA;AAAA;AAAA,IAHK;AAAA,EAIT;AAER;AAEA,IAAO,8BAAQ;","names":["useRef","useRef","startOn","delay","time"]}
|
package/dist/index.umd.js
CHANGED
|
@@ -3278,11 +3278,7 @@ var PixodeskAnimatorReact = (() => {
|
|
|
3278
3278
|
const { type, animate, meta, children, ...props } = node;
|
|
3279
3279
|
const normProps = getNormalizedProps(props);
|
|
3280
3280
|
normProps["ref"] = (domEl) => {
|
|
3281
|
-
console.log("MOUNTED");
|
|
3282
3281
|
if (node["id"]) elementRefs.current.set(node["id"], domEl);
|
|
3283
|
-
return () => {
|
|
3284
|
-
console.log("UN-MOUNTED");
|
|
3285
|
-
};
|
|
3286
3282
|
};
|
|
3287
3283
|
return (0, import_react2.createElement)(type, normProps, children?.map((child) => renderNode2(child)));
|
|
3288
3284
|
};
|
|
@@ -3294,7 +3290,7 @@ var PixodeskAnimatorReact = (() => {
|
|
|
3294
3290
|
api?.destroy();
|
|
3295
3291
|
apiHolderRef.current = null;
|
|
3296
3292
|
};
|
|
3297
|
-
}, [doc]);
|
|
3293
|
+
}, [doc, apiHolderRef]);
|
|
3298
3294
|
return root;
|
|
3299
3295
|
};
|
|
3300
3296
|
var PixodeskSvgAnimatorImplOnce = import_react2.default.memo(
|
|
@@ -3395,7 +3391,6 @@ var PixodeskAnimatorReact = (() => {
|
|
|
3395
3391
|
}
|
|
3396
3392
|
};
|
|
3397
3393
|
}
|
|
3398
|
-
console.log("doc", doc);
|
|
3399
3394
|
const apiHolderRef = (0, import_react2.useRef)(null);
|
|
3400
3395
|
(0, import_react2.useImperativeHandle)(apiRef, () => {
|
|
3401
3396
|
return {
|