@pixodesk/svg-animator-vue 1.0.35 → 1.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -64,7 +64,8 @@ const animator = ref<VueAnimatorApi | null>(null);
64
64
  </template>
65
65
  ```
66
66
 
67
- `VueAnimatorApi` methods: `play()`, `pause()`, `cancel()`, `finish()`, `isPlaying()`, `setPlaybackRate(rate)`, `getCurrentTime()`, `setCurrentTime(ms)`.
67
+ <!-- px-check members VueAnimatorApi pkg=vue -->
68
+ `VueAnimatorApi` methods: `play()`, `pause()`, `cancel()`, `finish()`, `isPlaying()`, `setPlaybackRate(rate)`, `getCurrentTime()`, `setCurrentTime(ms)`, `getCurrentProgress()`, `setCurrentProgress(p)`.
68
69
 
69
70
  ### Controlled time
70
71
 
@@ -79,6 +80,7 @@ Render a single frame — by time in milliseconds, or by fraction of the whole t
79
80
 
80
81
  ## Props
81
82
 
83
+ <!-- px-check props PixodeskSvgAnimator pkg=vue -->
82
84
  | Prop | Type | Description |
83
85
  |---|---|---|
84
86
  | `doc` | `PxAnimatedSvgDocument` | The animation document to render (required) |
@@ -87,12 +89,15 @@ Render a single frame — by time in milliseconds, or by fraction of the whole t
87
89
  | `pause` | `boolean` | Pause current playback |
88
90
  | `progress` | `number` | show the frame at this position in the whole timeline (duration × iterations): `0` is the first frame, `0.5` the middle, `1` the last |
89
91
  | `time` | `number` | show the frame at that time, in milliseconds from the start |
90
- | `config` | `object \| string` | Per-instance override of the document's `animator` block, deep-merged over it — same shape as the file (`{ timeline: { mode, frameRate, fillMode, direction, trigger: { outAction, … } } }`); `null` at a slot deletes that key. A JSON string is accepted too |
91
- | `resetDocDefaults` | `boolean` | Ignore the document's playback settings and start from the player's defaults, with `config` on top |
92
- | `duration` | `number` | Shortcut for `config.timeline.duration` (ms) |
93
- | `delay` | `number` | Shortcut for `config.timeline.delay` (ms) |
94
- | `iterations` | `number \| 'infinite'` | Shortcut for `config.timeline.iterations` |
95
- | `startOn` | `'load' \| 'mouseOver' \| 'click' \| 'scrollIntoView' \| 'programmatic'` | Shortcut for `config.timeline.trigger.startOn` |
92
+ | `timeline` | `object \| string` | per-instance override of the document's `timeline` block, deep-merged over it — same shape as the file; `null` at any slot deletes that key. A JSON string is accepted too. See [Playback overrides](#playback-overrides) |
93
+ | `resetTimeline` | `boolean` | ignore the document's own timeline and start from the player's default timeline, with `timeline` on top |
94
+ | `duration` | `number` | Shortcut for `timeline.duration` (ms) |
95
+ | `delay` | `number` | Shortcut for `timeline.delay` (ms) |
96
+ | `iterations` | `number \| 'infinite'` | Shortcut for `timeline.iterations` |
97
+ | `startOn` | `'load' \| 'mouseOver' \| 'click' \| 'scrollIntoView' \| 'programmatic'` | Shortcut for `timeline.trigger.startOn` |
98
+ | `onWarn` | `(diagnostic) => void` | something is off but the animation still plays; without it → `console.warn` (a prop, not an event — see below) |
99
+ | `onError` | `(diagnostic) => void` | the animation could not be produced at all; without it → `console.error` |
100
+ | `silent` | `boolean \| PxDiagnosticKind[]` | silences the console fallback — everything, or just the kinds listed |
96
101
 
97
102
  With none of `autoplay` / `play` / `pause` / `progress` / `time` set, the component renders the animation statically (initial state, no playback); use the template ref for imperative control.
98
103
 
@@ -100,11 +105,12 @@ Note: passing a different `doc` (or unmounting) throws the old animator away and
100
105
 
101
106
  ## Events
102
107
 
108
+ <!-- px-check emits PixodeskSvgAnimator pkg=vue -->
103
109
  | Event | Description |
104
110
  |---|---|
105
111
  | `play` | Animation started or resumed |
106
112
  | `pause` | Animation paused |
107
- | `cancel` | Animation cancelled |
108
- | `finish` | Animation finished naturally |
113
+ | `cancel` | Animation canceled |
114
+ | `finish` | Animation reached its end — every iteration played, or `finish()` was called |
109
115
  | `remove` | Animation cleaned up (e.g. on unmount) |
110
116
  | `stop` | Fired alongside any event that halts playback (`pause` / `cancel` / `finish` / `remove`) |
package/dist/index.cjs CHANGED
@@ -30,7 +30,7 @@ module.exports = __toCommonJS(index_exports);
30
30
  var import_svg_animator_web = require("@pixodesk/svg-animator-web");
31
31
  var import_vue = require("vue");
32
32
  var VUE_PROP = { ref: "ref" };
33
- function createVueAdapter(elementRefs) {
33
+ function createVueAdapter(elementRefs, diag) {
34
34
  const warnedSelectors = /* @__PURE__ */ new Set();
35
35
  const adapter = {
36
36
  isConnected: () => true,
@@ -39,7 +39,7 @@ function createVueAdapter(elementRefs) {
39
39
  const element = elementRefs.get(id);
40
40
  if (!element && !warnedSelectors.has(id)) {
41
41
  warnedSelectors.add(id);
42
- console.warn('setAttribute: No elements found for id "' + id + '"');
42
+ diag.warn(import_svg_animator_web.PxDiagnosticKind.host, 'setAttribute: No elements found for id "' + id + '"');
43
43
  }
44
44
  if (element) {
45
45
  element.setAttribute(attrName, value);
@@ -51,19 +51,19 @@ function createVueAdapter(elementRefs) {
51
51
  };
52
52
  return adapter;
53
53
  }
54
- function applyDocOverrides(doc, props, compMode) {
55
- const { config, resetDocDefaults, duration, delay, iterations, startOn } = props;
56
- const patch = (0, import_svg_animator_web.foldAnimatorConfigShortcuts)(config, { duration, delay, iterations, startOn });
57
- const fullPatch = compMode !== "autoplay" /* autoplay */ ? {
54
+ function applyDocOverrides(doc, props, compMode, diag) {
55
+ const { timeline, resetTimeline, duration, delay, iterations, startOn } = props;
56
+ const patch = (0, import_svg_animator_web.foldTimelineOverride)(timeline, { duration, delay, iterations, startOn });
57
+ const fullPatch = (0, import_svg_animator_web.controlModeTakesOverTrigger)(compMode) ? {
58
58
  ...patch ?? {},
59
59
  timeline: {
60
60
  ...patch?.timeline ?? {},
61
61
  trigger: { ...patch?.timeline?.trigger ?? {}, startOn: "programmatic" }
62
62
  }
63
63
  } : patch;
64
- if (fullPatch !== void 0 || resetDocDefaults) {
65
- const applied = (0, import_svg_animator_web.applyAnimatorConfig)(doc, fullPatch ?? {}, { resetDefaults: !!resetDocDefaults });
66
- for (const w of applied.warnings) console.warn("[PixodeskSvgAnimator] config override:", w);
64
+ if (fullPatch !== void 0 || resetTimeline) {
65
+ const applied = (0, import_svg_animator_web.applyAnimatorConfig)(doc, fullPatch ?? {}, { resetDefaults: !!resetTimeline });
66
+ for (const w of applied.warnings) diag.warn(import_svg_animator_web.PxDiagnosticKind.usage, "timeline override: " + w);
67
67
  doc = applied.doc;
68
68
  }
69
69
  return doc;
@@ -73,9 +73,9 @@ function calcSeekMs(doc, props) {
73
73
  const animator = (0, import_svg_animator_web.getAnimatorConfig)(doc) || {};
74
74
  if (props.progress !== void 0) {
75
75
  const iterationsValue = props.iterations ?? animator.iterations;
76
- const iterationsCount = typeof iterationsValue === "number" && iterationsValue >= 1 ? iterationsValue : 1;
77
- const singleDuration = props.duration ?? animator.duration ?? 1e3;
78
- seekMs = props.progress * singleDuration * iterationsCount;
76
+ const iterationsCount = iterationsValue === "infinite" ? Infinity : typeof iterationsValue === "number" && iterationsValue >= 1 ? iterationsValue : 1;
77
+ const singleDuration = props.duration ?? animator.duration ?? import_svg_animator_web.DEFAULT_DURATION_MS;
78
+ seekMs = (0, import_svg_animator_web.progressToTimeMs)(props.progress, singleDuration, iterationsCount);
79
79
  }
80
80
  if (props.time !== void 0) seekMs = props.time;
81
81
  return seekMs;
@@ -85,10 +85,10 @@ var PixodeskSvgAnimator = (0, import_vue.defineComponent)({
85
85
  props: {
86
86
  // -- Source
87
87
  doc: { type: Object, required: true },
88
- // -- Playback override: one object spelled exactly like `animator` in the file,
89
- // plus the four shortcuts people reach for most.
90
- config: { type: [Object, String] },
91
- resetDocDefaults: { type: Boolean, default: void 0 },
88
+ // -- Playback override: the document's `timeline` block as a patch (or a JSON string),
89
+ // plus the four shortcuts people reach for most. The same names as React / RN.
90
+ timeline: { type: [Object, String] },
91
+ resetTimeline: { type: Boolean, default: void 0 },
92
92
  duration: { type: Number },
93
93
  delay: { type: Number },
94
94
  iterations: { type: [Number, String] },
@@ -99,21 +99,41 @@ var PixodeskSvgAnimator = (0, import_vue.defineComponent)({
99
99
  pause: { type: Boolean, default: void 0 },
100
100
  // -- Controlled time
101
101
  progress: { type: Number },
102
- time: { type: Number }
102
+ time: { type: Number },
103
+ // -- Diagnostics (API review §5).
104
+ // Function PROPS, not emits, on purpose: an emit handler always exists, so wiring
105
+ // these to `emit` would permanently suppress the console fallback for anyone who
106
+ // never listens. As props, "not given" really is undefined and the console still
107
+ // speaks by default — the same contract as React and React Native.
108
+ onWarn: { type: Function },
109
+ onError: { type: Function },
110
+ // `silent` takes `true` or just the kinds to quiet, so `platform` chatter can be
111
+ // silenced while `document` problems still speak.
112
+ silent: { type: [Boolean, Array], default: void 0 }
103
113
  },
104
114
  emits: ["play", "stop", "pause", "cancel", "finish", "remove"],
105
115
  setup(props, { expose, emit }) {
106
116
  const elementRefs = /* @__PURE__ */ new Map();
107
117
  const apiRef = (0, import_vue.shallowRef)(null);
108
- const compMode = (0, import_vue.computed)(() => {
109
- if (props.autoplay) return "autoplay" /* autoplay */;
110
- if (props.progress !== void 0 || props.time !== void 0) return "fixedTime" /* fixedTime */;
111
- if (props.play !== void 0 || props.pause !== void 0) return "play" /* play */;
112
- return "static" /* static */;
113
- });
118
+ const makeDiag = () => (0, import_svg_animator_web.createDiagnostics)(
119
+ { onWarn: props.onWarn, onError: props.onError, silent: props.silent },
120
+ "[PixodeskSvgAnimator]"
121
+ );
122
+ const resolvedMode = (0, import_vue.computed)(() => (0, import_svg_animator_web.resolveControlMode)({
123
+ progress: props.progress,
124
+ time: props.time,
125
+ play: props.play,
126
+ pause: props.pause,
127
+ autoplay: props.autoplay
128
+ }));
129
+ const compMode = (0, import_vue.computed)(() => resolvedMode.value.mode);
130
+ (0, import_vue.watch)(resolvedMode, (r) => {
131
+ const diag = makeDiag();
132
+ for (const w of r.warnings) diag.warn(import_svg_animator_web.PxDiagnosticKind.usage, w);
133
+ }, { immediate: true });
114
134
  const resolvedDoc = (0, import_vue.computed)(() => {
115
135
  let doc = (0, import_svg_animator_web.generateNewIds)(props.doc);
116
- return applyDocOverrides(doc, props, compMode.value);
136
+ return applyDocOverrides(doc, props, compMode.value, makeDiag());
117
137
  });
118
138
  function renderNode(node) {
119
139
  if (!node) return null;
@@ -130,32 +150,27 @@ var PixodeskSvgAnimator = (0, import_vue.defineComponent)({
130
150
  };
131
151
  }
132
152
  const childVNodes = children?.map((child) => renderNode(child)).filter(Boolean);
133
- return (0, import_vue.h)(type, normProps, childVNodes);
153
+ const text = typeof node.textContent === "string" ? node.textContent : void 0;
154
+ return (0, import_vue.h)(type, normProps, childVNodes?.length ? childVNodes : text);
134
155
  }
135
156
  function createApi() {
136
157
  destroyApi();
137
158
  const doc = resolvedDoc.value;
138
159
  if (!doc) return;
139
- const callbacks = {
160
+ apiRef.value = (0, import_svg_animator_web.createAnimator)({
161
+ doc,
162
+ adapter: createVueAdapter(elementRefs, makeDiag()),
140
163
  onPlay: () => emit("play"),
141
- onPause: () => {
142
- emit("pause");
143
- emit("stop");
144
- },
145
- onCancel: () => {
146
- emit("cancel");
147
- emit("stop");
148
- },
149
- onFinish: () => {
150
- emit("finish");
151
- emit("stop");
152
- },
153
- onRemove: () => {
154
- emit("remove");
155
- emit("stop");
156
- }
157
- };
158
- apiRef.value = (0, import_svg_animator_web.createAnimator)({ data: doc, adapter: createVueAdapter(elementRefs), callbacks });
164
+ onPause: () => emit("pause"),
165
+ onCancel: () => emit("cancel"),
166
+ onFinish: () => emit("finish"),
167
+ onRemove: () => emit("remove"),
168
+ onStop: () => emit("stop"),
169
+ // The player's own diagnostics reach the same handlers as the component's.
170
+ onWarn: props.onWarn,
171
+ onError: props.onError,
172
+ silent: props.silent
173
+ });
159
174
  syncPlayState();
160
175
  applySeek();
161
176
  }
@@ -164,19 +179,19 @@ var PixodeskSvgAnimator = (0, import_vue.defineComponent)({
164
179
  apiRef.value = null;
165
180
  }
166
181
  function syncPlayState() {
167
- if (compMode.value !== "play" /* play */) return;
182
+ if (compMode.value !== import_svg_animator_web.PxControlMode.play) return;
168
183
  if (props.play && !props.pause) {
169
184
  apiRef.value?.play();
170
185
  } else if (props.pause) {
171
186
  apiRef.value?.pause();
172
187
  } else if (props.play === false) {
173
- apiRef.value?.finish();
188
+ apiRef.value?.pause();
174
189
  } else {
175
190
  apiRef.value?.play();
176
191
  }
177
192
  }
178
193
  function applySeek() {
179
- if (compMode.value !== "fixedTime" /* fixedTime */) return;
194
+ if (compMode.value !== import_svg_animator_web.PxControlMode.fixedTime) return;
180
195
  const doc = resolvedDoc.value;
181
196
  if (!doc) return;
182
197
  const seekMs = calcSeekMs(doc, props);
@@ -200,7 +215,9 @@ var PixodeskSvgAnimator = (0, import_vue.defineComponent)({
200
215
  finish: () => apiRef.value?.finish(),
201
216
  setPlaybackRate: (rate) => apiRef.value?.setPlaybackRate(rate),
202
217
  getCurrentTime: () => apiRef.value?.getCurrentTime() ?? null,
203
- setCurrentTime: (time) => apiRef.value?.setCurrentTime(time)
218
+ setCurrentTime: (time) => apiRef.value?.setCurrentTime(time),
219
+ getCurrentProgress: () => apiRef.value?.getCurrentProgress() ?? null,
220
+ setCurrentProgress: (progress) => apiRef.value?.setCurrentProgress(progress)
204
221
  };
205
222
  expose(publicApi);
206
223
  return () => {
@@ -212,13 +229,15 @@ var PixodeskSvgAnimator = (0, import_vue.defineComponent)({
212
229
  var PixodeskSvgAnimator_default = PixodeskSvgAnimator;
213
230
 
214
231
  // src/PixodeskSvgCssAnimator.ts
232
+ var import_svg_animator_web2 = require("@pixodesk/svg-animator-web");
215
233
  var import_vue2 = require("vue");
216
234
  var PixodeskSvgCssAnimator = (0, import_vue2.defineComponent)({
217
235
  name: "PixodeskSvgCssAnimator",
218
236
  inheritAttrs: false,
219
237
  props: {
220
238
  startOn: { type: String, default: "load" },
221
- outAction: { type: String, default: "continue" }
239
+ outAction: { type: String, default: "continue" },
240
+ scrollIntoViewThreshold: { type: Number, default: import_svg_animator_web2.PX_TRIGGER_DEFAULTS.scrollIntoViewThreshold }
222
241
  },
223
242
  setup(props, { slots }) {
224
243
  const attrs = (0, import_vue2.useAttrs)();
@@ -233,11 +252,13 @@ var PixodeskSvgCssAnimator = (0, import_vue2.defineComponent)({
233
252
  const el = divRef.value;
234
253
  if (!el) return;
235
254
  const outState = props.outAction === "reset" ? "idle" : props.outAction === "pause" ? "paused" : "playing";
255
+ const threshold = props.scrollIntoViewThreshold;
256
+ const visible = (entry) => threshold > 0 ? entry.intersectionRatio >= threshold : entry.isIntersecting;
236
257
  const observer = new IntersectionObserver(
237
258
  ([entry]) => {
238
- state.value = entry.isIntersecting ? "playing" : outState;
259
+ state.value = visible(entry) ? "playing" : outState;
239
260
  },
240
- { threshold: 0.1 }
261
+ { threshold }
241
262
  );
242
263
  observer.observe(el);
243
264
  observerCleanup = () => observer.disconnect();
@@ -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, PxAnimatorConfigPatch, PxNode, PxPlatformAdapter, PxTimelineEngineExtra, PxTrigger } from '@pixodesk/svg-animator-web';\nimport { camelCaseToKebabWordIfNeeded, createAnimator, FillMode, generateNewIds, getNormalizedProps, STYLE_ATTR_NAMES, applyAnimatorConfig, foldAnimatorConfigShortcuts, getAnimatorConfig } from '@pixodesk/svg-animator-web';\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' } as const;\n\nexport interface VueAnimatorApi {\n /** Returns true if the animation is currently running. */\n isPlaying(): boolean;\n\n /** Starts or resumes the animation. */\n play(): void;\n\n /** Pauses the animation at its current state. */\n pause(): void;\n\n /** Stops the animation and resets it to its initial state. */\n cancel(): void;\n\n /** Jumps to the end of the animation and holds the final state. */\n finish(): void;\n\n /** Changes the speed of the animation. 1 is normal, 2 is double, -1 is reverse. */\n setPlaybackRate(rate: number): void;\n\n /** Returns the current playback time in milliseconds. */\n getCurrentTime(): number | null;\n\n /** Jumps to a specific time (in milliseconds) in the animation. */\n setCurrentTime(time: number): void;\n}\n\n\n// -- Internal types ---------------------------------------------------------\n\nenum CompMode {\n static = 'static',\n autoplay = 'autoplay',\n play = 'play',\n fixedTime = 'fixedTime'\n}\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>) {\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 console.warn('setAttribute: No elements found for id \"' + id + '\"');\n }\n\n if (element) {\n element.setAttribute(attrName, value);\n if (STYLE_ATTR_NAMES.has(attrName)) {\n (element as HTMLElement).style[attrName as any] = value;\n }\n }\n },\n };\n return adapter;\n}\n\n// FIXME: add model validation (e.g. isElementFileJson check)\n\n\n// -- Helper: apply doc overrides --------------------------------------------\n\ninterface DocOverrideProps {\n /** Per-instance override of the document's `animator` config — the same shape as\n * `animator` in SCHEMA.md, deep-merged; `null` at a slot deletes it. Also accepts a\n * JSON string. Replaces the former flat mode/fill/direction/frameRate/outAction props. */\n config?: PxAnimatorConfigPatch | string;\n /** Start from the player's defaults instead of the document's playback settings. */\n resetDocDefaults?: boolean;\n duration?: number;\n delay?: number;\n iterations?: number | 'infinite';\n startOn?: 'load' | 'mouseOver' | 'click' | 'scrollIntoView' | 'programmatic';\n progress?: number;\n time?: number;\n}\n\nfunction applyDocOverrides(\n doc: PxAnimatedSvgDocument,\n props: DocOverrideProps,\n compMode: CompMode,\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 PLAYBACK-OVERRIDE-PLAN.md §1.1.\n const { config, resetDocDefaults, duration, delay, iterations, startOn } = props;\n const patch = foldAnimatorConfigShortcuts(config, { duration, delay, iterations, startOn });\n const fullPatch: any = compMode !== CompMode.autoplay\n ? {\n ...(patch ?? {}),\n timeline: {\n ...((patch as any)?.timeline ?? {}),\n trigger: { ...((patch as any)?.timeline?.trigger ?? {}), startOn: 'programmatic' },\n },\n }\n : patch;\n\n if (fullPatch !== undefined || resetDocDefaults) {\n const applied = applyAnimatorConfig(doc, fullPatch ?? {}, { resetDefaults: !!resetDocDefaults });\n for (const w of applied.warnings) console.warn('[PixodeskSvgAnimator] config override:', 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); `time` is absolute.\n * Applied through the animator API (setCurrentTime) so scrubbing does NOT\n * 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 const iterationsValue = props.iterations ?? animator.iterations;\n const iterationsCount = typeof iterationsValue === 'number' && iterationsValue >= 1 ? iterationsValue : 1;\n const singleDuration = props.duration ?? animator.duration ?? 1000; // engine default duration\n seekMs = 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 */\nconst PixodeskSvgAnimator = defineComponent({\n name: 'PixodeskSvgAnimator',\n\n props: {\n // -- Source\n doc: { type: Object as PropType<PxAnimatedSvgDocument>, required: true },\n\n // -- Playback override: one object spelled exactly like `animator` in the file,\n // plus the four shortcuts people reach for most.\n config: { type: [Object, String] as PropType<PxAnimatorConfigPatch | string> },\n resetDocDefaults: { type: Boolean, default: undefined },\n duration: { type: Number },\n delay: { type: Number },\n iterations: { type: [Number, String] as PropType<number | 'infinite'> },\n startOn: { type: String as PropType<'load' | 'mouseOver' | 'click' | 'scrollIntoView' | 'programmatic'> },\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\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 // -- Determine control mode ---------------------------------------------\n\n const compMode = computed<CompMode>(() => {\n if (props.autoplay) return CompMode.autoplay;\n if (props.progress !== undefined || props.time !== undefined) return CompMode.fixedTime;\n if (props.play !== undefined || props.pause !== undefined) return CompMode.play;\n return CompMode.static;\n });\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);\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, ...attrs } = node;\n const normProps = getNormalizedProps(attrs);\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 return h(type, normProps, childVNodes);\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.\n // `stop` fires alongside any event that halts playback.\n const callbacks = {\n onPlay: () => emit('play'),\n onPause: () => { emit('pause'); emit('stop'); },\n onCancel: () => { emit('cancel'); emit('stop'); },\n onFinish: () => { emit('finish'); emit('stop'); },\n onRemove: () => { emit('remove'); emit('stop'); },\n };\n\n apiRef.value = createAnimator({ data: doc, adapter: createVueAdapter(elementRefs), callbacks });\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 !== CompMode.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 → jump to the end state\n apiRef.value?.finish();\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 !== CompMode.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 };\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 { OutAction, StartOn } from \"@pixodesk/svg-animator-web\";\nimport { computed, defineComponent, h, onMounted, onUnmounted, ref, useAttrs, type PropType } from 'vue';\n\n\ntype AnimState = 'idle' | 'paused' | 'playing';\n\n\n/**\n * Controls playback of a SVG+CSS animated file 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** flavor (no `<script>` tag). Import the SVG as a Vue component\n * via `vite-svg-loader` and pass it as the default slot:\n *\n * ```vue\n * <script setup>\n * import AnimationSvg from './animation.svg'; // vite-svg-loader\n * </script>\n *\n * <template>\n * <PixodeskSvgCssAnimator startOn=\"mouseOver\" outAction=\"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 * @prop startOn - What triggers the animation to start:\n * - `'load'` — plays immediately on mount (default)\n * - `'mouseOver'` — plays on hover\n * - `'click'` — plays on click, toggles on second click\n * - `'scrollIntoView'` — plays when the element enters the viewport\n * @prop outAction - What happens when the trigger ends (hover/scroll out, second click):\n * - `'continue'` — keeps playing (default)\n * - `'pause'` — pauses at the current frame\n * - `'reset'` — resets to the beginning\n */\nconst PixodeskSvgCssAnimator = defineComponent({\n name: 'PixodeskSvgCssAnimator',\n\n inheritAttrs: false,\n\n props: {\n startOn: { type: String as PropType<StartOn>, default: 'load' },\n outAction: { type: String as PropType<OutAction>, default: 'continue' },\n },\n\n setup(props, { slots }) {\n const attrs = useAttrs();\n const state = ref<AnimState>(props.startOn === 'load' ? 'playing' : 'idle');\n const divRef = ref<HTMLDivElement | null>(null);\n\n const goOut = () => {\n state.value =\n props.outAction === 'reset' ? 'idle' :\n props.outAction === 'pause' ? 'paused' : 'playing';\n };\n\n let observerCleanup: (() => void) | undefined;\n\n onMounted(() => {\n if (props.startOn !== 'scrollIntoView') return;\n const el = divRef.value;\n if (!el) return;\n const outState: AnimState =\n props.outAction === 'reset' ? 'idle' :\n props.outAction === 'pause' ? 'paused' : 'playing';\n const observer = new IntersectionObserver(\n ([entry]) => { state.value = entry.isIntersecting ? 'playing' : outState; },\n { threshold: 0.1 }\n );\n observer.observe(el);\n observerCleanup = () => observer.disconnect();\n });\n\n onUnmounted(() => observerCleanup?.());\n\n const animClass = 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.startOn === 'mouseOver' ? {\n onMouseenter: () => { state.value = 'playing'; },\n onMouseleave: goOut,\n } :\n props.startOn === 'click' ? {\n onClick: () => state.value === 'playing' ? goOut() : (state.value = 'playing'),\n } : {}\n );\n\n return () => h('div', {\n ref: divRef,\n ...attrs,\n class: [attrs.class, animClass.value],\n ...handlers.value,\n }, slots.default?.());\n },\n});\n\nexport default PixodeskSvgCssAnimator;\n"],"mappings":"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","names":["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, PxNode, PxPlatformAdapter, PxTimelineEngineExtra, PxTimelinePatch, PxTrigger } from '@pixodesk/svg-animator-web';\nimport { camelCaseToKebabWordIfNeeded, createAnimator, createDiagnostics, generateNewIds, getNormalizedProps, STYLE_ATTR_NAMES, applyAnimatorConfig, foldTimelineOverride, getAnimatorConfig, PxControlMode, resolveControlMode, controlModeTakesOverTrigger, PxDiagnosticKind, progressToTimeMs, DEFAULT_DURATION_MS, type PxAnimatorHandle, type PxControlProps, type PxPlaybackOverrideProps, type PxDiagnostic, type PxDiagnostics } from '@pixodesk/svg-animator-web';\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' } 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 */\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, 'setAttribute: No elements found for id \"' + id + '\"');\n }\n\n if (element) {\n element.setAttribute(attrName, value);\n if (STYLE_ATTR_NAMES.has(attrName)) {\n (element as HTMLElement).style[attrName as any] = value;\n }\n }\n },\n };\n return adapter;\n}\n\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 = PxPlaybackOverrideProps & 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 PLAYBACK-OVERRIDE-PLAN.md §1.1.\n const { timeline, resetTimeline, duration, delay, iterations, startOn } = props;\n const patch = foldTimelineOverride(timeline, { duration, delay, iterations, startOn });\n const fullPatch: any = controlModeTakesOverTrigger(compMode)\n ? {\n ...(patch ?? {}),\n timeline: {\n ...((patch as any)?.timeline ?? {}),\n trigger: { ...((patch as any)?.timeline?.trigger ?? {}), startOn: 'programmatic' },\n },\n }\n : patch;\n\n if (fullPatch !== undefined || resetTimeline) {\n const applied = applyAnimatorConfig(doc, fullPatch ?? {}, { resetDefaults: !!resetTimeline });\n for (const w of applied.warnings) diag.warn(PxDiagnosticKind.usage, 'timeline override: ' + 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 ?? 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 */\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 startOn: { type: String as PropType<'load' | 'mouseOver' | 'click' | 'scrollIntoView' | 'programmatic'> },\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 // `silent` takes `true` or just the kinds to quiet, so `platform` chatter can be\n // silenced while `document` problems still speak.\n silent: { type: [Boolean, Array] as PropType<boolean | ReadonlyArray<PxDiagnosticKind>>, 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, silent: props.silent },\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, 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, ...attrs } = node;\n const normProps = getNormalizedProps(attrs);\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). `getNormalizedProps` 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 apiRef.value = createAnimator({\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 silent: props.silent,\n });\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, PxOutAction, PxStartOn } from \"@pixodesk/svg-animator-web\";\nimport { computed, defineComponent, h, onMounted, onUnmounted, ref, useAttrs, type PropType } from 'vue';\n\n\ntype AnimState = 'idle' | 'paused' | 'playing';\n\n\n/**\n * Controls playback of a SVG+CSS animated file 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** flavor (no `<script>` tag). Import the SVG as a Vue component\n * via `vite-svg-loader` and pass it as the default slot:\n *\n * ```vue\n * <script setup>\n * import AnimationSvg from './animation.svg'; // vite-svg-loader\n * </script>\n *\n * <template>\n * <PixodeskSvgCssAnimator startOn=\"mouseOver\" outAction=\"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 * @prop startOn - What triggers the animation to start:\n * - `'load'` — plays immediately on mount (default)\n * - `'mouseOver'` — plays on hover\n * - `'click'` — plays on click, toggles on second click\n * - `'scrollIntoView'` — plays when the element enters the viewport\n * - `'programmatic'` — **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 `PxStartOn` type (one enum per wire key), so the value type-checks\n * — it simply has no effect. Use `PixodeskSvgAnimator` (the JSON player)\n * when you need to start an animation from code.\n * @prop outAction - What happens when the trigger ends (hover/scroll out, second click):\n * - `'continue'` — keeps playing (default)\n * - `'pause'` — pauses at the current frame\n * - `'reset'` — resets to the beginning\n * - `'reverse'` — **acts as `'continue'` here.** A CSS class toggle cannot run keyframes\n * backwards. Accepted so the prop keeps the shared `PxOutAction` type.\n * @prop scrollIntoViewThreshold - For `'scrollIntoView'`: how much of the element must be\n * visible (0–1) before it starts, and below which the out action applies. Defaults to the\n * wire default (`0`, any pixel) — the same as the JSON player, not a private `0.1`.\n */\nconst PixodeskSvgCssAnimator = defineComponent({\n name: 'PixodeskSvgCssAnimator',\n\n inheritAttrs: false,\n\n props: {\n startOn: { type: String as PropType<PxStartOn>, default: 'load' },\n outAction: { type: String as PropType<PxOutAction>, default: 'continue' },\n scrollIntoViewThreshold: { type: Number, default: PX_TRIGGER_DEFAULTS.scrollIntoViewThreshold },\n },\n\n setup(props, { slots }) {\n const attrs = useAttrs();\n const state = ref<AnimState>(props.startOn === 'load' ? 'playing' : 'idle');\n const divRef = ref<HTMLDivElement | null>(null);\n\n const goOut = () => {\n state.value =\n props.outAction === 'reset' ? 'idle' :\n props.outAction === 'pause' ? 'paused' : 'playing';\n };\n\n let observerCleanup: (() => void) | undefined;\n\n onMounted(() => {\n if (props.startOn !== 'scrollIntoView') return;\n const el = divRef.value;\n if (!el) return;\n const outState: AnimState =\n props.outAction === 'reset' ? 'idle' :\n props.outAction === 'pause' ? 'paused' : 'playing';\n // `isIntersecting` is true at ONE visible pixel, so with a threshold above 0 it could\n // never report \"out\" — read the ratio against the threshold instead (review §13).\n const threshold = props.scrollIntoViewThreshold;\n const visible = (entry: IntersectionObserverEntry): boolean =>\n threshold > 0 ? entry.intersectionRatio >= threshold : entry.isIntersecting;\n const observer = new IntersectionObserver(\n ([entry]) => { state.value = visible(entry) ? 'playing' : outState; },\n { threshold }\n );\n observer.observe(el);\n observerCleanup = () => observer.disconnect();\n });\n\n onUnmounted(() => observerCleanup?.());\n\n const animClass = 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.startOn === 'mouseOver' ? {\n onMouseenter: () => { state.value = 'playing'; },\n onMouseleave: goOut,\n } :\n props.startOn === 'click' ? {\n onClick: () => state.value === 'playing' ? goOut() : (state.value = 'playing'),\n } : {}\n );\n\n return () => h('div', {\n ref: divRef,\n ...attrs,\n class: [attrs.class, animClass.value],\n ...handlers.value,\n }, slots.default?.());\n },\n});\n\nexport default PixodeskSvgCssAnimator;\n"],"mappings":"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","names":["import_svg_animator_web","import_vue"]}
package/dist/index.d.cts CHANGED
@@ -1,25 +1,12 @@
1
1
  import * as vue from 'vue';
