@pixodesk/svg-animator-vue 1.0.47 → 1.0.48
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.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +24 -14
- package/dist/index.umd.js.map +1 -1
- package/package.json +3 -3
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, 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"]}
|
|
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 * - `'pause'` — pauses at the current frame (default)\n * - `'continue'` — keeps playing\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.d.cts
CHANGED
|
@@ -209,8 +209,8 @@ declare const PixodeskSvgAnimator: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
209
209
|
* - `'continue'` — keeps playing, and starts without waiting to be seen
|
|
210
210
|
* - `'reset'` — back to the beginning, so it replays on the next entry
|
|
211
211
|
* @prop mouseOut - What happens when the pointer leaves, for `start: 'mouseOver'`:
|
|
212
|
-
* - `'
|
|
213
|
-
* - `'
|
|
212
|
+
* - `'pause'` — pauses at the current frame (default)
|
|
213
|
+
* - `'continue'` — keeps playing
|
|
214
214
|
* - `'reset'` — resets to the beginning
|
|
215
215
|
* - `'reverse'` — **acts as `'continue'` here.** A CSS class toggle cannot run keyframes
|
|
216
216
|
* backwards. Accepted so the prop keeps the shared `PxMouseOutAction` type.
|
|
@@ -231,7 +231,7 @@ declare const PixodeskSvgCssAnimator: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
231
231
|
};
|
|
232
232
|
mouseOut: {
|
|
233
233
|
type: PropType<PxMouseOutAction>;
|
|
234
|
-
default: "
|
|
234
|
+
default: "pause";
|
|
235
235
|
};
|
|
236
236
|
visibilityThreshold: {
|
|
237
237
|
type: NumberConstructor;
|
|
@@ -254,7 +254,7 @@ declare const PixodeskSvgCssAnimator: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
254
254
|
};
|
|
255
255
|
mouseOut: {
|
|
256
256
|
type: PropType<PxMouseOutAction>;
|
|
257
|
-
default: "
|
|
257
|
+
default: "pause";
|
|
258
258
|
};
|
|
259
259
|
visibilityThreshold: {
|
|
260
260
|
type: NumberConstructor;
|
package/dist/index.d.ts
CHANGED
|
@@ -209,8 +209,8 @@ declare const PixodeskSvgAnimator: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
209
209
|
* - `'continue'` — keeps playing, and starts without waiting to be seen
|
|
210
210
|
* - `'reset'` — back to the beginning, so it replays on the next entry
|
|
211
211
|
* @prop mouseOut - What happens when the pointer leaves, for `start: 'mouseOver'`:
|
|
212
|
-
* - `'
|
|
213
|
-
* - `'
|
|
212
|
+
* - `'pause'` — pauses at the current frame (default)
|
|
213
|
+
* - `'continue'` — keeps playing
|
|
214
214
|
* - `'reset'` — resets to the beginning
|
|
215
215
|
* - `'reverse'` — **acts as `'continue'` here.** A CSS class toggle cannot run keyframes
|
|
216
216
|
* backwards. Accepted so the prop keeps the shared `PxMouseOutAction` type.
|
|
@@ -231,7 +231,7 @@ declare const PixodeskSvgCssAnimator: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
231
231
|
};
|
|
232
232
|
mouseOut: {
|
|
233
233
|
type: PropType<PxMouseOutAction>;
|
|
234
|
-
default: "
|
|
234
|
+
default: "pause";
|
|
235
235
|
};
|
|
236
236
|
visibilityThreshold: {
|
|
237
237
|
type: NumberConstructor;
|
|
@@ -254,7 +254,7 @@ declare const PixodeskSvgCssAnimator: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
254
254
|
};
|
|
255
255
|
mouseOut: {
|
|
256
256
|
type: PropType<PxMouseOutAction>;
|
|
257
|
-
default: "
|
|
257
|
+
default: "pause";
|
|
258
258
|
};
|
|
259
259
|
visibilityThreshold: {
|
|
260
260
|
type: NumberConstructor;
|
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, 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"]}
|
|
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 * - `'pause'` — pauses at the current frame (default)\n * - `'continue'` — keeps playing\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"]}
|
package/dist/index.umd.js
CHANGED
|
@@ -56761,7 +56761,7 @@ ${codeFrame}` : message);
|
|
|
56761
56761
|
PixodeskSvgCssAnimator: () => PixodeskSvgCssAnimator_default
|
|
56762
56762
|
});
|
|
56763
56763
|
|
|
56764
|
-
// ../svg-animator-web/dist/chunk-
|
|
56764
|
+
// ../svg-animator-web/dist/chunk-ZEUVRAKS.js
|
|
56765
56765
|
var __defProp2 = Object.defineProperty;
|
|
56766
56766
|
var __defProps = Object.defineProperties;
|
|
56767
56767
|
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
@@ -57445,13 +57445,14 @@ ${codeFrame}` : message);
|
|
|
57445
57445
|
var PX_TRIGGER_DEFAULTS = {
|
|
57446
57446
|
start: "load",
|
|
57447
57447
|
offScreen: "pause",
|
|
57448
|
-
mouseOut: "
|
|
57448
|
+
mouseOut: "pause",
|
|
57449
57449
|
visibilityThreshold: 0.5,
|
|
57450
57450
|
visibilityDebounce: 150,
|
|
57451
57451
|
finish: "hold"
|
|
57452
57452
|
};
|
|
57453
57453
|
var PX_DEFAULT_DURATION_MS = 1e3;
|
|
57454
57454
|
var PX_DEFAULT_ITERATIONS = 1;
|
|
57455
|
+
var PX_DEFAULT_UNITS_PER_EM = 1e3;
|
|
57455
57456
|
function resolveTrigger(trigger) {
|
|
57456
57457
|
var _a22, _b, _c, _d, _e;
|
|
57457
57458
|
return {
|
|
@@ -57714,7 +57715,7 @@ ${codeFrame}` : message);
|
|
|
57714
57715
|
fontFamily: px.string(),
|
|
57715
57716
|
fontStyle: px.string(),
|
|
57716
57717
|
ascent: px.number(),
|
|
57717
|
-
unitsPerEm: px.number(),
|
|
57718
|
+
unitsPerEm: px.number(PX_DEFAULT_UNITS_PER_EM).optional(),
|
|
57718
57719
|
glyphs: px.record(PxGlyphSchema)
|
|
57719
57720
|
}));
|
|
57720
57721
|
var PxDefinitionsSchema = implementsInterface()(px.object({
|
|
@@ -60404,6 +60405,9 @@ ${codeFrame}` : message);
|
|
|
60404
60405
|
if (!gf) warnings == null ? void 0 : warnings.push('textGlyphs: no glyphs for font "' + ((_a22 = s.fontFamily) != null ? _a22 : "") + '"');
|
|
60405
60406
|
return gf;
|
|
60406
60407
|
}
|
|
60408
|
+
function unitsPerEmOf(gf) {
|
|
60409
|
+
return (gf == null ? void 0 : gf.unitsPerEm) || PX_DEFAULT_UNITS_PER_EM;
|
|
60410
|
+
}
|
|
60407
60411
|
function soleFontOf(glyphs) {
|
|
60408
60412
|
const names = Object.keys(glyphs);
|
|
60409
60413
|
return names.length === 1 ? glyphs[names[0]] : void 0;
|
|
@@ -60433,7 +60437,7 @@ ${codeFrame}` : message);
|
|
|
60433
60437
|
const renderChars = (content, s) => {
|
|
60434
60438
|
var _a3;
|
|
60435
60439
|
const gf = glyphFontFor(s, glyphs, soleFont, warnings);
|
|
60436
|
-
const upm = (gf
|
|
60440
|
+
const upm = unitsPerEmOf(gf);
|
|
60437
60441
|
const scale = s.fontSize / upm;
|
|
60438
60442
|
const ascentEm = (_a3 = gf == null ? void 0 : gf.ascent) != null ? _a3 : 0.9 * upm;
|
|
60439
60443
|
const paint = paintOf(s);
|
|
@@ -60495,8 +60499,9 @@ ${codeFrame}` : message);
|
|
|
60495
60499
|
if (content && !((_a22 = el.children) == null ? void 0 : _a22.length)) {
|
|
60496
60500
|
const gf = glyphFontFor(s, glyphs, soleFont, warnings);
|
|
60497
60501
|
if (gf) {
|
|
60498
|
-
const
|
|
60499
|
-
const
|
|
60502
|
+
const upm = unitsPerEmOf(gf);
|
|
60503
|
+
const scale = s.fontSize / upm;
|
|
60504
|
+
const ascentEm = (_b = gf.ascent) != null ? _b : 0.9 * upm;
|
|
60500
60505
|
const paint = paintOf(s);
|
|
60501
60506
|
for (let i = 0; i < content.length; i++) {
|
|
60502
60507
|
const ch = content.charAt(i);
|
|
@@ -60506,7 +60511,7 @@ ${codeFrame}` : message);
|
|
|
60506
60511
|
cells.push({ glyphD: g.pathData, widthEm: g.width, scale, paint, midBase: adv + glyphAdv / 2 });
|
|
60507
60512
|
adv += glyphAdv;
|
|
60508
60513
|
} else if (/\S/.test(ch)) {
|
|
60509
|
-
const wEm = g && g.width > 0 ? g.width : MISSING_GLYPH_ADVANCE_EM *
|
|
60514
|
+
const wEm = g && g.width > 0 ? g.width : MISSING_GLYPH_ADVANCE_EM * upm;
|
|
60510
60515
|
const boxAdv = wEm * scale;
|
|
60511
60516
|
cells.push({ glyphD: missingGlyphBoxEm(wEm, ascentEm), widthEm: wEm, scale, paint, isMissing: true, midBase: adv + boxAdv / 2 });
|
|
60512
60517
|
adv += boxAdv;
|
|
@@ -64401,7 +64406,7 @@ ${codeFrame}` : message);
|
|
|
64401
64406
|
return proxy;
|
|
64402
64407
|
}
|
|
64403
64408
|
|
|
64404
|
-
// ../svg-animator-core/dist/chunk-
|
|
64409
|
+
// ../svg-animator-core/dist/chunk-J5V5JJDT.js
|
|
64405
64410
|
var __defProp3 = Object.defineProperty;
|
|
64406
64411
|
var __defProps3 = Object.defineProperties;
|
|
64407
64412
|
var __getOwnPropDescs3 = Object.getOwnPropertyDescriptors;
|
|
@@ -65060,13 +65065,14 @@ ${codeFrame}` : message);
|
|
|
65060
65065
|
var PX_TRIGGER_DEFAULTS2 = {
|
|
65061
65066
|
start: "load",
|
|
65062
65067
|
offScreen: "pause",
|
|
65063
|
-
mouseOut: "
|
|
65068
|
+
mouseOut: "pause",
|
|
65064
65069
|
visibilityThreshold: 0.5,
|
|
65065
65070
|
visibilityDebounce: 150,
|
|
65066
65071
|
finish: "hold"
|
|
65067
65072
|
};
|
|
65068
65073
|
var PX_DEFAULT_DURATION_MS2 = 1e3;
|
|
65069
65074
|
var PX_DEFAULT_ITERATIONS2 = 1;
|
|
65075
|
+
var PX_DEFAULT_UNITS_PER_EM2 = 1e3;
|
|
65070
65076
|
var PxControlMode = {
|
|
65071
65077
|
/** No control props — the document's trigger decides, and nothing is taken over. */
|
|
65072
65078
|
static: "static",
|
|
@@ -65344,7 +65350,7 @@ ${codeFrame}` : message);
|
|
|
65344
65350
|
fontFamily: px2.string(),
|
|
65345
65351
|
fontStyle: px2.string(),
|
|
65346
65352
|
ascent: px2.number(),
|
|
65347
|
-
unitsPerEm: px2.number(),
|
|
65353
|
+
unitsPerEm: px2.number(PX_DEFAULT_UNITS_PER_EM2).optional(),
|
|
65348
65354
|
glyphs: px2.record(PxGlyphSchema2)
|
|
65349
65355
|
}));
|
|
65350
65356
|
var PxDefinitionsSchema2 = implementsInterface2()(px2.object({
|
|
@@ -67946,6 +67952,9 @@ ${codeFrame}` : message);
|
|
|
67946
67952
|
if (!gf) warnings == null ? void 0 : warnings.push('textGlyphs: no glyphs for font "' + ((_a22 = s.fontFamily) != null ? _a22 : "") + '"');
|
|
67947
67953
|
return gf;
|
|
67948
67954
|
}
|
|
67955
|
+
function unitsPerEmOf2(gf) {
|
|
67956
|
+
return (gf == null ? void 0 : gf.unitsPerEm) || PX_DEFAULT_UNITS_PER_EM2;
|
|
67957
|
+
}
|
|
67949
67958
|
function soleFontOf2(glyphs) {
|
|
67950
67959
|
const names = Object.keys(glyphs);
|
|
67951
67960
|
return names.length === 1 ? glyphs[names[0]] : void 0;
|
|
@@ -67975,7 +67984,7 @@ ${codeFrame}` : message);
|
|
|
67975
67984
|
const renderChars = (content, s) => {
|
|
67976
67985
|
var _a3;
|
|
67977
67986
|
const gf = glyphFontFor2(s, glyphs, soleFont, warnings);
|
|
67978
|
-
const upm = (gf
|
|
67987
|
+
const upm = unitsPerEmOf2(gf);
|
|
67979
67988
|
const scale = s.fontSize / upm;
|
|
67980
67989
|
const ascentEm = (_a3 = gf == null ? void 0 : gf.ascent) != null ? _a3 : 0.9 * upm;
|
|
67981
67990
|
const paint = paintOf2(s);
|
|
@@ -68037,8 +68046,9 @@ ${codeFrame}` : message);
|
|
|
68037
68046
|
if (content && !((_a22 = el.children) == null ? void 0 : _a22.length)) {
|
|
68038
68047
|
const gf = glyphFontFor2(s, glyphs, soleFont, warnings);
|
|
68039
68048
|
if (gf) {
|
|
68040
|
-
const
|
|
68041
|
-
const
|
|
68049
|
+
const upm = unitsPerEmOf2(gf);
|
|
68050
|
+
const scale = s.fontSize / upm;
|
|
68051
|
+
const ascentEm = (_b = gf.ascent) != null ? _b : 0.9 * upm;
|
|
68042
68052
|
const paint = paintOf2(s);
|
|
68043
68053
|
for (let i = 0; i < content.length; i++) {
|
|
68044
68054
|
const ch = content.charAt(i);
|
|
@@ -68048,7 +68058,7 @@ ${codeFrame}` : message);
|
|
|
68048
68058
|
cells.push({ glyphD: g.pathData, widthEm: g.width, scale, paint, midBase: adv + glyphAdv / 2 });
|
|
68049
68059
|
adv += glyphAdv;
|
|
68050
68060
|
} else if (/\S/.test(ch)) {
|
|
68051
|
-
const wEm = g && g.width > 0 ? g.width : MISSING_GLYPH_ADVANCE_EM2 *
|
|
68061
|
+
const wEm = g && g.width > 0 ? g.width : MISSING_GLYPH_ADVANCE_EM2 * upm;
|
|
68052
68062
|
const boxAdv = wEm * scale;
|
|
68053
68063
|
cells.push({ glyphD: missingGlyphBoxEm2(wEm, ascentEm), widthEm: wEm, scale, paint, isMissing: true, midBase: adv + boxAdv / 2 });
|
|
68054
68064
|
adv += boxAdv;
|