@pixodesk/svg-animator-vue 1.0.44 → 1.0.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +14 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +16 -54
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +5305 -603
- package/dist/index.umd.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -32,33 +32,6 @@ var import_svg_animator_core = require("@pixodesk/svg-animator-core");
|
|
|
32
32
|
var import_internal = require("@pixodesk/svg-animator-core/internal");
|
|
33
33
|
var import_vue = require("vue");
|
|
34
34
|
var VUE_PROP = { ref: "ref", style: "style" };
|
|
35
|
-
function toInlineStyle(style) {
|
|
36
|
-
if (!style) return void 0;
|
|
37
|
-
const inline = {};
|
|
38
|
-
for (const [prop, value] of Object.entries(style)) inline[prop] = String(value);
|
|
39
|
-
return inline;
|
|
40
|
-
}
|
|
41
|
-
function createVueAdapter(elementRefs, diag) {
|
|
42
|
-
const warnedSelectors = /* @__PURE__ */ new Set();
|
|
43
|
-
const adapter = {
|
|
44
|
-
isConnected: () => true,
|
|
45
|
-
setAttribute: (id, attrName, value) => {
|
|
46
|
-
attrName = (0, import_internal.camelCaseToKebabWordIfNeeded)(attrName);
|
|
47
|
-
const element = elementRefs.get(id);
|
|
48
|
-
if (!element && !warnedSelectors.has(id)) {
|
|
49
|
-
warnedSelectors.add(id);
|
|
50
|
-
diag.warn(import_svg_animator_web.PxDiagnosticKind.host, import_svg_animator_core.PxDiagnosticCode.setAttributeNoElement, id);
|
|
51
|
-
}
|
|
52
|
-
if (element) {
|
|
53
|
-
element.setAttribute(attrName, value);
|
|
54
|
-
if (import_internal.PX_STYLE_ATTR_NAMES.has(attrName)) {
|
|
55
|
-
element.style[attrName] = value;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
return adapter;
|
|
61
|
-
}
|
|
62
35
|
function applyDocOverrides(doc, props, compMode, diag) {
|
|
63
36
|
const { timeline, resetTimeline, duration, delay, iterations, start } = props;
|
|
64
37
|
const patch = (0, import_svg_animator_core.foldTimelineOverride)(timeline, { duration, delay, iterations, start });
|
|
@@ -122,7 +95,7 @@ var PixodeskSvgAnimator = (0, import_vue.defineComponent)({
|
|
|
122
95
|
},
|
|
123
96
|
emits: ["play", "stop", "pause", "cancel", "finish", "remove"],
|
|
124
97
|
setup(props, { expose, emit }) {
|
|
125
|
-
|
|
98
|
+
let rootElement = null;
|
|
126
99
|
const apiRef = (0, import_vue.shallowRef)(null);
|
|
127
100
|
const makeDiag = () => (0, import_internal.createDiagnostics)(
|
|
128
101
|
{ onWarn: props.onWarn, onError: props.onError, muteWarn: props.muteWarn, muteError: props.muteError },
|
|
@@ -141,36 +114,25 @@ var PixodeskSvgAnimator = (0, import_vue.defineComponent)({
|
|
|
141
114
|
for (const w of r.warnings) diag.warn(import_svg_animator_web.PxDiagnosticKind.usage, import_svg_animator_core.PxDiagnosticCode.controlPropsConflict, w);
|
|
142
115
|
}, { immediate: true });
|
|
143
116
|
const resolvedDoc = (0, import_vue.computed)(() => {
|
|
144
|
-
|
|
145
|
-
return
|
|
117
|
+
const doc = applyDocOverrides(props.doc, props, compMode.value, makeDiag());
|
|
118
|
+
return (0, import_internal.prepareDocumentForRender)(doc);
|
|
146
119
|
});
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
normProps[VUE_PROP.ref] = (el) => {
|
|
156
|
-
if (el) {
|
|
157
|
-
elementRefs.set(nodeId, el);
|
|
158
|
-
} else {
|
|
159
|
-
elementRefs.delete(nodeId);
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
const childVNodes = children?.map((child) => renderNode(child)).filter(Boolean);
|
|
164
|
-
const text = typeof node.textContent === "string" ? node.textContent : void 0;
|
|
165
|
-
return (0, import_vue.h)(type, normProps, childVNodes?.length ? childVNodes : text);
|
|
166
|
-
}
|
|
120
|
+
const createVNode = ({ tag, attrs, style, children, text, node, isRoot, index }) => {
|
|
121
|
+
const vnodeProps = { ...attrs, key: node.id ?? index };
|
|
122
|
+
if (style) vnodeProps[VUE_PROP.style] = style;
|
|
123
|
+
if (isRoot) vnodeProps[VUE_PROP.ref] = (el) => {
|
|
124
|
+
rootElement = el;
|
|
125
|
+
};
|
|
126
|
+
return (0, import_vue.h)(tag, vnodeProps, children.length ? children : text);
|
|
127
|
+
};
|
|
167
128
|
function createApi() {
|
|
168
129
|
destroyApi();
|
|
169
130
|
const doc = resolvedDoc.value;
|
|
170
131
|
if (!doc) return;
|
|
171
132
|
const options = {
|
|
172
133
|
doc,
|
|
173
|
-
|
|
134
|
+
// Mounted (or `flush: 'post'`) by the time this runs, so the root exists.
|
|
135
|
+
rootElement: rootElement ?? void 0,
|
|
174
136
|
onPlay: () => emit("play"),
|
|
175
137
|
onPause: () => emit("pause"),
|
|
176
138
|
onCancel: () => emit("cancel"),
|
|
@@ -235,7 +197,7 @@ var PixodeskSvgAnimator = (0, import_vue.defineComponent)({
|
|
|
235
197
|
expose(publicApi);
|
|
236
198
|
return () => {
|
|
237
199
|
const doc = resolvedDoc.value;
|
|
238
|
-
return doc
|
|
200
|
+
return (0, import_internal.renderPxTree)(doc, createVNode, makeDiag());
|
|
239
201
|
};
|
|
240
202
|
}
|
|
241
203
|
});
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/PixodeskSvgAnimator.ts","../src/PixodeskSvgCssAnimator.ts"],"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 { PixodeskSvgAnimator };\nexport type { VueAnimatorApi } from './PixodeskSvgAnimator';\n\nimport PixodeskSvgCssAnimator from './PixodeskSvgCssAnimator';\nexport { PixodeskSvgCssAnimator };","/*---------------------------------------------------------------------------------------\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 { PxAnimatedSvgDocument, PxAnimatorApi, PxNode, PxPlatformAdapter, PxTimelineEngineSetting, PxTimelinePatch, PxTrigger } from '@pixodesk/svg-animator-web';\nimport type { PxInternalAnimatorOptions } from '@pixodesk/svg-animator-web/internal';\nimport { createAnimator, generateNewIds, toDomProps, PxDiagnosticKind, type PxPlaybackOverride, type PxDiagnostic, type PxDiagnostics } from '@pixodesk/svg-animator-web';\nimport { applyAnimatorConfig, foldTimelineOverride, getAnimatorConfig, PxDiagnosticCode, PxControlMode, resolveControlMode, controlModeTakesOverTrigger, progressToTimeMs, type PxAnimatorHandle, type PxControlProps } from '@pixodesk/svg-animator-core';\nimport { camelCaseToKebabWordIfNeeded, createDiagnostics, PX_STYLE_ATTR_NAMES, PX_DEFAULT_DURATION_MS } from '@pixodesk/svg-animator-core/internal';\nimport {\n computed, defineComponent, h, onMounted, onUnmounted, ref, shallowRef, type PropType, type VNode,\n watch,\n} from 'vue';\n\n\n// -- Public types -----------------------------------------------------------\n\n/** Vue's own prop names on the vnode — not wire keys. See the React twin. */\nconst VUE_PROP = { ref: 'ref', style: 'style' } as const;\n\n/** `node.style` (camelCase declarations) as an element style object — values stringified, exactly\n * as the web player writes them (`element.style[prop] = String(value)`). */\nfunction toInlineStyle(style: PxNode['style']): Record<string, string> | undefined {\n if (!style) return undefined;\n const inline: Record<string, string> = {};\n for (const [prop, value] of Object.entries(style)) inline[prop] = String(value);\n return inline;\n}\n\n/**\n * The imperative handle the template ref exposes — core's `PxAnimatorHandle` under this\n * package's name (review §9). One definition for React, Vue and React Native.\n * @public\n */\nexport type VueAnimatorApi = PxAnimatorHandle;\n\n\n// -- Internal types ---------------------------------------------------------\n\n// The control mode is core's `PxControlMode`, shared with React and React Native\n// (API review §1/§7) — one precedence rule, one set of conflict warnings.\n\n\n// -- Vue ↔ Animator bridge --------------------------------------------------\n\n/**\n * Creates a platform adapter that routes animator attribute updates\n * to the corresponding Vue-managed DOM element refs.\n */\nfunction createVueAdapter(elementRefs: Map<string, Element>, diag: PxDiagnostics) {\n const warnedSelectors = new Set<string>();\n\n const adapter: PxPlatformAdapter = {\n isConnected: () => true,\n setAttribute: (id, attrName, value) => {\n attrName = camelCaseToKebabWordIfNeeded(attrName);\n\n const element = elementRefs.get(id);\n\n if (!element && !warnedSelectors.has(id)) {\n warnedSelectors.add(id);\n diag.warn(PxDiagnosticKind.host, PxDiagnosticCode.setAttributeNoElement, id);\n }\n\n if (element) {\n element.setAttribute(attrName, value);\n if (PX_STYLE_ATTR_NAMES.has(attrName)) {\n (element as HTMLElement).style[attrName as any] = value;\n }\n }\n },\n };\n return adapter;\n}\n\n// FIXME: add model validation (e.g. isElementFileJson check)\n\n\n// -- Helper: apply doc overrides --------------------------------------------\n\n/**\n * What `applyDocOverrides` / `calcSeekMs` read off the props — core's shared shapes, not a local\n * copy (review §9). The runtime `props: {…}` block below stays Vue's own, because Vue needs\n * runtime prop declarations; this is only the TypeScript view of the same members.\n */\ntype DocOverrideProps = PxPlaybackOverride & Pick<PxControlProps, 'progress' | 'time'>;\n\nfunction applyDocOverrides(\n doc: PxAnimatedSvgDocument,\n props: DocOverrideProps,\n compMode: PxControlMode,\n diag: PxDiagnostics,\n): PxAnimatedSvgDocument {\n\n // ONE patch, applied ONCE: the props, plus the component's own need to take the trigger\n // over in the non-autoplay control modes.\n //\n // This replaces three hand-rolled spread blocks that wrote the FLAT runtime keys. On a\n // wire-format document — which is what every writer emits — `flattenAnimatorTimeline`\n // overwrote them from `timeline` immediately afterwards, so the overrides were silently\n // discarded. See dev-docs/plans/playback-override.md §1.1.\n const { timeline, resetTimeline, duration, delay, iterations, start } = props;\n const patch = foldTimelineOverride(timeline, { duration, delay, iterations, start });\n const fullPatch: any = controlModeTakesOverTrigger(compMode)\n ? {\n ...(patch ?? {}),\n timeline: {\n ...((patch as any)?.timeline ?? {}),\n trigger: { ...((patch as any)?.timeline?.trigger ?? {}), start: 'none' },\n },\n }\n : patch;\n\n if (fullPatch !== undefined || resetTimeline) {\n const applied = applyAnimatorConfig(doc, fullPatch ?? {}, { resetTimeline: !!resetTimeline });\n for (const w of applied.warnings) diag.warn(PxDiagnosticKind.usage, PxDiagnosticCode.timelineOverrideIgnored, w);\n doc = applied.doc;\n }\n\n return doc;\n}\n\n/**\n * Controlled-time mode: absolute seek target in ms. `progress` is a fraction\n * (0–1) of the WHOLE timeline (duration × iterations — ONE iteration when\n * endless); `time` is absolute. Applied through the animator API\n * (setCurrentTime) so scrubbing does NOT recreate the animator.\n */\nfunction calcSeekMs(doc: PxAnimatedSvgDocument, props: DocOverrideProps): number | undefined {\n let seekMs: number | undefined;\n // The FLAT runtime view, so this works on a wire-format document too — reading\n // `doc.animator.duration` directly finds nothing there.\n const animator = getAnimatorConfig(doc) || {};\n if (props.progress !== undefined) {\n // ONE rule for progress → time (core's `progressToTimeMs`): the same mapping\n // `getCurrentProgress` reads back, so a prop of 0.5 and a read of 0.5 agree.\n const iterationsValue = props.iterations ?? animator.iterations;\n const iterationsCount = iterationsValue === 'infinite' ? Infinity\n : (typeof iterationsValue === 'number' && iterationsValue >= 1 ? iterationsValue : 1);\n const singleDuration = props.duration ?? animator.duration ?? PX_DEFAULT_DURATION_MS;\n seekMs = progressToTimeMs(props.progress, singleDuration, iterationsCount);\n }\n if (props.time !== undefined) seekMs = props.time;\n return seekMs;\n}\n\n\n// -- Main public component --------------------------------------------------\n\n/**\n * Vue 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 * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" autoplay />\n * ```\n *\n * 2. **Declarative play/pause** – controlled via boolean props.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" play :pause=\"false\" />\n * ```\n *\n * 3. **Imperative** – exposes a ref-based API for full programmatic control.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" ref=\"animator\" />\n * <button @click=\"$refs.animator.play()\">Play</button>\n * ```\n *\n * 4. **Controlled time** – renders a single frame at a given time.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" :progress=\"0.5\" />\n * <PixodeskSvgAnimator :doc=\"animation\" :time=\"500\" />\n * ```\n * @public\n */\nconst PixodeskSvgAnimator = defineComponent({\n name: 'PixodeskSvgAnimator',\n\n props: {\n // -- Source\n doc: { type: Object as PropType<PxAnimatedSvgDocument>, required: true },\n\n // -- Playback override: the document's `timeline` block as a patch (or a JSON string),\n // plus the four shortcuts people reach for most. The same names as React / RN.\n timeline: { type: [Object, String] as PropType<PxTimelinePatch | string> },\n resetTimeline: { type: Boolean, default: undefined },\n duration: { type: Number },\n delay: { type: Number },\n iterations: { type: [Number, String] as PropType<number | 'infinite'> },\n start: { type: String as PropType<'load' | 'mouseOver' | 'click' | 'none'> },\n\n // -- Declarative control\n autoplay: { type: Boolean, default: undefined },\n play: { type: Boolean, default: undefined },\n pause: { type: Boolean, default: undefined },\n\n // -- Controlled time\n progress: { type: Number },\n time: { type: Number },\n\n // -- Diagnostics (API review §5).\n // Function PROPS, not emits, on purpose: an emit handler always exists, so wiring\n // these to `emit` would permanently suppress the console fallback for anyone who\n // never listens. As props, \"not given\" really is undefined and the console still\n // speaks by default — the same contract as React and React Native.\n onWarn: { type: Function as PropType<(diagnostic: PxDiagnostic) => void> },\n onError: { type: Function as PropType<(diagnostic: PxDiagnostic) => void> },\n // `muteWarn` / `muteError` switch the console fallback off — for a host that knows\n // about the warnings and tolerates them; a handler you passed still fires.\n muteWarn: { type: Boolean, default: undefined },\n muteError: { type: Boolean, default: undefined },\n },\n\n emits: ['play', 'stop', 'pause', 'cancel', 'finish', 'remove'],\n\n setup(props, { expose, emit }) {\n const elementRefs = new Map<string, Element>();\n const apiRef = shallowRef<PxAnimatorApi | null>(null);\n\n /**\n * The diagnostics channel, built from the CURRENT props each time (API review §5).\n * Not hoisted into a constant wrapper: `(m, d) => props.onWarn?.(m, d)` would always be\n * a function, so the channel would think a handler exists and the console fallback\n * would never fire for anyone who passed nothing.\n */\n const makeDiag = (): PxDiagnostics => createDiagnostics(\n { onWarn: props.onWarn, onError: props.onError, muteWarn: props.muteWarn, muteError: props.muteError },\n '[PixodeskSvgAnimator]',\n );\n\n // -- Determine control mode ---------------------------------------------\n\n // ONE control-mode rule, decided in core (API review §1/§7). The template ref is not an\n // input: it is exposed in every mode and never changes which one is chosen.\n const resolvedMode = computed(() => resolveControlMode({\n progress: props.progress, time: props.time,\n play: props.play, pause: props.pause, autoplay: props.autoplay,\n }));\n const compMode = computed<PxControlMode>(() => resolvedMode.value.mode);\n\n // Warn where the mode is COMPUTED, not inside `applyDocOverrides` — that runs again on\n // every doc recompute and would repeat the same sentence.\n watch(resolvedMode, r => {\n const diag = makeDiag();\n for (const w of r.warnings) diag.warn(PxDiagnosticKind.usage, PxDiagnosticCode.controlPropsConflict, w);\n }, { immediate: true });\n\n // -- Prepare the document with overrides --------------------------------\n\n const resolvedDoc = computed(() => {\n let doc = generateNewIds(props.doc);\n return applyDocOverrides(doc, props, compMode.value, makeDiag());\n });\n\n // -- Render the SVG node tree -------------------------------------------\n\n function renderNode(node: PxNode | undefined): VNode | null {\n if (!node) return null;\n\n const { type, animate, meta, children, style, ...attrs } = node;\n const normProps = toDomProps(attrs);\n\n // `node.style` is not an attribute (`toDomProps` drops it) — it is the element's style.\n const inlineStyle = toInlineStyle(style);\n if (inlineStyle) normProps[VUE_PROP.style] = inlineStyle;\n\n // Capture a ref to each element with an id.\n if (node.id) {\n const nodeId = node.id;\n normProps[VUE_PROP.ref] = (el: Element | null) => {\n if (el) {\n elementRefs.set(nodeId, el);\n } else {\n elementRefs.delete(nodeId);\n }\n };\n }\n\n const childVNodes = children?.map(child => renderNode(child)).filter(Boolean) as VNode[] | undefined;\n // Text content: a node's own `textContent` renders only when it has no child nodes — a\n // line <tspan> can carry both, and then its children are the styled spans (the React\n // Native renderer's rule). `toDomProps` strips `textContent` from the attributes.\n const text = typeof node.textContent === 'string' ? node.textContent : undefined;\n return h(type, normProps, childVNodes?.length ? childVNodes : text);\n }\n\n // -- Animator lifecycle -------------------------------------------------\n\n function createApi() {\n destroyApi();\n const doc = resolvedDoc.value;\n if (!doc) return;\n\n // Route animator lifecycle events to Vue component events, INLINE under the same\n // names every surface uses (review §9). `stop` fires alongside any event that halts\n // playback — that is the PLAYER's rule, so `onStop` is passed through, not re-derived.\n // `adapter` is the components' extension of the public options (`PxInternalAnimatorOptions`,\n // review §25.14): the frame loop writes to the elements Vue rendered, not to a container.\n const options: PxInternalAnimatorOptions = {\n doc,\n adapter: createVueAdapter(elementRefs, makeDiag()),\n onPlay: () => emit('play'),\n onPause: () => emit('pause'),\n onCancel: () => emit('cancel'),\n onFinish: () => emit('finish'),\n onRemove: () => emit('remove'),\n onStop: () => emit('stop'),\n // The player's own diagnostics reach the same handlers as the component's.\n onWarn: props.onWarn,\n onError: props.onError,\n muteWarn: props.muteWarn,\n muteError: props.muteError,\n };\n apiRef.value = createAnimator(options);\n\n // (Re)apply the declarative control state to the fresh animator —\n // covers both the initial mount (e.g. `:play=\"true\"` from the\n // start) and doc swaps.\n syncPlayState();\n applySeek();\n }\n\n function destroyApi() {\n apiRef.value?.destroy();\n apiRef.value = null;\n }\n\n /** Declarative play/pause → animator calls. */\n function syncPlayState() {\n if (compMode.value !== PxControlMode.play) return;\n if (props.play && !props.pause) {\n apiRef.value?.play();\n } else if (props.pause) {\n apiRef.value?.pause();\n } else if (props.play === false) {\n // explicit play=false → hold where it is (review §8). This used to `finish()`;\n // a boolean whose `false` means \"jump to the end\" is not what anyone guesses.\n apiRef.value?.pause();\n } else {\n // pause-only usage: pause switched off → resume\n apiRef.value?.play();\n }\n }\n\n /** Controlled-time mode: seek through the animator API (no recreate). */\n function applySeek() {\n if (compMode.value !== PxControlMode.fixedTime) return;\n const doc = resolvedDoc.value;\n if (!doc) return;\n const seekMs = calcSeekMs(doc, props);\n if (seekMs !== undefined) {\n apiRef.value?.setCurrentTime(seekMs);\n apiRef.value?.pause();\n }\n }\n\n // Create the animator once DOM refs are available.\n onMounted(() => createApi());\n\n // Recreate the animator when the resolved doc changes.\n // `flush: 'post'` — the animator must be created AFTER the DOM is\n // patched, otherwise the new root element isn't in the document yet\n // and trigger setup fails (autoplay would never resume after a doc swap).\n watch(resolvedDoc, () => createApi(), { flush: 'post' });\n\n // Sync declarative play/pause props with the animator.\n watch([compMode, () => props.play, () => props.pause], () => syncPlayState());\n\n // Scrubbing progress/time only seeks — the animator is NOT recreated.\n watch([compMode, () => props.progress, () => props.time], () => applySeek());\n\n onUnmounted(() => {\n destroyApi();\n });\n\n // -- Expose imperative API ----------------------------------------------\n\n const publicApi: VueAnimatorApi = {\n isPlaying: () => apiRef.value?.isPlaying() || false,\n play: () => apiRef.value?.play(),\n pause: () => apiRef.value?.pause(),\n cancel: () => apiRef.value?.cancel(),\n finish: () => apiRef.value?.finish(),\n setPlaybackRate: (rate: number) => apiRef.value?.setPlaybackRate(rate),\n getCurrentTime: () => apiRef.value?.getCurrentTime() ?? null,\n setCurrentTime: (time: number) => apiRef.value?.setCurrentTime(time),\n getCurrentProgress: () => apiRef.value?.getCurrentProgress() ?? null,\n setCurrentProgress: (progress: number) => apiRef.value?.setCurrentProgress(progress),\n };\n\n expose(publicApi);\n\n // -- Render -------------------------------------------------------------\n\n return () => {\n const doc = resolvedDoc.value;\n return doc ? renderNode(doc) : null;\n };\n },\n});\n\nexport default PixodeskSvgAnimator;\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 { PX_TRIGGER_DEFAULTS, PxMouseOutAction, PxOffScreenAction, PxTriggerStart } from '@pixodesk/svg-animator-core';\nimport { createVisibilityGate, type PxVisibilityGate } from '@pixodesk/svg-animator-web/internal';\nimport { computed, defineComponent, h, onBeforeUnmount, onMounted, ref, watch, type PropType } from 'vue';\n\n\ntype AnimState = 'idle' | 'paused' | 'playing';\n\n/**\n * Controls playback of 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 Vue component\n * (`vite-svg-loader`) and pass it in the default slot:\n *\n * ```vue\n * <script setup>\n * import AnimationSvg from './animation.svg?component';\n * </script>\n *\n * <template>\n * <PixodeskSvgCssAnimator start=\"mouseOver\" mouseOut=\"pause\">\n * <AnimationSvg />\n * </PixodeskSvgCssAnimator>\n * </template>\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 * TWO AXES, the same two the JSON player has: `start` says what STARTS it, `offScreen` says\n * whether it may RUN. The gate itself is the player's (`createVisibilityGate`), so the threshold,\n * the dwell, the hysteresis and the hidden-tab rule cannot drift between the two.\n *\n * @prop start - What starts the animation:\n * - `'load'` — plays as soon as it is visible enough (default)\n * - `'mouseOver'` — plays on hover\n * - `'click'` — plays on click, toggles on second click\n * - `'none'` — **not supported here.** This wrapper exposes no `play()`, so there is\n * nothing to wait for and the animation never starts. The prop keeps the\n * shared `PxTriggerStart` type (one enum per wire key), so the value\n * type-checks — it simply has no effect. Use `PixodeskSvgAnimator` (the JSON\n * player) when you need to start an animation from code.\n * @prop offScreen - What happens while none of it is on screen:\n * - `'pause'` — pauses at the current frame (default); resumes when it comes back\n * - `'continue'` — keeps playing, and starts without waiting to be seen\n * - `'reset'` — back to the beginning, so it replays on the next entry\n * @prop mouseOut - What happens when the pointer leaves, for `start: 'mouseOver'`:\n * - `'continue'` — keeps playing (default)\n * - `'pause'` — pauses at the current frame\n * - `'reset'` — resets to the beginning\n * - `'reverse'` — **acts as `'continue'` here.** A CSS class toggle cannot run keyframes\n * backwards. Accepted so the prop keeps the shared `PxMouseOutAction` type.\n * @prop visibilityThreshold - How much of the element must be on screen (0–1) before it may run.\n * Defaults to the wire default (`0.5`), the same as the JSON player.\n * @prop visibilityDebounce - How long that must hold, in ms, before it starts. Defaults to the\n * wire default (`150`).\n * @public\n */\nconst PixodeskSvgCssAnimator = defineComponent({\n name: 'PixodeskSvgCssAnimator',\n // The wrapper spreads `attrs` itself and merges `class` by hand, so Vue must not ALSO\n // apply them — otherwise a host class lands on the div twice.\n inheritAttrs: false,\n props: {\n start: { type: String as PropType<PxTriggerStart>, default: PX_TRIGGER_DEFAULTS.start },\n offScreen: { type: String as PropType<PxOffScreenAction>, default: PX_TRIGGER_DEFAULTS.offScreen },\n mouseOut: { type: String as PropType<PxMouseOutAction>, default: PX_TRIGGER_DEFAULTS.mouseOut },\n visibilityThreshold: { type: Number, default: PX_TRIGGER_DEFAULTS.visibilityThreshold },\n visibilityDebounce: { type: Number, default: PX_TRIGGER_DEFAULTS.visibilityDebounce },\n },\n setup(props, { slots, attrs }) {\n const state = ref<AnimState>('idle');\n const el = ref<HTMLDivElement | null>(null);\n let gate: PxVisibilityGate | null = null;\n\n const goOut = (): void => {\n state.value =\n props.mouseOut === PxMouseOutAction.reset ? 'idle' :\n props.mouseOut === PxMouseOutAction.pause ? 'paused' : 'playing';\n };\n\n const attach = (): void => {\n gate?.dispose();\n gate = null;\n const node = el.value;\n if (!node) return;\n gate = createVisibilityGate(node, {\n start: props.start,\n offScreen: props.offScreen,\n mouseOut: props.mouseOut,\n visibilityThreshold: props.visibilityThreshold,\n visibilityDebounce: props.visibilityDebounce,\n }, {\n isPlaying: () => state.value === 'playing',\n play: () => { state.value = 'playing'; },\n pause: () => { state.value = 'paused'; },\n cancel: () => { state.value = 'idle'; },\n });\n // Only `load` is held by the gate; a hover or a click is aimed at something seen.\n if (props.start === PxTriggerStart.load) gate.requestStart(false);\n };\n\n onMounted(attach);\n watch(() => [props.start, props.offScreen, props.mouseOut, props.visibilityThreshold, props.visibilityDebounce], attach);\n onBeforeUnmount(() => { gate?.dispose(); gate = null; });\n\n const requestStart = (): void => {\n if (gate) gate.requestStart(true);\n else state.value = 'playing';\n };\n\n const cssClass = computed(() =>\n state.value === 'playing' ? 'px-anim-enabled px-anim-playing' :\n state.value === 'paused' ? 'px-anim-enabled' : ''\n );\n\n const handlers = computed(() =>\n props.start === PxTriggerStart.mouseOver\n ? { onMouseenter: requestStart, onMouseleave: goOut }\n : props.start === PxTriggerStart.click\n ? { onClick: () => { if (state.value === 'playing') state.value = 'paused'; else requestStart(); } }\n : {}\n );\n\n return () => h('div', {\n ref: el,\n ...attrs,\n class: [attrs.class, cssClass.value],\n ...handlers.value,\n }, slots.default?.());\n },\n});\nexport default PixodeskSvgCssAnimator;\n"],"mappings":"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","names":["import_svg_animator_core","import_internal","import_vue"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/PixodeskSvgAnimator.ts","../src/PixodeskSvgCssAnimator.ts"],"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 { PixodeskSvgAnimator };\nexport type { VueAnimatorApi } from './PixodeskSvgAnimator';\n\nimport PixodeskSvgCssAnimator from './PixodeskSvgCssAnimator';\nexport { PixodeskSvgCssAnimator };","/*---------------------------------------------------------------------------------------\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 { PxAnimatedSvgDocument, PxAnimatorApi, PxTimelineEngineSetting, PxTimelinePatch, PxTrigger } from '@pixodesk/svg-animator-web';\nimport type { PxInternalAnimatorOptions } from '@pixodesk/svg-animator-web/internal';\nimport { createAnimator, PxDiagnosticKind, type PxPlaybackOverride, type PxDiagnostic, type PxDiagnostics } from '@pixodesk/svg-animator-web';\nimport { applyAnimatorConfig, foldTimelineOverride, getAnimatorConfig, PxDiagnosticCode, PxControlMode, resolveControlMode, controlModeTakesOverTrigger, progressToTimeMs, type PxAnimatorHandle, type PxControlProps } from '@pixodesk/svg-animator-core';\nimport { createDiagnostics, PX_DEFAULT_DURATION_MS, prepareDocumentForRender, renderPxTree, type PxElementFactory } from '@pixodesk/svg-animator-core/internal';\nimport {\n computed, defineComponent, h, onMounted, onUnmounted, ref, shallowRef, type PropType, type VNode,\n watch,\n} from 'vue';\n\n\n// -- Public types -----------------------------------------------------------\n\n/** Vue's own prop names on the vnode — not wire keys. See the React twin. */\nconst VUE_PROP = { ref: 'ref', style: 'style' } as const;\n\n/**\n * The imperative handle the template ref exposes — core's `PxAnimatorHandle` under this\n * package's name (review §9). One definition for React, Vue and React Native.\n * @public\n */\nexport type VueAnimatorApi = PxAnimatorHandle;\n\n\n// -- Internal types ---------------------------------------------------------\n\n// The control mode is core's `PxControlMode`, shared with React and React Native\n// (API review §1/§7) — one precedence rule, one set of conflict warnings.\n\n\n// FIXME: add model validation (e.g. isElementFileJson check)\n\n\n// -- Helper: apply doc overrides --------------------------------------------\n\n/**\n * What `applyDocOverrides` / `calcSeekMs` read off the props — core's shared shapes, not a local\n * copy (review §9). The runtime `props: {…}` block below stays Vue's own, because Vue needs\n * runtime prop declarations; this is only the TypeScript view of the same members.\n */\ntype DocOverrideProps = PxPlaybackOverride & Pick<PxControlProps, 'progress' | 'time'>;\n\nfunction applyDocOverrides(\n doc: PxAnimatedSvgDocument,\n props: DocOverrideProps,\n compMode: PxControlMode,\n diag: PxDiagnostics,\n): PxAnimatedSvgDocument {\n\n // ONE patch, applied ONCE: the props, plus the component's own need to take the trigger\n // over in the non-autoplay control modes.\n //\n // This replaces three hand-rolled spread blocks that wrote the FLAT runtime keys. On a\n // wire-format document — which is what every writer emits — `flattenAnimatorTimeline`\n // overwrote them from `timeline` immediately afterwards, so the overrides were silently\n // discarded. See dev-docs/plans/playback-override.md §1.1.\n const { timeline, resetTimeline, duration, delay, iterations, start } = props;\n const patch = foldTimelineOverride(timeline, { duration, delay, iterations, start });\n const fullPatch: any = controlModeTakesOverTrigger(compMode)\n ? {\n ...(patch ?? {}),\n timeline: {\n ...((patch as any)?.timeline ?? {}),\n trigger: { ...((patch as any)?.timeline?.trigger ?? {}), start: 'none' },\n },\n }\n : patch;\n\n if (fullPatch !== undefined || resetTimeline) {\n const applied = applyAnimatorConfig(doc, fullPatch ?? {}, { resetTimeline: !!resetTimeline });\n for (const w of applied.warnings) diag.warn(PxDiagnosticKind.usage, PxDiagnosticCode.timelineOverrideIgnored, w);\n doc = applied.doc;\n }\n\n return doc;\n}\n\n/**\n * Controlled-time mode: absolute seek target in ms. `progress` is a fraction\n * (0–1) of the WHOLE timeline (duration × iterations — ONE iteration when\n * endless); `time` is absolute. Applied through the animator API\n * (setCurrentTime) so scrubbing does NOT recreate the animator.\n */\nfunction calcSeekMs(doc: PxAnimatedSvgDocument, props: DocOverrideProps): number | undefined {\n let seekMs: number | undefined;\n // The FLAT runtime view, so this works on a wire-format document too — reading\n // `doc.animator.duration` directly finds nothing there.\n const animator = getAnimatorConfig(doc) || {};\n if (props.progress !== undefined) {\n // ONE rule for progress → time (core's `progressToTimeMs`): the same mapping\n // `getCurrentProgress` reads back, so a prop of 0.5 and a read of 0.5 agree.\n const iterationsValue = props.iterations ?? animator.iterations;\n const iterationsCount = iterationsValue === 'infinite' ? Infinity\n : (typeof iterationsValue === 'number' && iterationsValue >= 1 ? iterationsValue : 1);\n const singleDuration = props.duration ?? animator.duration ?? PX_DEFAULT_DURATION_MS;\n seekMs = progressToTimeMs(props.progress, singleDuration, iterationsCount);\n }\n if (props.time !== undefined) seekMs = props.time;\n return seekMs;\n}\n\n\n// -- Main public component --------------------------------------------------\n\n/**\n * Vue 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 * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" autoplay />\n * ```\n *\n * 2. **Declarative play/pause** – controlled via boolean props.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" play :pause=\"false\" />\n * ```\n *\n * 3. **Imperative** – exposes a ref-based API for full programmatic control.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" ref=\"animator\" />\n * <button @click=\"$refs.animator.play()\">Play</button>\n * ```\n *\n * 4. **Controlled time** – renders a single frame at a given time.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" :progress=\"0.5\" />\n * <PixodeskSvgAnimator :doc=\"animation\" :time=\"500\" />\n * ```\n * @public\n */\nconst PixodeskSvgAnimator = defineComponent({\n name: 'PixodeskSvgAnimator',\n\n props: {\n // -- Source\n doc: { type: Object as PropType<PxAnimatedSvgDocument>, required: true },\n\n // -- Playback override: the document's `timeline` block as a patch (or a JSON string),\n // plus the four shortcuts people reach for most. The same names as React / RN.\n timeline: { type: [Object, String] as PropType<PxTimelinePatch | string> },\n resetTimeline: { type: Boolean, default: undefined },\n duration: { type: Number },\n delay: { type: Number },\n iterations: { type: [Number, String] as PropType<number | 'infinite'> },\n start: { type: String as PropType<'load' | 'mouseOver' | 'click' | 'none'> },\n\n // -- Declarative control\n autoplay: { type: Boolean, default: undefined },\n play: { type: Boolean, default: undefined },\n pause: { type: Boolean, default: undefined },\n\n // -- Controlled time\n progress: { type: Number },\n time: { type: Number },\n\n // -- Diagnostics (API review §5).\n // Function PROPS, not emits, on purpose: an emit handler always exists, so wiring\n // these to `emit` would permanently suppress the console fallback for anyone who\n // never listens. As props, \"not given\" really is undefined and the console still\n // speaks by default — the same contract as React and React Native.\n onWarn: { type: Function as PropType<(diagnostic: PxDiagnostic) => void> },\n onError: { type: Function as PropType<(diagnostic: PxDiagnostic) => void> },\n // `muteWarn` / `muteError` switch the console fallback off — for a host that knows\n // about the warnings and tolerates them; a handler you passed still fires.\n muteWarn: { type: Boolean, default: undefined },\n muteError: { type: Boolean, default: undefined },\n },\n\n emits: ['play', 'stop', 'pause', 'cancel', 'finish', 'remove'],\n\n setup(props, { expose, emit }) {\n // The `<svg>` this component rendered — handed to the player, which attaches its\n // triggers to it and writes animated values to the elements under it.\n let rootElement: Element | null = null;\n const apiRef = shallowRef<PxAnimatorApi | null>(null);\n\n /**\n * The diagnostics channel, built from the CURRENT props each time (API review §5).\n * Not hoisted into a constant wrapper: `(m, d) => props.onWarn?.(m, d)` would always be\n * a function, so the channel would think a handler exists and the console fallback\n * would never fire for anyone who passed nothing.\n */\n const makeDiag = (): PxDiagnostics => createDiagnostics(\n { onWarn: props.onWarn, onError: props.onError, muteWarn: props.muteWarn, muteError: props.muteError },\n '[PixodeskSvgAnimator]',\n );\n\n // -- Determine control mode ---------------------------------------------\n\n // ONE control-mode rule, decided in core (API review §1/§7). The template ref is not an\n // input: it is exposed in every mode and never changes which one is chosen.\n const resolvedMode = computed(() => resolveControlMode({\n progress: props.progress, time: props.time,\n play: props.play, pause: props.pause, autoplay: props.autoplay,\n }));\n const compMode = computed<PxControlMode>(() => resolvedMode.value.mode);\n\n // Warn where the mode is COMPUTED, not inside `applyDocOverrides` — that runs again on\n // every doc recompute and would repeat the same sentence.\n watch(resolvedMode, r => {\n const diag = makeDiag();\n for (const w of r.warnings) diag.warn(PxDiagnosticKind.usage, PxDiagnosticCode.controlPropsConflict, w);\n }, { immediate: true });\n\n // -- Prepare the document with overrides --------------------------------\n\n const resolvedDoc = computed(() => {\n // Overrides FIRST: they can change `timeline.engine` and `duration`, and\n // materialization reads both (the engine picks the stages, the duration sizes loop\n // expansion). Then render what the web player renders — materialized, fresh ids.\n // Rendering the raw document dropped every `effects` bucket, so a document painted\n // by `effects` alone came out as an empty canvas.\n const doc = applyDocOverrides(props.doc, props, compMode.value, makeDiag());\n return prepareDocumentForRender(doc);\n });\n\n // -- Render the SVG node tree -------------------------------------------\n\n // The ONLY Vue-specific part of rendering: how to create a vnode. Every decision about the\n // document — tags, attribute names and values, sanitization, styles, text — is made by\n // core's `renderPxTree`, the same code the web player renders with. Vue writes an SVG\n // attribute name verbatim, so the names it is handed are used as they are.\n const createVNode: PxElementFactory<VNode> = ({ tag, attrs, style, children, text, node, isRoot, index }) => {\n const vnodeProps: Record<string, unknown> = { ...attrs, key: node.id ?? index };\n if (style) vnodeProps[VUE_PROP.style] = style;\n if (isRoot) vnodeProps[VUE_PROP.ref] = (el: Element | null) => { rootElement = el; };\n return h(tag, vnodeProps, children.length ? children : text);\n };\n\n // -- Animator lifecycle -------------------------------------------------\n\n function createApi() {\n destroyApi();\n const doc = resolvedDoc.value;\n if (!doc) return;\n\n // Route animator lifecycle events to Vue component events, INLINE under the same\n // names every surface uses (review §9). `stop` fires alongside any event that halts\n // playback — that is the PLAYER's rule, so `onStop` is passed through, not re-derived.\n // No `adapter`: the web player's OWN writer drives the elements Vue rendered, found by\n // id under the root. A second writer here was a second place for rules to go missing.\n const options: PxInternalAnimatorOptions = {\n doc,\n // Mounted (or `flush: 'post'`) by the time this runs, so the root exists.\n rootElement: rootElement ?? undefined,\n onPlay: () => emit('play'),\n onPause: () => emit('pause'),\n onCancel: () => emit('cancel'),\n onFinish: () => emit('finish'),\n onRemove: () => emit('remove'),\n onStop: () => emit('stop'),\n // The player's own diagnostics reach the same handlers as the component's.\n onWarn: props.onWarn,\n onError: props.onError,\n muteWarn: props.muteWarn,\n muteError: props.muteError,\n };\n apiRef.value = createAnimator(options);\n\n // (Re)apply the declarative control state to the fresh animator —\n // covers both the initial mount (e.g. `:play=\"true\"` from the\n // start) and doc swaps.\n syncPlayState();\n applySeek();\n }\n\n function destroyApi() {\n apiRef.value?.destroy();\n apiRef.value = null;\n }\n\n /** Declarative play/pause → animator calls. */\n function syncPlayState() {\n if (compMode.value !== PxControlMode.play) return;\n if (props.play && !props.pause) {\n apiRef.value?.play();\n } else if (props.pause) {\n apiRef.value?.pause();\n } else if (props.play === false) {\n // explicit play=false → hold where it is (review §8). This used to `finish()`;\n // a boolean whose `false` means \"jump to the end\" is not what anyone guesses.\n apiRef.value?.pause();\n } else {\n // pause-only usage: pause switched off → resume\n apiRef.value?.play();\n }\n }\n\n /** Controlled-time mode: seek through the animator API (no recreate). */\n function applySeek() {\n if (compMode.value !== PxControlMode.fixedTime) return;\n const doc = resolvedDoc.value;\n if (!doc) return;\n const seekMs = calcSeekMs(doc, props);\n if (seekMs !== undefined) {\n apiRef.value?.setCurrentTime(seekMs);\n apiRef.value?.pause();\n }\n }\n\n // Create the animator once DOM refs are available.\n onMounted(() => createApi());\n\n // Recreate the animator when the resolved doc changes.\n // `flush: 'post'` — the animator must be created AFTER the DOM is\n // patched, otherwise the new root element isn't in the document yet\n // and trigger setup fails (autoplay would never resume after a doc swap).\n watch(resolvedDoc, () => createApi(), { flush: 'post' });\n\n // Sync declarative play/pause props with the animator.\n watch([compMode, () => props.play, () => props.pause], () => syncPlayState());\n\n // Scrubbing progress/time only seeks — the animator is NOT recreated.\n watch([compMode, () => props.progress, () => props.time], () => applySeek());\n\n onUnmounted(() => {\n destroyApi();\n });\n\n // -- Expose imperative API ----------------------------------------------\n\n const publicApi: VueAnimatorApi = {\n isPlaying: () => apiRef.value?.isPlaying() || false,\n play: () => apiRef.value?.play(),\n pause: () => apiRef.value?.pause(),\n cancel: () => apiRef.value?.cancel(),\n finish: () => apiRef.value?.finish(),\n setPlaybackRate: (rate: number) => apiRef.value?.setPlaybackRate(rate),\n getCurrentTime: () => apiRef.value?.getCurrentTime() ?? null,\n setCurrentTime: (time: number) => apiRef.value?.setCurrentTime(time),\n getCurrentProgress: () => apiRef.value?.getCurrentProgress() ?? null,\n setCurrentProgress: (progress: number) => apiRef.value?.setCurrentProgress(progress),\n };\n\n expose(publicApi);\n\n // -- Render -------------------------------------------------------------\n\n return () => {\n const doc = resolvedDoc.value;\n return renderPxTree(doc, createVNode, makeDiag());\n };\n },\n});\n\nexport default PixodeskSvgAnimator;\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 { PX_TRIGGER_DEFAULTS, PxMouseOutAction, PxOffScreenAction, PxTriggerStart } from '@pixodesk/svg-animator-core';\nimport { createVisibilityGate, type PxVisibilityGate } from '@pixodesk/svg-animator-web/internal';\nimport { computed, defineComponent, h, onBeforeUnmount, onMounted, ref, watch, type PropType } from 'vue';\n\n\ntype AnimState = 'idle' | 'paused' | 'playing';\n\n/**\n * Controls playback of 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 Vue component\n * (`vite-svg-loader`) and pass it in the default slot:\n *\n * ```vue\n * <script setup>\n * import AnimationSvg from './animation.svg?component';\n * </script>\n *\n * <template>\n * <PixodeskSvgCssAnimator start=\"mouseOver\" mouseOut=\"pause\">\n * <AnimationSvg />\n * </PixodeskSvgCssAnimator>\n * </template>\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 * TWO AXES, the same two the JSON player has: `start` says what STARTS it, `offScreen` says\n * whether it may RUN. The gate itself is the player's (`createVisibilityGate`), so the threshold,\n * the dwell, the hysteresis and the hidden-tab rule cannot drift between the two.\n *\n * @prop start - What starts the animation:\n * - `'load'` — plays as soon as it is visible enough (default)\n * - `'mouseOver'` — plays on hover\n * - `'click'` — plays on click, toggles on second click\n * - `'none'` — **not supported here.** This wrapper exposes no `play()`, so there is\n * nothing to wait for and the animation never starts. The prop keeps the\n * shared `PxTriggerStart` type (one enum per wire key), so the value\n * type-checks — it simply has no effect. Use `PixodeskSvgAnimator` (the JSON\n * player) when you need to start an animation from code.\n * @prop offScreen - What happens while none of it is on screen:\n * - `'pause'` — pauses at the current frame (default); resumes when it comes back\n * - `'continue'` — keeps playing, and starts without waiting to be seen\n * - `'reset'` — back to the beginning, so it replays on the next entry\n * @prop mouseOut - What happens when the pointer leaves, for `start: 'mouseOver'`:\n * - `'continue'` — keeps playing (default)\n * - `'pause'` — pauses at the current frame\n * - `'reset'` — resets to the beginning\n * - `'reverse'` — **acts as `'continue'` here.** A CSS class toggle cannot run keyframes\n * backwards. Accepted so the prop keeps the shared `PxMouseOutAction` type.\n * @prop visibilityThreshold - How much of the element must be on screen (0–1) before it may run.\n * Defaults to the wire default (`0.5`), the same as the JSON player.\n * @prop visibilityDebounce - How long that must hold, in ms, before it starts. Defaults to the\n * wire default (`150`).\n * @public\n */\nconst PixodeskSvgCssAnimator = defineComponent({\n name: 'PixodeskSvgCssAnimator',\n // The wrapper spreads `attrs` itself and merges `class` by hand, so Vue must not ALSO\n // apply them — otherwise a host class lands on the div twice.\n inheritAttrs: false,\n props: {\n start: { type: String as PropType<PxTriggerStart>, default: PX_TRIGGER_DEFAULTS.start },\n offScreen: { type: String as PropType<PxOffScreenAction>, default: PX_TRIGGER_DEFAULTS.offScreen },\n mouseOut: { type: String as PropType<PxMouseOutAction>, default: PX_TRIGGER_DEFAULTS.mouseOut },\n visibilityThreshold: { type: Number, default: PX_TRIGGER_DEFAULTS.visibilityThreshold },\n visibilityDebounce: { type: Number, default: PX_TRIGGER_DEFAULTS.visibilityDebounce },\n },\n setup(props, { slots, attrs }) {\n const state = ref<AnimState>('idle');\n const el = ref<HTMLDivElement | null>(null);\n let gate: PxVisibilityGate | null = null;\n\n const goOut = (): void => {\n state.value =\n props.mouseOut === PxMouseOutAction.reset ? 'idle' :\n props.mouseOut === PxMouseOutAction.pause ? 'paused' : 'playing';\n };\n\n const attach = (): void => {\n gate?.dispose();\n gate = null;\n const node = el.value;\n if (!node) return;\n gate = createVisibilityGate(node, {\n start: props.start,\n offScreen: props.offScreen,\n mouseOut: props.mouseOut,\n visibilityThreshold: props.visibilityThreshold,\n visibilityDebounce: props.visibilityDebounce,\n }, {\n isPlaying: () => state.value === 'playing',\n play: () => { state.value = 'playing'; },\n pause: () => { state.value = 'paused'; },\n cancel: () => { state.value = 'idle'; },\n });\n // Only `load` is held by the gate; a hover or a click is aimed at something seen.\n if (props.start === PxTriggerStart.load) gate.requestStart(false);\n };\n\n onMounted(attach);\n watch(() => [props.start, props.offScreen, props.mouseOut, props.visibilityThreshold, props.visibilityDebounce], attach);\n onBeforeUnmount(() => { gate?.dispose(); gate = null; });\n\n const requestStart = (): void => {\n if (gate) gate.requestStart(true);\n else state.value = 'playing';\n };\n\n const cssClass = computed(() =>\n state.value === 'playing' ? 'px-anim-enabled px-anim-playing' :\n state.value === 'paused' ? 'px-anim-enabled' : ''\n );\n\n const handlers = computed(() =>\n props.start === PxTriggerStart.mouseOver\n ? { onMouseenter: requestStart, onMouseleave: goOut }\n : props.start === PxTriggerStart.click\n ? { onClick: () => { if (state.value === 'playing') state.value = 'paused'; else requestStart(); } }\n : {}\n );\n\n return () => h('div', {\n ref: el,\n ...attrs,\n class: [attrs.class, cssClass.value],\n ...handlers.value,\n }, slots.default?.());\n },\n});\nexport default PixodeskSvgCssAnimator;\n"],"mappings":"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","names":["import_svg_animator_core","import_internal","import_vue"]}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
var Vue = window.Vue; var PixodeskAnimatorWeb = window.PixodeskAnimator;
|
|
2
2
|
|
|
3
3
|
// src/PixodeskSvgAnimator.ts
|
|
4
|
-
import { createAnimator,
|
|
4
|
+
import { createAnimator, PxDiagnosticKind } from "@pixodesk/svg-animator-web";
|
|
5
5
|
import { applyAnimatorConfig, foldTimelineOverride, getAnimatorConfig, PxDiagnosticCode, PxControlMode, resolveControlMode, controlModeTakesOverTrigger, progressToTimeMs } from "@pixodesk/svg-animator-core";
|
|
6
|
-
import {
|
|
6
|
+
import { createDiagnostics, PX_DEFAULT_DURATION_MS, prepareDocumentForRender, renderPxTree } from "@pixodesk/svg-animator-core/internal";
|
|
7
7
|
import {
|
|
8
8
|
computed,
|
|
9
9
|
defineComponent,
|
|
@@ -14,33 +14,6 @@ import {
|
|
|
14
14
|
watch
|
|
15
15
|
} from "vue";
|
|
16
16
|
var VUE_PROP = { ref: "ref", style: "style" };
|
|
17
|
-
function toInlineStyle(style) {
|
|
18
|
-
if (!style) return void 0;
|
|
19
|
-
const inline = {};
|
|
20
|
-
for (const [prop, value] of Object.entries(style)) inline[prop] = String(value);
|
|
21
|
-
return inline;
|
|
22
|
-
}
|
|
23
|
-
function createVueAdapter(elementRefs, diag) {
|
|
24
|
-
const warnedSelectors = /* @__PURE__ */ new Set();
|
|
25
|
-
const adapter = {
|
|
26
|
-
isConnected: () => true,
|
|
27
|
-
setAttribute: (id, attrName, value) => {
|
|
28
|
-
attrName = camelCaseToKebabWordIfNeeded(attrName);
|
|
29
|
-
const element = elementRefs.get(id);
|
|
30
|
-
if (!element && !warnedSelectors.has(id)) {
|
|
31
|
-
warnedSelectors.add(id);
|
|
32
|
-
diag.warn(PxDiagnosticKind.host, PxDiagnosticCode.setAttributeNoElement, id);
|
|
33
|
-
}
|
|
34
|
-
if (element) {
|
|
35
|
-
element.setAttribute(attrName, value);
|
|
36
|
-
if (PX_STYLE_ATTR_NAMES.has(attrName)) {
|
|
37
|
-
element.style[attrName] = value;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
return adapter;
|
|
43
|
-
}
|
|
44
17
|
function applyDocOverrides(doc, props, compMode, diag) {
|
|
45
18
|
const { timeline, resetTimeline, duration, delay, iterations, start } = props;
|
|
46
19
|
const patch = foldTimelineOverride(timeline, { duration, delay, iterations, start });
|
|
@@ -104,7 +77,7 @@ var PixodeskSvgAnimator = defineComponent({
|
|
|
104
77
|
},
|
|
105
78
|
emits: ["play", "stop", "pause", "cancel", "finish", "remove"],
|
|
106
79
|
setup(props, { expose, emit }) {
|
|
107
|
-
|
|
80
|
+
let rootElement = null;
|
|
108
81
|
const apiRef = shallowRef(null);
|
|
109
82
|
const makeDiag = () => createDiagnostics(
|
|
110
83
|
{ onWarn: props.onWarn, onError: props.onError, muteWarn: props.muteWarn, muteError: props.muteError },
|
|
@@ -123,36 +96,25 @@ var PixodeskSvgAnimator = defineComponent({
|
|
|
123
96
|
for (const w of r.warnings) diag.warn(PxDiagnosticKind.usage, PxDiagnosticCode.controlPropsConflict, w);
|
|
124
97
|
}, { immediate: true });
|
|
125
98
|
const resolvedDoc = computed(() => {
|
|
126
|
-
|
|
127
|
-
return
|
|
99
|
+
const doc = applyDocOverrides(props.doc, props, compMode.value, makeDiag());
|
|
100
|
+
return prepareDocumentForRender(doc);
|
|
128
101
|
});
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
normProps[VUE_PROP.ref] = (el) => {
|
|
138
|
-
if (el) {
|
|
139
|
-
elementRefs.set(nodeId, el);
|
|
140
|
-
} else {
|
|
141
|
-
elementRefs.delete(nodeId);
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
const childVNodes = children?.map((child) => renderNode(child)).filter(Boolean);
|
|
146
|
-
const text = typeof node.textContent === "string" ? node.textContent : void 0;
|
|
147
|
-
return h(type, normProps, childVNodes?.length ? childVNodes : text);
|
|
148
|
-
}
|
|
102
|
+
const createVNode = ({ tag, attrs, style, children, text, node, isRoot, index }) => {
|
|
103
|
+
const vnodeProps = { ...attrs, key: node.id ?? index };
|
|
104
|
+
if (style) vnodeProps[VUE_PROP.style] = style;
|
|
105
|
+
if (isRoot) vnodeProps[VUE_PROP.ref] = (el) => {
|
|
106
|
+
rootElement = el;
|
|
107
|
+
};
|
|
108
|
+
return h(tag, vnodeProps, children.length ? children : text);
|
|
109
|
+
};
|
|
149
110
|
function createApi() {
|
|
150
111
|
destroyApi();
|
|
151
112
|
const doc = resolvedDoc.value;
|
|
152
113
|
if (!doc) return;
|
|
153
114
|
const options = {
|
|
154
115
|
doc,
|
|
155
|
-
|
|
116
|
+
// Mounted (or `flush: 'post'`) by the time this runs, so the root exists.
|
|
117
|
+
rootElement: rootElement ?? void 0,
|
|
156
118
|
onPlay: () => emit("play"),
|
|
157
119
|
onPause: () => emit("pause"),
|
|
158
120
|
onCancel: () => emit("cancel"),
|
|
@@ -217,7 +179,7 @@ var PixodeskSvgAnimator = defineComponent({
|
|
|
217
179
|
expose(publicApi);
|
|
218
180
|
return () => {
|
|
219
181
|
const doc = resolvedDoc.value;
|
|
220
|
-
return doc
|
|
182
|
+
return renderPxTree(doc, createVNode, makeDiag());
|
|
221
183
|
};
|
|
222
184
|
}
|
|
223
185
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/PixodeskSvgAnimator.ts","../src/PixodeskSvgCssAnimator.ts"],"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 { PxAnimatedSvgDocument, PxAnimatorApi, PxNode, PxPlatformAdapter, PxTimelineEngineSetting, PxTimelinePatch, PxTrigger } from '@pixodesk/svg-animator-web';\nimport type { PxInternalAnimatorOptions } from '@pixodesk/svg-animator-web/internal';\nimport { createAnimator, generateNewIds, toDomProps, PxDiagnosticKind, type PxPlaybackOverride, type PxDiagnostic, type PxDiagnostics } from '@pixodesk/svg-animator-web';\nimport { applyAnimatorConfig, foldTimelineOverride, getAnimatorConfig, PxDiagnosticCode, PxControlMode, resolveControlMode, controlModeTakesOverTrigger, progressToTimeMs, type PxAnimatorHandle, type PxControlProps } from '@pixodesk/svg-animator-core';\nimport { camelCaseToKebabWordIfNeeded, createDiagnostics, PX_STYLE_ATTR_NAMES, PX_DEFAULT_DURATION_MS } from '@pixodesk/svg-animator-core/internal';\nimport {\n computed, defineComponent, h, onMounted, onUnmounted, ref, shallowRef, type PropType, type VNode,\n watch,\n} from 'vue';\n\n\n// -- Public types -----------------------------------------------------------\n\n/** Vue's own prop names on the vnode — not wire keys. See the React twin. */\nconst VUE_PROP = { ref: 'ref', style: 'style' } as const;\n\n/** `node.style` (camelCase declarations) as an element style object — values stringified, exactly\n * as the web player writes them (`element.style[prop] = String(value)`). */\nfunction toInlineStyle(style: PxNode['style']): Record<string, string> | undefined {\n if (!style) return undefined;\n const inline: Record<string, string> = {};\n for (const [prop, value] of Object.entries(style)) inline[prop] = String(value);\n return inline;\n}\n\n/**\n * The imperative handle the template ref exposes — core's `PxAnimatorHandle` under this\n * package's name (review §9). One definition for React, Vue and React Native.\n * @public\n */\nexport type VueAnimatorApi = PxAnimatorHandle;\n\n\n// -- Internal types ---------------------------------------------------------\n\n// The control mode is core's `PxControlMode`, shared with React and React Native\n// (API review §1/§7) — one precedence rule, one set of conflict warnings.\n\n\n// -- Vue ↔ Animator bridge --------------------------------------------------\n\n/**\n * Creates a platform adapter that routes animator attribute updates\n * to the corresponding Vue-managed DOM element refs.\n */\nfunction createVueAdapter(elementRefs: Map<string, Element>, diag: PxDiagnostics) {\n const warnedSelectors = new Set<string>();\n\n const adapter: PxPlatformAdapter = {\n isConnected: () => true,\n setAttribute: (id, attrName, value) => {\n attrName = camelCaseToKebabWordIfNeeded(attrName);\n\n const element = elementRefs.get(id);\n\n if (!element && !warnedSelectors.has(id)) {\n warnedSelectors.add(id);\n diag.warn(PxDiagnosticKind.host, PxDiagnosticCode.setAttributeNoElement, id);\n }\n\n if (element) {\n element.setAttribute(attrName, value);\n if (PX_STYLE_ATTR_NAMES.has(attrName)) {\n (element as HTMLElement).style[attrName as any] = value;\n }\n }\n },\n };\n return adapter;\n}\n\n// FIXME: add model validation (e.g. isElementFileJson check)\n\n\n// -- Helper: apply doc overrides --------------------------------------------\n\n/**\n * What `applyDocOverrides` / `calcSeekMs` read off the props — core's shared shapes, not a local\n * copy (review §9). The runtime `props: {…}` block below stays Vue's own, because Vue needs\n * runtime prop declarations; this is only the TypeScript view of the same members.\n */\ntype DocOverrideProps = PxPlaybackOverride & Pick<PxControlProps, 'progress' | 'time'>;\n\nfunction applyDocOverrides(\n doc: PxAnimatedSvgDocument,\n props: DocOverrideProps,\n compMode: PxControlMode,\n diag: PxDiagnostics,\n): PxAnimatedSvgDocument {\n\n // ONE patch, applied ONCE: the props, plus the component's own need to take the trigger\n // over in the non-autoplay control modes.\n //\n // This replaces three hand-rolled spread blocks that wrote the FLAT runtime keys. On a\n // wire-format document — which is what every writer emits — `flattenAnimatorTimeline`\n // overwrote them from `timeline` immediately afterwards, so the overrides were silently\n // discarded. See dev-docs/plans/playback-override.md §1.1.\n const { timeline, resetTimeline, duration, delay, iterations, start } = props;\n const patch = foldTimelineOverride(timeline, { duration, delay, iterations, start });\n const fullPatch: any = controlModeTakesOverTrigger(compMode)\n ? {\n ...(patch ?? {}),\n timeline: {\n ...((patch as any)?.timeline ?? {}),\n trigger: { ...((patch as any)?.timeline?.trigger ?? {}), start: 'none' },\n },\n }\n : patch;\n\n if (fullPatch !== undefined || resetTimeline) {\n const applied = applyAnimatorConfig(doc, fullPatch ?? {}, { resetTimeline: !!resetTimeline });\n for (const w of applied.warnings) diag.warn(PxDiagnosticKind.usage, PxDiagnosticCode.timelineOverrideIgnored, w);\n doc = applied.doc;\n }\n\n return doc;\n}\n\n/**\n * Controlled-time mode: absolute seek target in ms. `progress` is a fraction\n * (0–1) of the WHOLE timeline (duration × iterations — ONE iteration when\n * endless); `time` is absolute. Applied through the animator API\n * (setCurrentTime) so scrubbing does NOT recreate the animator.\n */\nfunction calcSeekMs(doc: PxAnimatedSvgDocument, props: DocOverrideProps): number | undefined {\n let seekMs: number | undefined;\n // The FLAT runtime view, so this works on a wire-format document too — reading\n // `doc.animator.duration` directly finds nothing there.\n const animator = getAnimatorConfig(doc) || {};\n if (props.progress !== undefined) {\n // ONE rule for progress → time (core's `progressToTimeMs`): the same mapping\n // `getCurrentProgress` reads back, so a prop of 0.5 and a read of 0.5 agree.\n const iterationsValue = props.iterations ?? animator.iterations;\n const iterationsCount = iterationsValue === 'infinite' ? Infinity\n : (typeof iterationsValue === 'number' && iterationsValue >= 1 ? iterationsValue : 1);\n const singleDuration = props.duration ?? animator.duration ?? PX_DEFAULT_DURATION_MS;\n seekMs = progressToTimeMs(props.progress, singleDuration, iterationsCount);\n }\n if (props.time !== undefined) seekMs = props.time;\n return seekMs;\n}\n\n\n// -- Main public component --------------------------------------------------\n\n/**\n * Vue 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 * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" autoplay />\n * ```\n *\n * 2. **Declarative play/pause** – controlled via boolean props.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" play :pause=\"false\" />\n * ```\n *\n * 3. **Imperative** – exposes a ref-based API for full programmatic control.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" ref=\"animator\" />\n * <button @click=\"$refs.animator.play()\">Play</button>\n * ```\n *\n * 4. **Controlled time** – renders a single frame at a given time.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" :progress=\"0.5\" />\n * <PixodeskSvgAnimator :doc=\"animation\" :time=\"500\" />\n * ```\n * @public\n */\nconst PixodeskSvgAnimator = defineComponent({\n name: 'PixodeskSvgAnimator',\n\n props: {\n // -- Source\n doc: { type: Object as PropType<PxAnimatedSvgDocument>, required: true },\n\n // -- Playback override: the document's `timeline` block as a patch (or a JSON string),\n // plus the four shortcuts people reach for most. The same names as React / RN.\n timeline: { type: [Object, String] as PropType<PxTimelinePatch | string> },\n resetTimeline: { type: Boolean, default: undefined },\n duration: { type: Number },\n delay: { type: Number },\n iterations: { type: [Number, String] as PropType<number | 'infinite'> },\n start: { type: String as PropType<'load' | 'mouseOver' | 'click' | 'none'> },\n\n // -- Declarative control\n autoplay: { type: Boolean, default: undefined },\n play: { type: Boolean, default: undefined },\n pause: { type: Boolean, default: undefined },\n\n // -- Controlled time\n progress: { type: Number },\n time: { type: Number },\n\n // -- Diagnostics (API review §5).\n // Function PROPS, not emits, on purpose: an emit handler always exists, so wiring\n // these to `emit` would permanently suppress the console fallback for anyone who\n // never listens. As props, \"not given\" really is undefined and the console still\n // speaks by default — the same contract as React and React Native.\n onWarn: { type: Function as PropType<(diagnostic: PxDiagnostic) => void> },\n onError: { type: Function as PropType<(diagnostic: PxDiagnostic) => void> },\n // `muteWarn` / `muteError` switch the console fallback off — for a host that knows\n // about the warnings and tolerates them; a handler you passed still fires.\n muteWarn: { type: Boolean, default: undefined },\n muteError: { type: Boolean, default: undefined },\n },\n\n emits: ['play', 'stop', 'pause', 'cancel', 'finish', 'remove'],\n\n setup(props, { expose, emit }) {\n const elementRefs = new Map<string, Element>();\n const apiRef = shallowRef<PxAnimatorApi | null>(null);\n\n /**\n * The diagnostics channel, built from the CURRENT props each time (API review §5).\n * Not hoisted into a constant wrapper: `(m, d) => props.onWarn?.(m, d)` would always be\n * a function, so the channel would think a handler exists and the console fallback\n * would never fire for anyone who passed nothing.\n */\n const makeDiag = (): PxDiagnostics => createDiagnostics(\n { onWarn: props.onWarn, onError: props.onError, muteWarn: props.muteWarn, muteError: props.muteError },\n '[PixodeskSvgAnimator]',\n );\n\n // -- Determine control mode ---------------------------------------------\n\n // ONE control-mode rule, decided in core (API review §1/§7). The template ref is not an\n // input: it is exposed in every mode and never changes which one is chosen.\n const resolvedMode = computed(() => resolveControlMode({\n progress: props.progress, time: props.time,\n play: props.play, pause: props.pause, autoplay: props.autoplay,\n }));\n const compMode = computed<PxControlMode>(() => resolvedMode.value.mode);\n\n // Warn where the mode is COMPUTED, not inside `applyDocOverrides` — that runs again on\n // every doc recompute and would repeat the same sentence.\n watch(resolvedMode, r => {\n const diag = makeDiag();\n for (const w of r.warnings) diag.warn(PxDiagnosticKind.usage, PxDiagnosticCode.controlPropsConflict, w);\n }, { immediate: true });\n\n // -- Prepare the document with overrides --------------------------------\n\n const resolvedDoc = computed(() => {\n let doc = generateNewIds(props.doc);\n return applyDocOverrides(doc, props, compMode.value, makeDiag());\n });\n\n // -- Render the SVG node tree -------------------------------------------\n\n function renderNode(node: PxNode | undefined): VNode | null {\n if (!node) return null;\n\n const { type, animate, meta, children, style, ...attrs } = node;\n const normProps = toDomProps(attrs);\n\n // `node.style` is not an attribute (`toDomProps` drops it) — it is the element's style.\n const inlineStyle = toInlineStyle(style);\n if (inlineStyle) normProps[VUE_PROP.style] = inlineStyle;\n\n // Capture a ref to each element with an id.\n if (node.id) {\n const nodeId = node.id;\n normProps[VUE_PROP.ref] = (el: Element | null) => {\n if (el) {\n elementRefs.set(nodeId, el);\n } else {\n elementRefs.delete(nodeId);\n }\n };\n }\n\n const childVNodes = children?.map(child => renderNode(child)).filter(Boolean) as VNode[] | undefined;\n // Text content: a node's own `textContent` renders only when it has no child nodes — a\n // line <tspan> can carry both, and then its children are the styled spans (the React\n // Native renderer's rule). `toDomProps` strips `textContent` from the attributes.\n const text = typeof node.textContent === 'string' ? node.textContent : undefined;\n return h(type, normProps, childVNodes?.length ? childVNodes : text);\n }\n\n // -- Animator lifecycle -------------------------------------------------\n\n function createApi() {\n destroyApi();\n const doc = resolvedDoc.value;\n if (!doc) return;\n\n // Route animator lifecycle events to Vue component events, INLINE under the same\n // names every surface uses (review §9). `stop` fires alongside any event that halts\n // playback — that is the PLAYER's rule, so `onStop` is passed through, not re-derived.\n // `adapter` is the components' extension of the public options (`PxInternalAnimatorOptions`,\n // review §25.14): the frame loop writes to the elements Vue rendered, not to a container.\n const options: PxInternalAnimatorOptions = {\n doc,\n adapter: createVueAdapter(elementRefs, makeDiag()),\n onPlay: () => emit('play'),\n onPause: () => emit('pause'),\n onCancel: () => emit('cancel'),\n onFinish: () => emit('finish'),\n onRemove: () => emit('remove'),\n onStop: () => emit('stop'),\n // The player's own diagnostics reach the same handlers as the component's.\n onWarn: props.onWarn,\n onError: props.onError,\n muteWarn: props.muteWarn,\n muteError: props.muteError,\n };\n apiRef.value = createAnimator(options);\n\n // (Re)apply the declarative control state to the fresh animator —\n // covers both the initial mount (e.g. `:play=\"true\"` from the\n // start) and doc swaps.\n syncPlayState();\n applySeek();\n }\n\n function destroyApi() {\n apiRef.value?.destroy();\n apiRef.value = null;\n }\n\n /** Declarative play/pause → animator calls. */\n function syncPlayState() {\n if (compMode.value !== PxControlMode.play) return;\n if (props.play && !props.pause) {\n apiRef.value?.play();\n } else if (props.pause) {\n apiRef.value?.pause();\n } else if (props.play === false) {\n // explicit play=false → hold where it is (review §8). This used to `finish()`;\n // a boolean whose `false` means \"jump to the end\" is not what anyone guesses.\n apiRef.value?.pause();\n } else {\n // pause-only usage: pause switched off → resume\n apiRef.value?.play();\n }\n }\n\n /** Controlled-time mode: seek through the animator API (no recreate). */\n function applySeek() {\n if (compMode.value !== PxControlMode.fixedTime) return;\n const doc = resolvedDoc.value;\n if (!doc) return;\n const seekMs = calcSeekMs(doc, props);\n if (seekMs !== undefined) {\n apiRef.value?.setCurrentTime(seekMs);\n apiRef.value?.pause();\n }\n }\n\n // Create the animator once DOM refs are available.\n onMounted(() => createApi());\n\n // Recreate the animator when the resolved doc changes.\n // `flush: 'post'` — the animator must be created AFTER the DOM is\n // patched, otherwise the new root element isn't in the document yet\n // and trigger setup fails (autoplay would never resume after a doc swap).\n watch(resolvedDoc, () => createApi(), { flush: 'post' });\n\n // Sync declarative play/pause props with the animator.\n watch([compMode, () => props.play, () => props.pause], () => syncPlayState());\n\n // Scrubbing progress/time only seeks — the animator is NOT recreated.\n watch([compMode, () => props.progress, () => props.time], () => applySeek());\n\n onUnmounted(() => {\n destroyApi();\n });\n\n // -- Expose imperative API ----------------------------------------------\n\n const publicApi: VueAnimatorApi = {\n isPlaying: () => apiRef.value?.isPlaying() || false,\n play: () => apiRef.value?.play(),\n pause: () => apiRef.value?.pause(),\n cancel: () => apiRef.value?.cancel(),\n finish: () => apiRef.value?.finish(),\n setPlaybackRate: (rate: number) => apiRef.value?.setPlaybackRate(rate),\n getCurrentTime: () => apiRef.value?.getCurrentTime() ?? null,\n setCurrentTime: (time: number) => apiRef.value?.setCurrentTime(time),\n getCurrentProgress: () => apiRef.value?.getCurrentProgress() ?? null,\n setCurrentProgress: (progress: number) => apiRef.value?.setCurrentProgress(progress),\n };\n\n expose(publicApi);\n\n // -- Render -------------------------------------------------------------\n\n return () => {\n const doc = resolvedDoc.value;\n return doc ? renderNode(doc) : null;\n };\n },\n});\n\nexport default PixodeskSvgAnimator;\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 { PX_TRIGGER_DEFAULTS, PxMouseOutAction, PxOffScreenAction, PxTriggerStart } from '@pixodesk/svg-animator-core';\nimport { createVisibilityGate, type PxVisibilityGate } from '@pixodesk/svg-animator-web/internal';\nimport { computed, defineComponent, h, onBeforeUnmount, onMounted, ref, watch, type PropType } from 'vue';\n\n\ntype AnimState = 'idle' | 'paused' | 'playing';\n\n/**\n * Controls playback of 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 Vue component\n * (`vite-svg-loader`) and pass it in the default slot:\n *\n * ```vue\n * <script setup>\n * import AnimationSvg from './animation.svg?component';\n * </script>\n *\n * <template>\n * <PixodeskSvgCssAnimator start=\"mouseOver\" mouseOut=\"pause\">\n * <AnimationSvg />\n * </PixodeskSvgCssAnimator>\n * </template>\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 * TWO AXES, the same two the JSON player has: `start` says what STARTS it, `offScreen` says\n * whether it may RUN. The gate itself is the player's (`createVisibilityGate`), so the threshold,\n * the dwell, the hysteresis and the hidden-tab rule cannot drift between the two.\n *\n * @prop start - What starts the animation:\n * - `'load'` — plays as soon as it is visible enough (default)\n * - `'mouseOver'` — plays on hover\n * - `'click'` — plays on click, toggles on second click\n * - `'none'` — **not supported here.** This wrapper exposes no `play()`, so there is\n * nothing to wait for and the animation never starts. The prop keeps the\n * shared `PxTriggerStart` type (one enum per wire key), so the value\n * type-checks — it simply has no effect. Use `PixodeskSvgAnimator` (the JSON\n * player) when you need to start an animation from code.\n * @prop offScreen - What happens while none of it is on screen:\n * - `'pause'` — pauses at the current frame (default); resumes when it comes back\n * - `'continue'` — keeps playing, and starts without waiting to be seen\n * - `'reset'` — back to the beginning, so it replays on the next entry\n * @prop mouseOut - What happens when the pointer leaves, for `start: 'mouseOver'`:\n * - `'continue'` — keeps playing (default)\n * - `'pause'` — pauses at the current frame\n * - `'reset'` — resets to the beginning\n * - `'reverse'` — **acts as `'continue'` here.** A CSS class toggle cannot run keyframes\n * backwards. Accepted so the prop keeps the shared `PxMouseOutAction` type.\n * @prop visibilityThreshold - How much of the element must be on screen (0–1) before it may run.\n * Defaults to the wire default (`0.5`), the same as the JSON player.\n * @prop visibilityDebounce - How long that must hold, in ms, before it starts. Defaults to the\n * wire default (`150`).\n * @public\n */\nconst PixodeskSvgCssAnimator = defineComponent({\n name: 'PixodeskSvgCssAnimator',\n // The wrapper spreads `attrs` itself and merges `class` by hand, so Vue must not ALSO\n // apply them — otherwise a host class lands on the div twice.\n inheritAttrs: false,\n props: {\n start: { type: String as PropType<PxTriggerStart>, default: PX_TRIGGER_DEFAULTS.start },\n offScreen: { type: String as PropType<PxOffScreenAction>, default: PX_TRIGGER_DEFAULTS.offScreen },\n mouseOut: { type: String as PropType<PxMouseOutAction>, default: PX_TRIGGER_DEFAULTS.mouseOut },\n visibilityThreshold: { type: Number, default: PX_TRIGGER_DEFAULTS.visibilityThreshold },\n visibilityDebounce: { type: Number, default: PX_TRIGGER_DEFAULTS.visibilityDebounce },\n },\n setup(props, { slots, attrs }) {\n const state = ref<AnimState>('idle');\n const el = ref<HTMLDivElement | null>(null);\n let gate: PxVisibilityGate | null = null;\n\n const goOut = (): void => {\n state.value =\n props.mouseOut === PxMouseOutAction.reset ? 'idle' :\n props.mouseOut === PxMouseOutAction.pause ? 'paused' : 'playing';\n };\n\n const attach = (): void => {\n gate?.dispose();\n gate = null;\n const node = el.value;\n if (!node) return;\n gate = createVisibilityGate(node, {\n start: props.start,\n offScreen: props.offScreen,\n mouseOut: props.mouseOut,\n visibilityThreshold: props.visibilityThreshold,\n visibilityDebounce: props.visibilityDebounce,\n }, {\n isPlaying: () => state.value === 'playing',\n play: () => { state.value = 'playing'; },\n pause: () => { state.value = 'paused'; },\n cancel: () => { state.value = 'idle'; },\n });\n // Only `load` is held by the gate; a hover or a click is aimed at something seen.\n if (props.start === PxTriggerStart.load) gate.requestStart(false);\n };\n\n onMounted(attach);\n watch(() => [props.start, props.offScreen, props.mouseOut, props.visibilityThreshold, props.visibilityDebounce], attach);\n onBeforeUnmount(() => { gate?.dispose(); gate = null; });\n\n const requestStart = (): void => {\n if (gate) gate.requestStart(true);\n else state.value = 'playing';\n };\n\n const cssClass = computed(() =>\n state.value === 'playing' ? 'px-anim-enabled px-anim-playing' :\n state.value === 'paused' ? 'px-anim-enabled' : ''\n );\n\n const handlers = computed(() =>\n props.start === PxTriggerStart.mouseOver\n ? { onMouseenter: requestStart, onMouseleave: goOut }\n : props.start === PxTriggerStart.click\n ? { onClick: () => { if (state.value === 'playing') state.value = 'paused'; else requestStart(); } }\n : {}\n );\n\n return () => h('div', {\n ref: el,\n ...attrs,\n class: [attrs.class, cssClass.value],\n ...handlers.value,\n }, slots.default?.());\n },\n});\nexport default PixodeskSvgCssAnimator;\n"],"mappings":"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","names":["computed","defineComponent","h","onMounted","ref","watch"]}
|
|
1
|
+
{"version":3,"sources":["../src/PixodeskSvgAnimator.ts","../src/PixodeskSvgCssAnimator.ts"],"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 { PxAnimatedSvgDocument, PxAnimatorApi, PxTimelineEngineSetting, PxTimelinePatch, PxTrigger } from '@pixodesk/svg-animator-web';\nimport type { PxInternalAnimatorOptions } from '@pixodesk/svg-animator-web/internal';\nimport { createAnimator, PxDiagnosticKind, type PxPlaybackOverride, type PxDiagnostic, type PxDiagnostics } from '@pixodesk/svg-animator-web';\nimport { applyAnimatorConfig, foldTimelineOverride, getAnimatorConfig, PxDiagnosticCode, PxControlMode, resolveControlMode, controlModeTakesOverTrigger, progressToTimeMs, type PxAnimatorHandle, type PxControlProps } from '@pixodesk/svg-animator-core';\nimport { createDiagnostics, PX_DEFAULT_DURATION_MS, prepareDocumentForRender, renderPxTree, type PxElementFactory } from '@pixodesk/svg-animator-core/internal';\nimport {\n computed, defineComponent, h, onMounted, onUnmounted, ref, shallowRef, type PropType, type VNode,\n watch,\n} from 'vue';\n\n\n// -- Public types -----------------------------------------------------------\n\n/** Vue's own prop names on the vnode — not wire keys. See the React twin. */\nconst VUE_PROP = { ref: 'ref', style: 'style' } as const;\n\n/**\n * The imperative handle the template ref exposes — core's `PxAnimatorHandle` under this\n * package's name (review §9). One definition for React, Vue and React Native.\n * @public\n */\nexport type VueAnimatorApi = PxAnimatorHandle;\n\n\n// -- Internal types ---------------------------------------------------------\n\n// The control mode is core's `PxControlMode`, shared with React and React Native\n// (API review §1/§7) — one precedence rule, one set of conflict warnings.\n\n\n// FIXME: add model validation (e.g. isElementFileJson check)\n\n\n// -- Helper: apply doc overrides --------------------------------------------\n\n/**\n * What `applyDocOverrides` / `calcSeekMs` read off the props — core's shared shapes, not a local\n * copy (review §9). The runtime `props: {…}` block below stays Vue's own, because Vue needs\n * runtime prop declarations; this is only the TypeScript view of the same members.\n */\ntype DocOverrideProps = PxPlaybackOverride & Pick<PxControlProps, 'progress' | 'time'>;\n\nfunction applyDocOverrides(\n doc: PxAnimatedSvgDocument,\n props: DocOverrideProps,\n compMode: PxControlMode,\n diag: PxDiagnostics,\n): PxAnimatedSvgDocument {\n\n // ONE patch, applied ONCE: the props, plus the component's own need to take the trigger\n // over in the non-autoplay control modes.\n //\n // This replaces three hand-rolled spread blocks that wrote the FLAT runtime keys. On a\n // wire-format document — which is what every writer emits — `flattenAnimatorTimeline`\n // overwrote them from `timeline` immediately afterwards, so the overrides were silently\n // discarded. See dev-docs/plans/playback-override.md §1.1.\n const { timeline, resetTimeline, duration, delay, iterations, start } = props;\n const patch = foldTimelineOverride(timeline, { duration, delay, iterations, start });\n const fullPatch: any = controlModeTakesOverTrigger(compMode)\n ? {\n ...(patch ?? {}),\n timeline: {\n ...((patch as any)?.timeline ?? {}),\n trigger: { ...((patch as any)?.timeline?.trigger ?? {}), start: 'none' },\n },\n }\n : patch;\n\n if (fullPatch !== undefined || resetTimeline) {\n const applied = applyAnimatorConfig(doc, fullPatch ?? {}, { resetTimeline: !!resetTimeline });\n for (const w of applied.warnings) diag.warn(PxDiagnosticKind.usage, PxDiagnosticCode.timelineOverrideIgnored, w);\n doc = applied.doc;\n }\n\n return doc;\n}\n\n/**\n * Controlled-time mode: absolute seek target in ms. `progress` is a fraction\n * (0–1) of the WHOLE timeline (duration × iterations — ONE iteration when\n * endless); `time` is absolute. Applied through the animator API\n * (setCurrentTime) so scrubbing does NOT recreate the animator.\n */\nfunction calcSeekMs(doc: PxAnimatedSvgDocument, props: DocOverrideProps): number | undefined {\n let seekMs: number | undefined;\n // The FLAT runtime view, so this works on a wire-format document too — reading\n // `doc.animator.duration` directly finds nothing there.\n const animator = getAnimatorConfig(doc) || {};\n if (props.progress !== undefined) {\n // ONE rule for progress → time (core's `progressToTimeMs`): the same mapping\n // `getCurrentProgress` reads back, so a prop of 0.5 and a read of 0.5 agree.\n const iterationsValue = props.iterations ?? animator.iterations;\n const iterationsCount = iterationsValue === 'infinite' ? Infinity\n : (typeof iterationsValue === 'number' && iterationsValue >= 1 ? iterationsValue : 1);\n const singleDuration = props.duration ?? animator.duration ?? PX_DEFAULT_DURATION_MS;\n seekMs = progressToTimeMs(props.progress, singleDuration, iterationsCount);\n }\n if (props.time !== undefined) seekMs = props.time;\n return seekMs;\n}\n\n\n// -- Main public component --------------------------------------------------\n\n/**\n * Vue 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 * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" autoplay />\n * ```\n *\n * 2. **Declarative play/pause** – controlled via boolean props.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" play :pause=\"false\" />\n * ```\n *\n * 3. **Imperative** – exposes a ref-based API for full programmatic control.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" ref=\"animator\" />\n * <button @click=\"$refs.animator.play()\">Play</button>\n * ```\n *\n * 4. **Controlled time** – renders a single frame at a given time.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" :progress=\"0.5\" />\n * <PixodeskSvgAnimator :doc=\"animation\" :time=\"500\" />\n * ```\n * @public\n */\nconst PixodeskSvgAnimator = defineComponent({\n name: 'PixodeskSvgAnimator',\n\n props: {\n // -- Source\n doc: { type: Object as PropType<PxAnimatedSvgDocument>, required: true },\n\n // -- Playback override: the document's `timeline` block as a patch (or a JSON string),\n // plus the four shortcuts people reach for most. The same names as React / RN.\n timeline: { type: [Object, String] as PropType<PxTimelinePatch | string> },\n resetTimeline: { type: Boolean, default: undefined },\n duration: { type: Number },\n delay: { type: Number },\n iterations: { type: [Number, String] as PropType<number | 'infinite'> },\n start: { type: String as PropType<'load' | 'mouseOver' | 'click' | 'none'> },\n\n // -- Declarative control\n autoplay: { type: Boolean, default: undefined },\n play: { type: Boolean, default: undefined },\n pause: { type: Boolean, default: undefined },\n\n // -- Controlled time\n progress: { type: Number },\n time: { type: Number },\n\n // -- Diagnostics (API review §5).\n // Function PROPS, not emits, on purpose: an emit handler always exists, so wiring\n // these to `emit` would permanently suppress the console fallback for anyone who\n // never listens. As props, \"not given\" really is undefined and the console still\n // speaks by default — the same contract as React and React Native.\n onWarn: { type: Function as PropType<(diagnostic: PxDiagnostic) => void> },\n onError: { type: Function as PropType<(diagnostic: PxDiagnostic) => void> },\n // `muteWarn` / `muteError` switch the console fallback off — for a host that knows\n // about the warnings and tolerates them; a handler you passed still fires.\n muteWarn: { type: Boolean, default: undefined },\n muteError: { type: Boolean, default: undefined },\n },\n\n emits: ['play', 'stop', 'pause', 'cancel', 'finish', 'remove'],\n\n setup(props, { expose, emit }) {\n // The `<svg>` this component rendered — handed to the player, which attaches its\n // triggers to it and writes animated values to the elements under it.\n let rootElement: Element | null = null;\n const apiRef = shallowRef<PxAnimatorApi | null>(null);\n\n /**\n * The diagnostics channel, built from the CURRENT props each time (API review §5).\n * Not hoisted into a constant wrapper: `(m, d) => props.onWarn?.(m, d)` would always be\n * a function, so the channel would think a handler exists and the console fallback\n * would never fire for anyone who passed nothing.\n */\n const makeDiag = (): PxDiagnostics => createDiagnostics(\n { onWarn: props.onWarn, onError: props.onError, muteWarn: props.muteWarn, muteError: props.muteError },\n '[PixodeskSvgAnimator]',\n );\n\n // -- Determine control mode ---------------------------------------------\n\n // ONE control-mode rule, decided in core (API review §1/§7). The template ref is not an\n // input: it is exposed in every mode and never changes which one is chosen.\n const resolvedMode = computed(() => resolveControlMode({\n progress: props.progress, time: props.time,\n play: props.play, pause: props.pause, autoplay: props.autoplay,\n }));\n const compMode = computed<PxControlMode>(() => resolvedMode.value.mode);\n\n // Warn where the mode is COMPUTED, not inside `applyDocOverrides` — that runs again on\n // every doc recompute and would repeat the same sentence.\n watch(resolvedMode, r => {\n const diag = makeDiag();\n for (const w of r.warnings) diag.warn(PxDiagnosticKind.usage, PxDiagnosticCode.controlPropsConflict, w);\n }, { immediate: true });\n\n // -- Prepare the document with overrides --------------------------------\n\n const resolvedDoc = computed(() => {\n // Overrides FIRST: they can change `timeline.engine` and `duration`, and\n // materialization reads both (the engine picks the stages, the duration sizes loop\n // expansion). Then render what the web player renders — materialized, fresh ids.\n // Rendering the raw document dropped every `effects` bucket, so a document painted\n // by `effects` alone came out as an empty canvas.\n const doc = applyDocOverrides(props.doc, props, compMode.value, makeDiag());\n return prepareDocumentForRender(doc);\n });\n\n // -- Render the SVG node tree -------------------------------------------\n\n // The ONLY Vue-specific part of rendering: how to create a vnode. Every decision about the\n // document — tags, attribute names and values, sanitization, styles, text — is made by\n // core's `renderPxTree`, the same code the web player renders with. Vue writes an SVG\n // attribute name verbatim, so the names it is handed are used as they are.\n const createVNode: PxElementFactory<VNode> = ({ tag, attrs, style, children, text, node, isRoot, index }) => {\n const vnodeProps: Record<string, unknown> = { ...attrs, key: node.id ?? index };\n if (style) vnodeProps[VUE_PROP.style] = style;\n if (isRoot) vnodeProps[VUE_PROP.ref] = (el: Element | null) => { rootElement = el; };\n return h(tag, vnodeProps, children.length ? children : text);\n };\n\n // -- Animator lifecycle -------------------------------------------------\n\n function createApi() {\n destroyApi();\n const doc = resolvedDoc.value;\n if (!doc) return;\n\n // Route animator lifecycle events to Vue component events, INLINE under the same\n // names every surface uses (review §9). `stop` fires alongside any event that halts\n // playback — that is the PLAYER's rule, so `onStop` is passed through, not re-derived.\n // No `adapter`: the web player's OWN writer drives the elements Vue rendered, found by\n // id under the root. A second writer here was a second place for rules to go missing.\n const options: PxInternalAnimatorOptions = {\n doc,\n // Mounted (or `flush: 'post'`) by the time this runs, so the root exists.\n rootElement: rootElement ?? undefined,\n onPlay: () => emit('play'),\n onPause: () => emit('pause'),\n onCancel: () => emit('cancel'),\n onFinish: () => emit('finish'),\n onRemove: () => emit('remove'),\n onStop: () => emit('stop'),\n // The player's own diagnostics reach the same handlers as the component's.\n onWarn: props.onWarn,\n onError: props.onError,\n muteWarn: props.muteWarn,\n muteError: props.muteError,\n };\n apiRef.value = createAnimator(options);\n\n // (Re)apply the declarative control state to the fresh animator —\n // covers both the initial mount (e.g. `:play=\"true\"` from the\n // start) and doc swaps.\n syncPlayState();\n applySeek();\n }\n\n function destroyApi() {\n apiRef.value?.destroy();\n apiRef.value = null;\n }\n\n /** Declarative play/pause → animator calls. */\n function syncPlayState() {\n if (compMode.value !== PxControlMode.play) return;\n if (props.play && !props.pause) {\n apiRef.value?.play();\n } else if (props.pause) {\n apiRef.value?.pause();\n } else if (props.play === false) {\n // explicit play=false → hold where it is (review §8). This used to `finish()`;\n // a boolean whose `false` means \"jump to the end\" is not what anyone guesses.\n apiRef.value?.pause();\n } else {\n // pause-only usage: pause switched off → resume\n apiRef.value?.play();\n }\n }\n\n /** Controlled-time mode: seek through the animator API (no recreate). */\n function applySeek() {\n if (compMode.value !== PxControlMode.fixedTime) return;\n const doc = resolvedDoc.value;\n if (!doc) return;\n const seekMs = calcSeekMs(doc, props);\n if (seekMs !== undefined) {\n apiRef.value?.setCurrentTime(seekMs);\n apiRef.value?.pause();\n }\n }\n\n // Create the animator once DOM refs are available.\n onMounted(() => createApi());\n\n // Recreate the animator when the resolved doc changes.\n // `flush: 'post'` — the animator must be created AFTER the DOM is\n // patched, otherwise the new root element isn't in the document yet\n // and trigger setup fails (autoplay would never resume after a doc swap).\n watch(resolvedDoc, () => createApi(), { flush: 'post' });\n\n // Sync declarative play/pause props with the animator.\n watch([compMode, () => props.play, () => props.pause], () => syncPlayState());\n\n // Scrubbing progress/time only seeks — the animator is NOT recreated.\n watch([compMode, () => props.progress, () => props.time], () => applySeek());\n\n onUnmounted(() => {\n destroyApi();\n });\n\n // -- Expose imperative API ----------------------------------------------\n\n const publicApi: VueAnimatorApi = {\n isPlaying: () => apiRef.value?.isPlaying() || false,\n play: () => apiRef.value?.play(),\n pause: () => apiRef.value?.pause(),\n cancel: () => apiRef.value?.cancel(),\n finish: () => apiRef.value?.finish(),\n setPlaybackRate: (rate: number) => apiRef.value?.setPlaybackRate(rate),\n getCurrentTime: () => apiRef.value?.getCurrentTime() ?? null,\n setCurrentTime: (time: number) => apiRef.value?.setCurrentTime(time),\n getCurrentProgress: () => apiRef.value?.getCurrentProgress() ?? null,\n setCurrentProgress: (progress: number) => apiRef.value?.setCurrentProgress(progress),\n };\n\n expose(publicApi);\n\n // -- Render -------------------------------------------------------------\n\n return () => {\n const doc = resolvedDoc.value;\n return renderPxTree(doc, createVNode, makeDiag());\n };\n },\n});\n\nexport default PixodeskSvgAnimator;\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 { PX_TRIGGER_DEFAULTS, PxMouseOutAction, PxOffScreenAction, PxTriggerStart } from '@pixodesk/svg-animator-core';\nimport { createVisibilityGate, type PxVisibilityGate } from '@pixodesk/svg-animator-web/internal';\nimport { computed, defineComponent, h, onBeforeUnmount, onMounted, ref, watch, type PropType } from 'vue';\n\n\ntype AnimState = 'idle' | 'paused' | 'playing';\n\n/**\n * Controls playback of 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 Vue component\n * (`vite-svg-loader`) and pass it in the default slot:\n *\n * ```vue\n * <script setup>\n * import AnimationSvg from './animation.svg?component';\n * </script>\n *\n * <template>\n * <PixodeskSvgCssAnimator start=\"mouseOver\" mouseOut=\"pause\">\n * <AnimationSvg />\n * </PixodeskSvgCssAnimator>\n * </template>\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 * TWO AXES, the same two the JSON player has: `start` says what STARTS it, `offScreen` says\n * whether it may RUN. The gate itself is the player's (`createVisibilityGate`), so the threshold,\n * the dwell, the hysteresis and the hidden-tab rule cannot drift between the two.\n *\n * @prop start - What starts the animation:\n * - `'load'` — plays as soon as it is visible enough (default)\n * - `'mouseOver'` — plays on hover\n * - `'click'` — plays on click, toggles on second click\n * - `'none'` — **not supported here.** This wrapper exposes no `play()`, so there is\n * nothing to wait for and the animation never starts. The prop keeps the\n * shared `PxTriggerStart` type (one enum per wire key), so the value\n * type-checks — it simply has no effect. Use `PixodeskSvgAnimator` (the JSON\n * player) when you need to start an animation from code.\n * @prop offScreen - What happens while none of it is on screen:\n * - `'pause'` — pauses at the current frame (default); resumes when it comes back\n * - `'continue'` — keeps playing, and starts without waiting to be seen\n * - `'reset'` — back to the beginning, so it replays on the next entry\n * @prop mouseOut - What happens when the pointer leaves, for `start: 'mouseOver'`:\n * - `'continue'` — keeps playing (default)\n * - `'pause'` — pauses at the current frame\n * - `'reset'` — resets to the beginning\n * - `'reverse'` — **acts as `'continue'` here.** A CSS class toggle cannot run keyframes\n * backwards. Accepted so the prop keeps the shared `PxMouseOutAction` type.\n * @prop visibilityThreshold - How much of the element must be on screen (0–1) before it may run.\n * Defaults to the wire default (`0.5`), the same as the JSON player.\n * @prop visibilityDebounce - How long that must hold, in ms, before it starts. Defaults to the\n * wire default (`150`).\n * @public\n */\nconst PixodeskSvgCssAnimator = defineComponent({\n name: 'PixodeskSvgCssAnimator',\n // The wrapper spreads `attrs` itself and merges `class` by hand, so Vue must not ALSO\n // apply them — otherwise a host class lands on the div twice.\n inheritAttrs: false,\n props: {\n start: { type: String as PropType<PxTriggerStart>, default: PX_TRIGGER_DEFAULTS.start },\n offScreen: { type: String as PropType<PxOffScreenAction>, default: PX_TRIGGER_DEFAULTS.offScreen },\n mouseOut: { type: String as PropType<PxMouseOutAction>, default: PX_TRIGGER_DEFAULTS.mouseOut },\n visibilityThreshold: { type: Number, default: PX_TRIGGER_DEFAULTS.visibilityThreshold },\n visibilityDebounce: { type: Number, default: PX_TRIGGER_DEFAULTS.visibilityDebounce },\n },\n setup(props, { slots, attrs }) {\n const state = ref<AnimState>('idle');\n const el = ref<HTMLDivElement | null>(null);\n let gate: PxVisibilityGate | null = null;\n\n const goOut = (): void => {\n state.value =\n props.mouseOut === PxMouseOutAction.reset ? 'idle' :\n props.mouseOut === PxMouseOutAction.pause ? 'paused' : 'playing';\n };\n\n const attach = (): void => {\n gate?.dispose();\n gate = null;\n const node = el.value;\n if (!node) return;\n gate = createVisibilityGate(node, {\n start: props.start,\n offScreen: props.offScreen,\n mouseOut: props.mouseOut,\n visibilityThreshold: props.visibilityThreshold,\n visibilityDebounce: props.visibilityDebounce,\n }, {\n isPlaying: () => state.value === 'playing',\n play: () => { state.value = 'playing'; },\n pause: () => { state.value = 'paused'; },\n cancel: () => { state.value = 'idle'; },\n });\n // Only `load` is held by the gate; a hover or a click is aimed at something seen.\n if (props.start === PxTriggerStart.load) gate.requestStart(false);\n };\n\n onMounted(attach);\n watch(() => [props.start, props.offScreen, props.mouseOut, props.visibilityThreshold, props.visibilityDebounce], attach);\n onBeforeUnmount(() => { gate?.dispose(); gate = null; });\n\n const requestStart = (): void => {\n if (gate) gate.requestStart(true);\n else state.value = 'playing';\n };\n\n const cssClass = computed(() =>\n state.value === 'playing' ? 'px-anim-enabled px-anim-playing' :\n state.value === 'paused' ? 'px-anim-enabled' : ''\n );\n\n const handlers = computed(() =>\n props.start === PxTriggerStart.mouseOver\n ? { onMouseenter: requestStart, onMouseleave: goOut }\n : props.start === PxTriggerStart.click\n ? { onClick: () => { if (state.value === 'playing') state.value = 'paused'; else requestStart(); } }\n : {}\n );\n\n return () => h('div', {\n ref: el,\n ...attrs,\n class: [attrs.class, cssClass.value],\n ...handlers.value,\n }, slots.default?.());\n },\n});\nexport default PixodeskSvgCssAnimator;\n"],"mappings":"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","names":["computed","defineComponent","h","onMounted","ref","watch"]}
|