2
2
  import { PropType, VNode } from 'vue';
3
- import { PxAnimatedSvgDocument, PxAnimatorConfigPatch, StartOn, OutAction } from '@pixodesk/svg-animator-web';
3
+ import { PxAnimatedSvgDocument, PxTimelinePatch, PxDiagnostic, PxDiagnosticKind, PxAnimatorHandle, PxStartOn, PxOutAction } from '@pixodesk/svg-animator-web';
4
4
 
5
- interface VueAnimatorApi {
6
- /** Returns true if the animation is currently running. */
7
- isPlaying(): boolean;
8
- /** Starts or resumes the animation. */
9
- play(): void;
10
- /** Pauses the animation at its current state. */
11
- pause(): void;
12
- /** Stops the animation and resets it to its initial state. */
13
- cancel(): void;
14
- /** Jumps to the end of the animation and holds the final state. */
15
- finish(): void;
16
- /** Changes the speed of the animation. 1 is normal, 2 is double, -1 is reverse. */
17
- setPlaybackRate(rate: number): void;
18
- /** Returns the current playback time in milliseconds. */
19
- getCurrentTime(): number | null;
20
- /** Jumps to a specific time (in milliseconds) in the animation. */
21
- setCurrentTime(time: number): void;
22
- }
5
+ /**
6
+ * The imperative handle the template ref exposes — core's `PxAnimatorHandle` under this
7
+ * package's name (review §9). One definition for React, Vue and React Native.
8
+ */
9
+ type VueAnimatorApi = PxAnimatorHandle;
23
10
  /**
24
11
  * Vue component for rendering and controlling Pixodesk SVG animations.
25
12
  *
@@ -52,10 +39,10 @@ declare const PixodeskSvgAnimator: vue.DefineComponent<vue.ExtractPropTypes<{
52
39
  type: PropType<PxAnimatedSvgDocument>;
53
40
  required: true;
54
41
  };
55
- config: {
56
- type: PropType<PxAnimatorConfigPatch | string>;
42
+ timeline: {
43
+ type: PropType<PxTimelinePatch | string>;
57
44
  };
58
- resetDocDefaults: {
45
+ resetTimeline: {
59
46
  type: BooleanConstructor;
60
47
  default: undefined;
61
48
  };
@@ -89,17 +76,27 @@ declare const PixodeskSvgAnimator: vue.DefineComponent<vue.ExtractPropTypes<{
89
76
  time: {
90
77
  type: NumberConstructor;
91
78
  };
79
+ onWarn: {
80
+ type: PropType<(diagnostic: PxDiagnostic) => void>;
81
+ };
82
+ onError: {
83
+ type: PropType<(diagnostic: PxDiagnostic) => void>;
84
+ };
85
+ silent: {
86
+ type: PropType<boolean | ReadonlyArray<PxDiagnosticKind>>;
87
+ default: undefined;
88
+ };
92
89
  }>, () => VNode<vue.RendererNode, vue.RendererElement, {
93
90
  [key: string]: any;
94
- }> | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, ("play" | "stop" | "pause" | "cancel" | "finish" | "remove")[], "play" | "stop" | "pause" | "cancel" | "finish" | "remove", vue.PublicProps, Readonly<vue.ExtractPropTypes<{
91
+ }> | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, ("finish" | "play" | "pause" | "cancel" | "stop" | "remove")[], "finish" | "play" | "pause" | "cancel" | "stop" | "remove", vue.PublicProps, Readonly<vue.ExtractPropTypes<{
95
92
  doc: {
96
93
  type: PropType<PxAnimatedSvgDocument>;
97
94
  required: true;
98
95
  };
99
- config: {
100
- type: PropType<PxAnimatorConfigPatch | string>;
96
+ timeline: {
97
+ type: PropType<PxTimelinePatch | string>;
101
98
  };
102
- resetDocDefaults: {
99
+ resetTimeline: {
103
100
  type: BooleanConstructor;
104
101
  default: undefined;
105
102
  };
@@ -133,18 +130,29 @@ declare const PixodeskSvgAnimator: vue.DefineComponent<vue.ExtractPropTypes<{
133
130
  time: {
134
131
  type: NumberConstructor;
135
132
  };
133
+ onWarn: {
134
+ type: PropType<(diagnostic: PxDiagnostic) => void>;
135
+ };
136
+ onError: {
137
+ type: PropType<(diagnostic: PxDiagnostic) => void>;
138
+ };
139
+ silent: {
140
+ type: PropType<boolean | ReadonlyArray<PxDiagnosticKind>>;
141
+ default: undefined;
142
+ };
136
143
  }>> & Readonly<{
144
+ onFinish?: ((...args: any[]) => any) | undefined;
137
145
  onPlay?: ((...args: any[]) => any) | undefined;
138
- onStop?: ((...args: any[]) => any) | undefined;
139
146
  onPause?: ((...args: any[]) => any) | undefined;
140
147
  onCancel?: ((...args: any[]) => any) | undefined;
141
- onFinish?: ((...args: any[]) => any) | undefined;
148
+ onStop?: ((...args: any[]) => any) | undefined;
142
149
  onRemove?: ((...args: any[]) => any) | undefined;
143
150
  }>, {
144
- autoplay: boolean;
145
151
  play: boolean;
146
- resetDocDefaults: boolean;
147
152
  pause: boolean;
153
+ autoplay: boolean;
154
+ resetTimeline: boolean;
155
+ silent: boolean | readonly PxDiagnosticKind[];
148
156
  }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
149
157
 
150
158
  /**
@@ -176,34 +184,53 @@ declare const PixodeskSvgAnimator: vue.DefineComponent<vue.ExtractPropTypes<{
176
184
  * - `'mouseOver'` — plays on hover
177
185
  * - `'click'` — plays on click, toggles on second click
178
186
  * - `'scrollIntoView'` — plays when the element enters the viewport
187
+ * - `'programmatic'` — **not supported here.** This wrapper exposes no `play()`, so there is
188
+ * nothing to wait for and the animation never starts. The prop keeps the
189
+ * shared `PxStartOn` type (one enum per wire key), so the value type-checks
190
+ * — it simply has no effect. Use `PixodeskSvgAnimator` (the JSON player)
191
+ * when you need to start an animation from code.
179
192
  * @prop outAction - What happens when the trigger ends (hover/scroll out, second click):
180
193
  * - `'continue'` — keeps playing (default)
181
194
  * - `'pause'` — pauses at the current frame
182
195
  * - `'reset'` — resets to the beginning
196
+ * - `'reverse'` — **acts as `'continue'` here.** A CSS class toggle cannot run keyframes
197
+ * backwards. Accepted so the prop keeps the shared `PxOutAction` type.
198
+ * @prop scrollIntoViewThreshold - For `'scrollIntoView'`: how much of the element must be
199
+ * visible (0–1) before it starts, and below which the out action applies. Defaults to the
200
+ * wire default (`0`, any pixel) — the same as the JSON player, not a private `0.1`.
183
201
  */
184
202
  declare const PixodeskSvgCssAnimator: vue.DefineComponent<vue.ExtractPropTypes<{
185
203
  startOn: {
186
- type: PropType<StartOn>;
204
+ type: PropType<PxStartOn>;
187
205
  default: string;
188
206
  };
189
207
  outAction: {
190
- type: PropType<OutAction>;
208
+ type: PropType<PxOutAction>;
191
209
  default: string;
192
210
  };
211
+ scrollIntoViewThreshold: {
212
+ type: NumberConstructor;
213
+ default: 0;
214
+ };
193
215
  }>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
194
216
  [key: string]: any;
195
217
  }>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
196
218
  startOn: {
197
- type: PropType<StartOn>;
219
+ type: PropType<PxStartOn>;
198
220
  default: string;
199
221
  };
200
222
  outAction: {
201
- type: PropType<OutAction>;
223
+ type: PropType<PxOutAction>;
202
224
  default: string;
203
225
  };
226
+ scrollIntoViewThreshold: {
227
+ type: NumberConstructor;
228
+ default: 0;
229
+ };
204
230
  }>> & Readonly<{}>, {
205
- startOn: StartOn;
206
- outAction: OutAction;
231
+ startOn: PxStartOn;
232
+ outAction: PxOutAction;
233
+ scrollIntoViewThreshold: number;
207
234
  }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
208
235
 
209
236
  export { PixodeskSvgAnimator, PixodeskSvgCssAnimator, type VueAnimatorApi };