@vielzeug/necromancer 26.11.4 → 26.11.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/_elements.js +1 -2
- package/dist/_handle.cjs +1 -1
- package/dist/_handle.cjs.map +1 -1
- package/dist/_handle.d.ts +5 -0
- package/dist/_handle.d.ts.map +1 -1
- package/dist/_handle.js +2 -3
- package/dist/_handle.js.map +1 -1
- package/dist/_options.js +1 -2
- package/dist/animate-each.cjs +1 -1
- package/dist/animate-each.cjs.map +1 -1
- package/dist/animate-each.js +12 -13
- package/dist/animate.js +1 -2
- package/dist/errors.js +1 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -6
- package/dist/layout.cjs +1 -1
- package/dist/layout.cjs.map +1 -1
- package/dist/layout.js +21 -22
- package/dist/motion.js +1 -2
- package/dist/necromancer.cjs +1 -1
- package/dist/necromancer.cjs.map +1 -1
- package/dist/necromancer.iife.js +1 -1
- package/dist/necromancer.iife.js.map +1 -1
- package/dist/necromancer.js +67 -17
- package/dist/necromancer.js.map +1 -1
- package/dist/spring.cjs +2 -0
- package/dist/spring.cjs.map +1 -0
- package/dist/spring.d.ts +47 -0
- package/dist/spring.d.ts.map +1 -0
- package/dist/spring.js +64 -0
- package/dist/spring.js.map +1 -0
- package/dist/testing.js +1 -2
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -21,7 +21,10 @@ document.body.append(button);
|
|
|
21
21
|
|
|
22
22
|
const handle = animate(
|
|
23
23
|
button,
|
|
24
|
-
[
|
|
24
|
+
[
|
|
25
|
+
{ opacity: 0, transform: 'translateY(8px)' },
|
|
26
|
+
{ opacity: 1, transform: 'translateY(0)' },
|
|
27
|
+
],
|
|
25
28
|
{ duration: 180, easing: 'ease-out' },
|
|
26
29
|
);
|
|
27
30
|
|
package/dist/_elements.js
CHANGED
package/dist/_handle.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function attachAbortSignalListener(e,t){let abort=()=>t(e.reason);return e.addEventListener(`abort`,abort,{once:!0}),e.aborted?(e.removeEventListener(`abort`,abort),t(e.reason),()=>void 0):()=>e.removeEventListener(`abort`,abort)}function createResultPromise(e,t){return e.finished.then(()=>(t(),{status:`finished`}),e=>(t(),{reason:e,status:`cancelled`}))}function createAnimationHandle(e,t){let n=!1,r,i,a=createResultPromise(e,()=>{i?.(),i=void 0}).then(e=>n&&e.status===`cancelled`?r===void 0?{status:`cancelled`}:{reason:r,status:`cancelled`}:e),dispose=t=>{n||(n=!0,r=t,i?.(),i=void 0,e.cancel())};return t&&(i=attachAbortSignalListener(t,dispose)),{animation:e,dispose,get disposed(){return n},result:a,[Symbol.dispose](){this.dispose()}}}function createAnimationGroup(e,t){let n=!1,r,i=Promise.all(e.map(e=>e.result)).then(e=>(r?.(),r=void 0,e)),dispose=t=>{if(!n){n=!0,r?.(),r=void 0;for(let n of e)n.dispose(t)}};return t&&(r=attachAbortSignalListener(t,dispose)),{dispose,get disposed(){return n},handles:e,results:i,[Symbol.dispose](){this.dispose()}}}exports.createAnimationGroup=createAnimationGroup,exports.createAnimationHandle=createAnimationHandle;
|
|
1
|
+
function attachAbortSignalListener(e,t){let abort=()=>t(e.reason);return e.addEventListener(`abort`,abort,{once:!0}),e.aborted?(e.removeEventListener(`abort`,abort),t(e.reason),()=>void 0):()=>e.removeEventListener(`abort`,abort)}function createResultPromise(e,t){return e.finished.then(()=>(t(),{status:`finished`}),e=>(t(),{reason:e,status:`cancelled`}))}function createAnimationHandle(e,t){let n=!1,r,i,a=createResultPromise(e,()=>{i?.(),i=void 0}).then(e=>n&&e.status===`cancelled`?r===void 0?{status:`cancelled`}:{reason:r,status:`cancelled`}:e),dispose=t=>{n||(n=!0,r=t,i?.(),i=void 0,e.cancel())};return t&&(i=attachAbortSignalListener(t,dispose)),{animation:e,dispose,get disposed(){return n},result:a,[Symbol.dispose](){this.dispose()}}}function createAnimationGroup(e,t){let n=!1,r,i=Promise.all(e.map(e=>e.result)).then(e=>(r?.(),r=void 0,e)),dispose=t=>{if(!n){n=!0,r?.(),r=void 0;for(let n of e)n.dispose(t)}};return t&&(r=attachAbortSignalListener(t,dispose)),{dispose,get disposed(){return n},handles:e,results:i,[Symbol.dispose](){this.dispose()}}}exports.attachAbortSignalListener=attachAbortSignalListener,exports.createAnimationGroup=createAnimationGroup,exports.createAnimationHandle=createAnimationHandle;
|
|
2
2
|
//# sourceMappingURL=_handle.cjs.map
|
package/dist/_handle.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_handle.cjs","names":["dispose","signal","animation","onSettled","disposed","disposeReason","removeAbortListener","result","outcome","reason","results","handles","handle","childResults"],"sources":["../src/_handle.ts"],"sourcesContent":["import type { AnimationGroup, AnimationHandle, AnimationResult } from './types';\n\n/**\n * Attaches an abort listener to a signal and returns a function that removes it.\n * When the signal aborts, calls the dispose function with the signal's reason.\n */\
|
|
1
|
+
{"version":3,"file":"_handle.cjs","names":["dispose","signal","animation","onSettled","disposed","disposeReason","removeAbortListener","result","outcome","reason","results","handles","handle","childResults"],"sources":["../src/_handle.ts"],"sourcesContent":["import type { AnimationGroup, AnimationHandle, AnimationResult } from './types';\n\n/**\n * Attaches an abort listener to a signal and returns a function that removes it.\n * When the signal aborts, calls the dispose function with the signal's reason.\n */\nexport function attachAbortSignalListener(signal: AbortSignal, dispose: (reason?: unknown) => void): () => void {\n const abort = () => dispose(signal.reason);\n\n signal.addEventListener('abort', abort, { once: true });\n\n if (signal.aborted) {\n signal.removeEventListener('abort', abort);\n dispose(signal.reason);\n return () => undefined;\n }\n\n return () => signal.removeEventListener('abort', abort);\n}\n\nfunction createResultPromise(animation: Animation, onSettled: () => void): Promise<AnimationResult> {\n return animation.finished.then(\n () => {\n onSettled();\n\n return { status: 'finished' };\n },\n (reason: unknown) => {\n onSettled();\n\n return { reason, status: 'cancelled' };\n },\n );\n}\n\n/** Wraps a native Animation with Necromancer's lifecycle contract. */\nexport function createAnimationHandle(animation: Animation, signal?: AbortSignal): AnimationHandle {\n let disposed = false;\n let disposeReason: unknown;\n let removeAbortListener: (() => void) | undefined;\n\n const result: Promise<AnimationResult> = createResultPromise(animation, () => {\n removeAbortListener?.();\n removeAbortListener = undefined;\n }).then((outcome): AnimationResult => {\n // An owner-driven cancellation reports the owner's dispose reason (none for a\n // bare dispose()), never the native cancel's DOMException: only an external\n // cancellation of the native animation surfaces that reason here.\n if (disposed && outcome.status === 'cancelled') {\n return disposeReason === undefined ? { status: 'cancelled' } : { reason: disposeReason, status: 'cancelled' };\n }\n\n return outcome;\n });\n\n const dispose = (reason?: unknown): void => {\n if (disposed) return;\n\n disposed = true;\n disposeReason = reason;\n removeAbortListener?.();\n removeAbortListener = undefined;\n animation.cancel();\n };\n\n if (signal) {\n removeAbortListener = attachAbortSignalListener(signal, dispose);\n }\n\n return {\n animation,\n dispose,\n get disposed() {\n return disposed;\n },\n result,\n [Symbol.dispose]() {\n this.dispose();\n },\n };\n}\n\n/** Combines child animation handles under one disposal owner. */\nexport function createAnimationGroup(handles: readonly AnimationHandle[], signal?: AbortSignal): AnimationGroup {\n let disposed = false;\n let removeAbortListener: (() => void) | undefined;\n\n const results: Promise<readonly AnimationResult[]> = Promise.all(handles.map((handle) => handle.result)).then(\n (childResults): readonly AnimationResult[] => {\n removeAbortListener?.();\n removeAbortListener = undefined;\n\n return childResults;\n },\n );\n\n const dispose = (reason?: unknown): void => {\n if (disposed) return;\n\n disposed = true;\n removeAbortListener?.();\n removeAbortListener = undefined;\n for (const handle of handles) handle.dispose(reason);\n };\n\n if (signal) {\n removeAbortListener = attachAbortSignalListener(signal, dispose);\n }\n\n return {\n dispose,\n get disposed() {\n return disposed;\n },\n handles,\n results,\n [Symbol.dispose]() {\n this.dispose();\n },\n };\n}\n"],"mappings":"AAMA,SAAgB,0BAA0B,EAAqB,EAAiD,CAC9G,IAAM,UAAcA,EAAQC,EAAO,MAAM,EAUzC,OARAA,EAAO,iBAAiB,QAAS,MAAO,CAAE,KAAM,EAAK,CAAC,EAElDA,EAAO,SACTA,EAAO,oBAAoB,QAAS,KAAK,EACzCD,EAAQC,EAAO,MAAM,MACR,IAAA,QAGFA,EAAO,oBAAoB,QAAS,KAAK,CACxD,CAEA,SAAS,oBAAoB,EAAsB,EAAiD,CAClG,OAAOC,EAAU,SAAS,UAEtBC,EAAU,EAEH,CAAE,OAAQ,UAAW,GAE7B,IACCA,EAAU,EAEH,CAAE,SAAQ,OAAQ,WAAY,EAEzC,CACF,CAGA,SAAgB,sBAAsB,EAAsB,EAAuC,CACjG,IAAIC,EAAW,GACXC,EACAC,EAEEC,EAAmC,oBAAoBL,MAAiB,CAC5EI,IAAsB,EACtB,EAAsB,IAAA,EACxB,CAAC,CAAC,CAAC,KAAM,GAIHF,GAAYI,EAAQ,SAAW,YAC1BH,IAAkB,IAAA,GAAY,CAAE,OAAQ,WAAY,EAAI,CAAE,OAAQA,EAAe,OAAQ,WAAY,EAGvGG,CACR,EAEK,QAAW,GAA2B,CACtCJ,IAEJ,EAAW,GACX,EAAgBK,EAChBH,IAAsB,EACtB,EAAsB,IAAA,GACtBJ,EAAU,OAAO,EACnB,EAMA,OAJID,IACF,EAAsB,0BAA0BA,EAAQ,OAAO,GAG1D,CACL,YACA,QACA,IAAI,UAAW,CACb,OAAOG,CACT,EACA,SACA,CAAC,OAAO,UAAW,CACjB,KAAK,QAAQ,CACf,CACF,CACF,CAGA,SAAgB,qBAAqB,EAAqC,EAAsC,CAC9G,IAAIA,EAAW,GACXE,EAEEI,EAA+C,QAAQ,IAAIC,EAAQ,IAAK,GAAWC,EAAO,MAAM,CAAC,CAAC,CAAC,KACtG,IACCN,IAAsB,EACtB,EAAsB,IAAA,GAEfO,EAEX,EAEM,QAAW,GAA2B,CAC1C,GAAI,GAEJ,GAAW,GACXP,IAAsB,EACtB,EAAsB,IAAA,GACtB,IAAK,IAAMM,KAAUD,EAASC,EAAO,QAAQH,CAAM,CAHxC,CAIb,EAMA,OAJIR,IACF,EAAsB,0BAA0BA,EAAQ,OAAO,GAG1D,CACL,QACA,IAAI,UAAW,CACb,OAAOG,CACT,EACA,UACA,UACA,CAAC,OAAO,UAAW,CACjB,KAAK,QAAQ,CACf,CACF,CACF,C"}
|
package/dist/_handle.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { AnimationGroup, AnimationHandle } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Attaches an abort listener to a signal and returns a function that removes it.
|
|
4
|
+
* When the signal aborts, calls the dispose function with the signal's reason.
|
|
5
|
+
*/
|
|
6
|
+
export declare function attachAbortSignalListener(signal: AbortSignal, dispose: (reason?: unknown) => void): () => void;
|
|
2
7
|
/** Wraps a native Animation with Necromancer's lifecycle contract. */
|
|
3
8
|
export declare function createAnimationHandle(animation: Animation, signal?: AbortSignal): AnimationHandle;
|
|
4
9
|
/** Combines child animation handles under one disposal owner. */
|
package/dist/_handle.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_handle.d.ts","sourceRoot":"","sources":["../src/_handle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"_handle.d.ts","sourceRoot":"","sources":["../src/_handle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,MAAM,SAAS,CAAC;AAEhF;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAY9G;AAiBD,sEAAsE;AACtE,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,eAAe,CA4CjG;AAED,iEAAiE;AACjE,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,cAAc,CAqC9G"}
|
package/dist/_handle.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/_handle.ts
|
|
2
2
|
function attachAbortSignalListener(e, t) {
|
|
3
3
|
let abort = () => t(e.reason);
|
|
4
4
|
return e.addEventListener("abort", abort, { once: !0 }), e.aborted ? (e.removeEventListener("abort", abort), t(e.reason), () => void 0) : () => e.removeEventListener("abort", abort);
|
|
@@ -49,7 +49,6 @@ function createAnimationGroup(e, t) {
|
|
|
49
49
|
}
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
|
-
|
|
53
|
-
export { createAnimationGroup, createAnimationHandle };
|
|
52
|
+
export { attachAbortSignalListener, createAnimationGroup, createAnimationHandle };
|
|
54
53
|
|
|
55
54
|
//# sourceMappingURL=_handle.js.map
|
package/dist/_handle.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_handle.js","names":["dispose","signal","animation","onSettled","disposed","disposeReason","removeAbortListener","result","outcome","reason","results","handles","handle","childResults"],"sources":["../src/_handle.ts"],"sourcesContent":["import type { AnimationGroup, AnimationHandle, AnimationResult } from './types';\n\n/**\n * Attaches an abort listener to a signal and returns a function that removes it.\n * When the signal aborts, calls the dispose function with the signal's reason.\n */\
|
|
1
|
+
{"version":3,"file":"_handle.js","names":["dispose","signal","animation","onSettled","disposed","disposeReason","removeAbortListener","result","outcome","reason","results","handles","handle","childResults"],"sources":["../src/_handle.ts"],"sourcesContent":["import type { AnimationGroup, AnimationHandle, AnimationResult } from './types';\n\n/**\n * Attaches an abort listener to a signal and returns a function that removes it.\n * When the signal aborts, calls the dispose function with the signal's reason.\n */\nexport function attachAbortSignalListener(signal: AbortSignal, dispose: (reason?: unknown) => void): () => void {\n const abort = () => dispose(signal.reason);\n\n signal.addEventListener('abort', abort, { once: true });\n\n if (signal.aborted) {\n signal.removeEventListener('abort', abort);\n dispose(signal.reason);\n return () => undefined;\n }\n\n return () => signal.removeEventListener('abort', abort);\n}\n\nfunction createResultPromise(animation: Animation, onSettled: () => void): Promise<AnimationResult> {\n return animation.finished.then(\n () => {\n onSettled();\n\n return { status: 'finished' };\n },\n (reason: unknown) => {\n onSettled();\n\n return { reason, status: 'cancelled' };\n },\n );\n}\n\n/** Wraps a native Animation with Necromancer's lifecycle contract. */\nexport function createAnimationHandle(animation: Animation, signal?: AbortSignal): AnimationHandle {\n let disposed = false;\n let disposeReason: unknown;\n let removeAbortListener: (() => void) | undefined;\n\n const result: Promise<AnimationResult> = createResultPromise(animation, () => {\n removeAbortListener?.();\n removeAbortListener = undefined;\n }).then((outcome): AnimationResult => {\n // An owner-driven cancellation reports the owner's dispose reason (none for a\n // bare dispose()), never the native cancel's DOMException: only an external\n // cancellation of the native animation surfaces that reason here.\n if (disposed && outcome.status === 'cancelled') {\n return disposeReason === undefined ? { status: 'cancelled' } : { reason: disposeReason, status: 'cancelled' };\n }\n\n return outcome;\n });\n\n const dispose = (reason?: unknown): void => {\n if (disposed) return;\n\n disposed = true;\n disposeReason = reason;\n removeAbortListener?.();\n removeAbortListener = undefined;\n animation.cancel();\n };\n\n if (signal) {\n removeAbortListener = attachAbortSignalListener(signal, dispose);\n }\n\n return {\n animation,\n dispose,\n get disposed() {\n return disposed;\n },\n result,\n [Symbol.dispose]() {\n this.dispose();\n },\n };\n}\n\n/** Combines child animation handles under one disposal owner. */\nexport function createAnimationGroup(handles: readonly AnimationHandle[], signal?: AbortSignal): AnimationGroup {\n let disposed = false;\n let removeAbortListener: (() => void) | undefined;\n\n const results: Promise<readonly AnimationResult[]> = Promise.all(handles.map((handle) => handle.result)).then(\n (childResults): readonly AnimationResult[] => {\n removeAbortListener?.();\n removeAbortListener = undefined;\n\n return childResults;\n },\n );\n\n const dispose = (reason?: unknown): void => {\n if (disposed) return;\n\n disposed = true;\n removeAbortListener?.();\n removeAbortListener = undefined;\n for (const handle of handles) handle.dispose(reason);\n };\n\n if (signal) {\n removeAbortListener = attachAbortSignalListener(signal, dispose);\n }\n\n return {\n dispose,\n get disposed() {\n return disposed;\n },\n handles,\n results,\n [Symbol.dispose]() {\n this.dispose();\n },\n };\n}\n"],"mappings":";AAMA,SAAgB,0BAA0B,GAAqB,GAAiD;CAC9G,IAAM,cAAcA,EAAQC,EAAO,MAAM;CAUzC,OARAA,EAAO,iBAAiB,SAAS,OAAO,EAAE,MAAM,GAAK,CAAC,GAElDA,EAAO,WACTA,EAAO,oBAAoB,SAAS,KAAK,GACzCD,EAAQC,EAAO,MAAM,SACR,KAAA,WAGFA,EAAO,oBAAoB,SAAS,KAAK;AACxD;AAEA,SAAS,oBAAoB,GAAsB,GAAiD;CAClG,OAAOC,EAAU,SAAS,YAEtBC,EAAU,GAEH,EAAE,QAAQ,WAAW,KAE7B,OACCA,EAAU,GAEH;EAAE;EAAQ,QAAQ;CAAY,EAEzC;AACF;AAGA,SAAgB,sBAAsB,GAAsB,GAAuC;CACjG,IAAIC,IAAW,IACXC,GACAC,GAEEC,IAAmC,oBAAoBL,SAAiB;EAE5E,AADAI,IAAsB,GACtB,IAAsB,KAAA;CACxB,CAAC,CAAC,CAAC,MAAM,MAIHF,KAAYI,EAAQ,WAAW,cAC1BH,MAAkB,KAAA,IAAY,EAAE,QAAQ,YAAY,IAAI;EAAE,QAAQA;EAAe,QAAQ;CAAY,IAGvGG,CACR,GAEK,WAAW,MAA2B;EAC1C,AAAIJ,MAEJ,IAAW,IACX,IAAgBK,GAChBH,IAAsB,GACtB,IAAsB,KAAA,GACtBJ,EAAU,OAAO;CACnB;CAMA,OAJID,MACF,IAAsB,0BAA0BA,GAAQ,OAAO,IAG1D;EACL;EACA;EACA,IAAI,WAAW;GACb,OAAOG;EACT;EACA;EACA,CAAC,OAAO,WAAW;GACjB,KAAK,QAAQ;EACf;CACF;AACF;AAGA,SAAgB,qBAAqB,GAAqC,GAAsC;CAC9G,IAAIA,IAAW,IACXE,GAEEI,IAA+C,QAAQ,IAAIC,EAAQ,KAAK,MAAWC,EAAO,MAAM,CAAC,CAAC,CAAC,MACtG,OACCN,IAAsB,GACtB,IAAsB,KAAA,GAEfO,EAEX,GAEM,WAAW,MAA2B;EAC1C,IAAI,IAEJ;GAEA,AAFA,IAAW,IACXP,IAAsB,GACtB,IAAsB,KAAA;GACtB,KAAK,IAAMM,KAAUD,GAAS,EAAO,QAAQF,CAAM;EAHxC;CAIb;CAMA,OAJIR,MACF,IAAsB,0BAA0BA,GAAQ,OAAO,IAG1D;EACL;EACA,IAAI,WAAW;GACb,OAAOG;EACT;EACA;EACA;EACA,CAAC,OAAO,WAAW;GACjB,KAAK,QAAQ;EACf;CACF;AACF"}
|
package/dist/_options.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/_options.ts
|
|
2
2
|
function resolveAnimationOptions(e = {}, t = !1) {
|
|
3
3
|
let { motion: n, signal: r, ...i } = e;
|
|
4
4
|
return t ? {
|
|
@@ -17,7 +17,6 @@ function withStaggeredDelay(e, t) {
|
|
|
17
17
|
delay: n + t
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
|
-
//#endregion
|
|
21
20
|
export { resolveAnimationOptions, withStaggeredDelay };
|
|
22
21
|
|
|
23
22
|
//# sourceMappingURL=_options.js.map
|
package/dist/animate-each.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./
|
|
1
|
+
const e=require("./_elements.cjs"),t=require("./_handle.cjs"),n=require("./_options.cjs"),r=require("./animate.cjs"),i=require("./errors.cjs");function validateStagger(e,t){if(!Number.isFinite(e)||e<0)throw new i.NecromancerConfigError(`stagger must be a finite, non-negative number.`);if(e>0&&t!==void 0&&typeof t!=`number`)throw new i.NecromancerConfigError(`A non-zero stagger requires a numeric delay; received ${typeof t}.`)}function animateEach(i,a,o={}){let{signal:s,stagger:c=0,...l}=o;validateStagger(c,l.delay),s?.throwIfAborted();let u=e.uniqueElements(i),d=u.map((e,t)=>typeof a==`function`?a(e,t,u.length):a);s?.throwIfAborted();let f=[];try{for(let[e,t]of u.entries()){let i=d[e];f.push(r.animate(t,i,n.withStaggeredDelay(l,e*c)))}}catch(e){for(let t of f)t.dispose(e);throw e}return t.createAnimationGroup(f,s)}exports.animateEach=animateEach;
|
|
2
2
|
//# sourceMappingURL=animate-each.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"animate-each.cjs","names":["NecromancerConfigError","uniqueElements","animate","withStaggeredDelay","createAnimationGroup","
|
|
1
|
+
{"version":3,"file":"animate-each.cjs","names":["NecromancerConfigError","uniqueElements","animate","withStaggeredDelay","createAnimationGroup","require__elements","require__handle","require__options","require_animate","require_errors","stagger","delay","animationOptions","options","signal","unique","elements","frames","keyframes","element","index","handles","currentFrames","error","handle"],"sources":["../src/animate-each.ts"],"sourcesContent":["import { uniqueElements } from './_elements';\nimport { createAnimationGroup } from './_handle';\nimport { withStaggeredDelay } from './_options';\nimport { animate } from './animate';\nimport { NecromancerConfigError } from './errors';\nimport type { AnimateEachOptions, AnimationGroup, AnimationHandle, KeyframeFactory, Keyframes } from './types';\n\nfunction validateStagger(stagger: number, delay: number | string | null | undefined): void {\n if (!Number.isFinite(stagger) || stagger < 0) {\n throw new NecromancerConfigError('stagger must be a finite, non-negative number.');\n }\n\n // A non-zero stagger adds to the delay, so it needs a numeric one. Checking here,\n // before any animation starts, keeps a pure config error free of side effects.\n if (stagger > 0 && delay !== undefined && typeof delay !== 'number') {\n throw new NecromancerConfigError(`A non-zero stagger requires a numeric delay; received ${typeof delay}.`);\n }\n}\n\n/**\n * Starts lifecycle-owned animations for unique elements in iteration order.\n *\n * Each subsequent element starts after `stagger` milliseconds. A keyframe factory\n * receives the stable unique-element index and total before any animation starts.\n */\nexport function animateEach<ElementType extends Element>(\n elements: Iterable<ElementType>,\n keyframes: Keyframes | KeyframeFactory<ElementType>,\n options: AnimateEachOptions = {},\n): AnimationGroup {\n const { signal, stagger = 0, ...animationOptions } = options;\n\n validateStagger(stagger, animationOptions.delay);\n\n signal?.throwIfAborted();\n\n const unique = uniqueElements(elements);\n const frames = unique.map((element, index) =>\n typeof keyframes === 'function' ? keyframes(element, index, unique.length) : keyframes,\n );\n\n signal?.throwIfAborted();\n\n const handles: AnimationHandle[] = [];\n\n try {\n for (const [index, element] of unique.entries()) {\n // `frames` was built above from `unique`, one entry per element, so this index is always in range.\n const currentFrames = frames[index]!;\n\n handles.push(animate(element, currentFrames, withStaggeredDelay(animationOptions, index * stagger)));\n }\n } catch (error) {\n for (const handle of handles) handle.dispose(error);\n\n throw error;\n }\n\n return createAnimationGroup(handles, signal);\n}\n"],"mappings":"A,+IAOA,SAAS,gBAAgB,EAAiB,EAAiD,CACzF,GAAI,CAAC,OAAO,SAASU,CAAO,GAAKA,EAAU,EACzC,MAAM,IAAIV,EAAAA,uBAAuB,gDAAgD,EAKnF,GAAIU,EAAU,GAAKC,IAAU,IAAA,IAAa,OAAOA,GAAU,SACzD,MAAM,IAAIX,EAAAA,uBAAuB,yDAAyD,OAAOW,EAAM,EAAE,CAE7G,CAQA,SAAgB,YACd,EACA,EACA,EAA8B,CAAC,EACf,CAChB,GAAM,CAAE,SAAQ,UAAU,EAAG,GAAGC,GAAqBC,EAErD,gBAAgBH,EAASE,EAAiB,KAAK,EAE/CE,GAAQ,eAAe,EAEvB,IAAMC,EAASd,EAAAA,eAAee,CAAQ,EAChCC,EAASF,EAAO,KAAK,EAAS,IAClC,OAAOG,GAAc,WAAaA,EAAUC,EAASC,EAAOL,EAAO,MAAM,EAAIG,CAC/E,EAEAJ,GAAQ,eAAe,EAEvB,IAAMO,EAA6B,CAAC,EAEpC,GAAI,CACF,IAAK,GAAM,CAACD,EAAOD,KAAYJ,EAAO,QAAQ,EAAG,CAE/C,IAAMO,EAAgBL,EAAOG,GAE7BC,EAAQ,KAAKnB,EAAAA,QAAQiB,EAASG,EAAenB,EAAAA,mBAAmBS,EAAkBQ,EAAQV,CAAO,CAAC,CAAC,CACrG,CACF,OAASa,EAAO,CACd,IAAK,IAAMC,KAAUH,EAASG,EAAO,QAAQD,CAAK,EAElD,MAAMA,CACR,CAEA,OAAOnB,EAAAA,qBAAqBiB,EAASP,CAAM,CAC7C,C"}
|
package/dist/animate-each.js
CHANGED
|
@@ -1,31 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { uniqueElements as e } from "./_elements.js";
|
|
2
|
+
import { createAnimationGroup as t } from "./_handle.js";
|
|
3
|
+
import { withStaggeredDelay as n } from "./_options.js";
|
|
4
4
|
import { animate as r } from "./animate.js";
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import { NecromancerConfigError as i } from "./errors.js";
|
|
6
|
+
// src/animate-each.ts
|
|
7
7
|
function validateStagger(e, t) {
|
|
8
|
-
if (!Number.isFinite(e) || e < 0) throw new
|
|
9
|
-
if (e > 0 && t !== void 0 && typeof t != "number") throw new
|
|
8
|
+
if (!Number.isFinite(e) || e < 0) throw new i("stagger must be a finite, non-negative number.");
|
|
9
|
+
if (e > 0 && t !== void 0 && typeof t != "number") throw new i(`A non-zero stagger requires a numeric delay; received ${typeof t}.`);
|
|
10
10
|
}
|
|
11
|
-
function animateEach(
|
|
11
|
+
function animateEach(i, a, o = {}) {
|
|
12
12
|
let { signal: s, stagger: c = 0, ...l } = o;
|
|
13
13
|
validateStagger(c, l.delay), s?.throwIfAborted();
|
|
14
|
-
let u = i
|
|
14
|
+
let u = e(i), d = u.map((e, t) => typeof a == "function" ? a(e, t, u.length) : a);
|
|
15
15
|
s?.throwIfAborted();
|
|
16
16
|
let f = [];
|
|
17
17
|
try {
|
|
18
|
-
for (let [e,
|
|
18
|
+
for (let [e, t] of u.entries()) {
|
|
19
19
|
let i = d[e];
|
|
20
|
-
f.push(r(
|
|
20
|
+
f.push(r(t, i, n(l, e * c)));
|
|
21
21
|
}
|
|
22
22
|
} catch (e) {
|
|
23
23
|
for (let t of f) t.dispose(e);
|
|
24
24
|
throw e;
|
|
25
25
|
}
|
|
26
|
-
return
|
|
26
|
+
return t(f, s);
|
|
27
27
|
}
|
|
28
|
-
//#endregion
|
|
29
28
|
export { animateEach };
|
|
30
29
|
|
|
31
30
|
//# sourceMappingURL=animate-each.js.map
|
package/dist/animate.js
CHANGED
|
@@ -2,13 +2,12 @@ import { createAnimationHandle as e } from "./_handle.js";
|
|
|
2
2
|
import { resolveAnimationOptions as t } from "./_options.js";
|
|
3
3
|
import { NecromancerUnsupportedError as n } from "./errors.js";
|
|
4
4
|
import { shouldReduceMotion as r } from "./motion.js";
|
|
5
|
-
|
|
5
|
+
// src/animate.ts
|
|
6
6
|
function animate(i, a, o = {}) {
|
|
7
7
|
if (o.signal?.throwIfAborted(), typeof i.animate != "function") throw new n("The Web Animations API is not available in this environment.");
|
|
8
8
|
let s = i.animate(a, t(o, r(o.motion ?? "system")));
|
|
9
9
|
return e(s, o.signal);
|
|
10
10
|
}
|
|
11
|
-
//#endregion
|
|
12
11
|
export { animate };
|
|
13
12
|
|
|
14
13
|
//# sourceMappingURL=animate.js.map
|
package/dist/errors.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
// src/errors.ts
|
|
2
2
|
var NecromancerError = class extends Error {
|
|
3
3
|
constructor(e, t) {
|
|
4
4
|
super(e, t), this.name = new.target.name, Object.setPrototypeOf(this, new.target.prototype);
|
|
5
5
|
}
|
|
6
6
|
}, NecromancerConfigError = class extends NecromancerError {}, NecromancerUnsupportedError = class extends NecromancerError {};
|
|
7
|
-
//#endregion
|
|
8
7
|
export { NecromancerConfigError, NecromancerError, NecromancerUnsupportedError };
|
|
9
8
|
|
|
10
9
|
//# sourceMappingURL=errors.js.map
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./animate.cjs"),t=require("./animate-each.cjs"),n=require("./errors.cjs"),r=require("./layout.cjs"),i=require("./motion.cjs"),a=require("./spring.cjs");exports.NecromancerConfigError=n.NecromancerConfigError,exports.NecromancerError=n.NecromancerError,exports.NecromancerUnsupportedError=n.NecromancerUnsupportedError,exports.animate=e.animate,exports.animateEach=t.animateEach,exports.captureLayout=r.captureLayout,exports.shouldReduceMotion=i.shouldReduceMotion,exports.spring=a.spring;
|
package/dist/index.d.ts
CHANGED
|
@@ -3,5 +3,6 @@ export { animateEach } from './animate-each';
|
|
|
3
3
|
export { NecromancerConfigError, NecromancerError, NecromancerUnsupportedError } from './errors';
|
|
4
4
|
export { captureLayout } from './layout';
|
|
5
5
|
export { shouldReduceMotion } from './motion';
|
|
6
|
+
export { type SpringHandle, type SpringOptions, type SpringUpdate, spring } from './spring';
|
|
6
7
|
export type { AnimateEachOptions, AnimateOptions, AnimationGroup, AnimationHandle, AnimationResult, KeyframeFactory, Keyframes, LayoutAnimationOptions, LayoutCaptureOptions, LayoutTransition, MotionMode, } from './types';
|
|
7
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AACjG,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,YAAY,EACV,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,eAAe,EACf,eAAe,EACf,eAAe,EACf,SAAS,EACT,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,GACX,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AACjG,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAC5F,YAAY,EACV,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,eAAe,EACf,eAAe,EACf,eAAe,EACf,SAAS,EACT,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,GACX,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
1
|
+
import { animate as e } from "./animate.js";
|
|
2
|
+
import { animateEach as t } from "./animate-each.js";
|
|
3
|
+
import { NecromancerConfigError as n, NecromancerError as r, NecromancerUnsupportedError as i } from "./errors.js";
|
|
4
|
+
import { captureLayout as a } from "./layout.js";
|
|
5
|
+
import { shouldReduceMotion as o } from "./motion.js";
|
|
6
|
+
import { spring as s } from "./spring.js";
|
|
7
|
+
export { n as NecromancerConfigError, r as NecromancerError, i as NecromancerUnsupportedError, e as animate, t as animateEach, a as captureLayout, o as shouldReduceMotion, s as spring };
|
package/dist/layout.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./
|
|
1
|
+
const e=require("./_elements.cjs"),t=require("./animate-each.cjs"),n=require("./errors.cjs");function round(e,t){let n=10**t;return Math.round(e*n)/n}function measureSize(e){if(`offsetWidth`in e&&`offsetHeight`in e)return{height:e.offsetHeight,width:e.offsetWidth};let t=e.getBoundingClientRect();return{height:t.height,width:t.width}}function measureLayout(e){let t=e.getBoundingClientRect(),n=measureSize(e);return{height:n.height,width:n.width,x:t.x,y:t.y}}function computeScale(e,t){return t===0?1:e/t}function readKey(e,t,r){let i=t(e);if(typeof i!=`string`||i.length===0)throw new n.NecromancerConfigError(`getKey must return a non-empty string for every ${r} element.`);return i}function createKeyMap(e,t,r){let i=new Map;for(let a of e){let e=readKey(a.element,t,r);if(i.has(e))throw new n.NecromancerConfigError(`getKey returned duplicate keys for the ${r} elements.`);i.set(e,a)}return i}function changedElement(e,t){if(!e.isConnected)return;let n=measureLayout(e),r=t.x-n.x,i=t.y-n.y,a=computeScale(t.width,n.width),o=computeScale(t.height,n.height);return r===0&&i===0&&a===1&&o===1?void 0:{element:e,scaleX:a,scaleY:o,x:r,y:i}}function matchByIdentity(e,t){let n=new Map(e.map(e=>[e.element,e]));return t.flatMap(e=>{let t=n.get(e);return t?[changedElement(e,t.rect)].filter(e=>!!e):[]})}function matchByKey(e,t,r){let i=new Set;return t.flatMap(t=>{let a=readKey(t,r,`committed`),o=e.get(a);if(i.has(a))throw new n.NecromancerConfigError(`getKey returned duplicate keys for the committed elements.`);return i.add(a),o?[changedElement(t,o.rect)].filter(e=>!!e):[]})}function captureLayout(r,i={}){let a=e.uniqueElements(r).map(e=>({element:e,rect:measureLayout(e)})),o=i.getKey,s=o?createKeyMap(a,o,`captured`):void 0,c=!1;return{animate(r={}){if(c)throw new n.NecromancerConfigError(`This layout transition has already been animated.`);let{elements:i,...l}=r,u=e.uniqueElements(i??a.map(({element:e})=>e)),d=o&&s?matchByKey(s,u,o):matchByIdentity(a,u);return c=!0,t.animateEach(d.map(({element:e})=>e),(e,t)=>{let n=d[t],r=`${round(n.x,2)}px ${round(n.y,2)}px`;return[{composite:`add`,scale:`${round(n.scaleX,4)} ${round(n.scaleY,4)}`,translate:r},{composite:`add`,scale:`1 1`,translate:`0px 0px`}]},l)}}}exports.captureLayout=captureLayout;
|
|
2
2
|
//# sourceMappingURL=layout.cjs.map
|
package/dist/layout.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.cjs","names":["NecromancerConfigError","uniqueElements","animateEach","require_errors","require__elements","require_animate_each","factor","precision","value","element","rect","size","next","captured","key","getKey","phase","keys","entry","entries","x","y","scaleX","scaleY","elements","change","committed","options","consumed","committedElements","animationOptions","changed","index","translate"],"sources":["../src/layout.ts"],"sourcesContent":["import { uniqueElements } from './_elements';\nimport { animateEach } from './animate-each';\nimport { NecromancerConfigError } from './errors';\nimport type { LayoutAnimationOptions, LayoutCaptureOptions, LayoutTransition } from './types';\n\ntype LayoutRect = Readonly<Pick<DOMRectReadOnly, 'height' | 'width' | 'x' | 'y'>>;\ntype LayoutEntry<ElementType extends Element> = Readonly<{ element: ElementType; rect: LayoutRect }>;\ntype LayoutChange<ElementType extends Element> = Readonly<{\n element: ElementType;\n scaleX: number;\n scaleY: number;\n x: number;\n y: number;\n}>;\n\n// Rounds before templating into a keyframe string purely for DevTools readability: a `getBoundingClientRect()`\n// subtraction routinely produces values like `12.340000000000002`, which is noise once rendered as CSS.\n// Pixels only need whole-number precision; scale factors (close to 1) need finer precision to stay visually exact.\nfunction round(value: number, precision: number): number {\n const factor = 10 ** precision;\n\n return Math.round(value * factor) / factor;\n}\n\n// `getBoundingClientRect()`'s width/height is the rotated, axis-aligned bounding box: using it\n// for size would distort the scale ratio under any rotation. `offsetWidth`/`offsetHeight` report\n// the box's own untransformed layout size instead. Elements without an offset box (e.g. SVG)\n// fall back to the bounding rect, accepting that same distortion as the only option available.\nfunction measureSize(element: Element): Readonly<{ height: number; width: number }> {\n if ('offsetWidth' in element && 'offsetHeight' in element) {\n return { height: (element as HTMLElement).offsetHeight, width: (element as HTMLElement).offsetWidth };\n }\n\n const rect = element.getBoundingClientRect();\n\n return { height: rect.height, width: rect.width };\n}\n\nfunction measureLayout(element: Element): LayoutRect {\n const rect = element.getBoundingClientRect();\n const size = measureSize(element);\n\n return { height: size.height, width: size.width, x: rect.x, y: rect.y };\n}\n\n// `next` is 0 exactly when the element has collapsed (e.g. `display: none` mid-transition);\n// there's no meaningful ratio to compute then, so treat it as unscaled rather than producing Infinity/NaN.\nfunction computeScale(captured: number, next: number): number {\n return next === 0 ? 1 : captured / next;\n}\n\nfunction readKey<ElementType extends Element>(\n element: ElementType,\n getKey: (element: ElementType) => string,\n phase: string,\n): string {\n const key = getKey(element);\n\n if (typeof key !== 'string' || key.length === 0) {\n throw new NecromancerConfigError(`getKey must return a non-empty string for every ${phase} element.`);\n }\n\n return key;\n}\n\nfunction createKeyMap<ElementType extends Element>(\n entries: readonly LayoutEntry<ElementType>[],\n getKey: (element: ElementType) => string,\n phase: string,\n) {\n const keys = new Map<string, LayoutEntry<ElementType>>();\n\n for (const entry of entries) {\n const key = readKey(entry.element, getKey, phase);\n\n if (keys.has(key)) {\n throw new NecromancerConfigError(`getKey returned duplicate keys for the ${phase} elements.`);\n }\n\n keys.set(key, entry);\n }\n\n return keys;\n}\n\nfunction changedElement<ElementType extends Element>(\n element: ElementType,\n rect: LayoutRect,\n): LayoutChange<ElementType> | undefined {\n if (!element.isConnected) return undefined;\n\n const next = measureLayout(element);\n const x = rect.x - next.x;\n const y = rect.y - next.y;\n const scaleX = computeScale(rect.width, next.width);\n const scaleY = computeScale(rect.height, next.height);\n\n return x === 0 && y === 0 && scaleX === 1 && scaleY === 1 ? undefined : { element, scaleX, scaleY, x, y };\n}\n\nfunction matchByIdentity<ElementType extends Element>(\n entries: readonly LayoutEntry<ElementType>[],\n elements: readonly ElementType[],\n): LayoutChange<ElementType>[] {\n const captured = new Map(entries.map((entry) => [entry.element, entry]));\n\n return elements.flatMap((element) => {\n const entry = captured.get(element);\n\n return entry\n ? [changedElement(element, entry.rect)].filter((change): change is LayoutChange<ElementType> => Boolean(change))\n : [];\n });\n}\n\nfunction matchByKey<ElementType extends Element>(\n captured: ReadonlyMap<string, LayoutEntry<ElementType>>,\n elements: readonly ElementType[],\n getKey: (element: ElementType) => string,\n): LayoutChange<ElementType>[] {\n const committed = new Set<string>();\n\n return elements.flatMap((element) => {\n const key = readKey(element, getKey, 'committed');\n const entry = captured.get(key);\n\n if (committed.has(key)) {\n throw new NecromancerConfigError('getKey returned duplicate keys for the committed elements.');\n }\n\n committed.add(key);\n\n return entry\n ? [changedElement(element, entry.rect)].filter((change): change is LayoutChange<ElementType> => Boolean(change))\n : [];\n });\n}\n\n/**\n * Captures the current position and size of unique elements for a later FLIP animation.\n *\n * Each element's `x`/`y` position and `width`/`height` are captured. Rotation and other\n * transforms are not captured or compensated. Capture before mutating the DOM, then call\n * the returned transition's `animate()` method after the browser has applied the resulting\n * layout: position changes animate via translate, and size changes via scale, both\n * additively composed on top of any authored transform.\n */\nexport function captureLayout<ElementType extends Element>(\n elements: Iterable<ElementType>,\n options: LayoutCaptureOptions<ElementType> = {},\n): LayoutTransition<ElementType> {\n const entries: LayoutEntry<ElementType>[] = uniqueElements(elements).map((element) => ({\n element,\n rect: measureLayout(element),\n }));\n const getKey = options.getKey;\n const captured = getKey ? createKeyMap(entries, getKey, 'captured') : undefined;\n\n let consumed = false;\n\n return {\n animate(options: LayoutAnimationOptions<ElementType> = {}) {\n if (consumed) throw new NecromancerConfigError('This layout transition has already been animated.');\n\n const { elements: committedElements, ...animationOptions } = options;\n const elements = uniqueElements(committedElements ?? entries.map(({ element }) => element));\n const changed = getKey && captured ? matchByKey(captured, elements, getKey) : matchByIdentity(entries, elements);\n\n consumed = true;\n\n return animateEach(\n changed.map(({ element }) => element),\n (_, index) => {\n // `changed` was mapped directly into the elements array above, so this index is always in range.\n const change = changed[index]!;\n const translate = `${round(change.x, 2)}px ${round(change.y, 2)}px`;\n const scale = `${round(change.scaleX, 4)} ${round(change.scaleY, 4)}`;\n\n return [\n { composite: 'add', scale, translate },\n { composite: 'add', scale: '1 1', translate: '0px 0px' },\n ];\n },\n animationOptions,\n );\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"layout.cjs","names":["NecromancerConfigError","uniqueElements","animateEach","require__elements","require_animate_each","require_errors","factor","precision","value","element","rect","size","next","captured","key","getKey","phase","keys","entry","entries","x","y","scaleX","scaleY","elements","change","committed","options","consumed","committedElements","animationOptions","changed","index","translate"],"sources":["../src/layout.ts"],"sourcesContent":["import { uniqueElements } from './_elements';\nimport { animateEach } from './animate-each';\nimport { NecromancerConfigError } from './errors';\nimport type { LayoutAnimationOptions, LayoutCaptureOptions, LayoutTransition } from './types';\n\ntype LayoutRect = Readonly<Pick<DOMRectReadOnly, 'height' | 'width' | 'x' | 'y'>>;\ntype LayoutEntry<ElementType extends Element> = Readonly<{ element: ElementType; rect: LayoutRect }>;\ntype LayoutChange<ElementType extends Element> = Readonly<{\n element: ElementType;\n scaleX: number;\n scaleY: number;\n x: number;\n y: number;\n}>;\n\n// Rounds before templating into a keyframe string purely for DevTools readability: a `getBoundingClientRect()`\n// subtraction routinely produces values like `12.340000000000002`, which is noise once rendered as CSS.\n// Pixels only need whole-number precision; scale factors (close to 1) need finer precision to stay visually exact.\nfunction round(value: number, precision: number): number {\n const factor = 10 ** precision;\n\n return Math.round(value * factor) / factor;\n}\n\n// `getBoundingClientRect()`'s width/height is the rotated, axis-aligned bounding box: using it\n// for size would distort the scale ratio under any rotation. `offsetWidth`/`offsetHeight` report\n// the box's own untransformed layout size instead. Elements without an offset box (e.g. SVG)\n// fall back to the bounding rect, accepting that same distortion as the only option available.\nfunction measureSize(element: Element): Readonly<{ height: number; width: number }> {\n if ('offsetWidth' in element && 'offsetHeight' in element) {\n return { height: (element as HTMLElement).offsetHeight, width: (element as HTMLElement).offsetWidth };\n }\n\n const rect = element.getBoundingClientRect();\n\n return { height: rect.height, width: rect.width };\n}\n\nfunction measureLayout(element: Element): LayoutRect {\n const rect = element.getBoundingClientRect();\n const size = measureSize(element);\n\n return { height: size.height, width: size.width, x: rect.x, y: rect.y };\n}\n\n// `next` is 0 exactly when the element has collapsed (e.g. `display: none` mid-transition);\n// there's no meaningful ratio to compute then, so treat it as unscaled rather than producing Infinity/NaN.\nfunction computeScale(captured: number, next: number): number {\n return next === 0 ? 1 : captured / next;\n}\n\nfunction readKey<ElementType extends Element>(\n element: ElementType,\n getKey: (element: ElementType) => string,\n phase: string,\n): string {\n const key = getKey(element);\n\n if (typeof key !== 'string' || key.length === 0) {\n throw new NecromancerConfigError(`getKey must return a non-empty string for every ${phase} element.`);\n }\n\n return key;\n}\n\nfunction createKeyMap<ElementType extends Element>(\n entries: readonly LayoutEntry<ElementType>[],\n getKey: (element: ElementType) => string,\n phase: string,\n) {\n const keys = new Map<string, LayoutEntry<ElementType>>();\n\n for (const entry of entries) {\n const key = readKey(entry.element, getKey, phase);\n\n if (keys.has(key)) {\n throw new NecromancerConfigError(`getKey returned duplicate keys for the ${phase} elements.`);\n }\n\n keys.set(key, entry);\n }\n\n return keys;\n}\n\nfunction changedElement<ElementType extends Element>(\n element: ElementType,\n rect: LayoutRect,\n): LayoutChange<ElementType> | undefined {\n if (!element.isConnected) return undefined;\n\n const next = measureLayout(element);\n const x = rect.x - next.x;\n const y = rect.y - next.y;\n const scaleX = computeScale(rect.width, next.width);\n const scaleY = computeScale(rect.height, next.height);\n\n return x === 0 && y === 0 && scaleX === 1 && scaleY === 1 ? undefined : { element, scaleX, scaleY, x, y };\n}\n\nfunction matchByIdentity<ElementType extends Element>(\n entries: readonly LayoutEntry<ElementType>[],\n elements: readonly ElementType[],\n): LayoutChange<ElementType>[] {\n const captured = new Map(entries.map((entry) => [entry.element, entry]));\n\n return elements.flatMap((element) => {\n const entry = captured.get(element);\n\n return entry\n ? [changedElement(element, entry.rect)].filter((change): change is LayoutChange<ElementType> => Boolean(change))\n : [];\n });\n}\n\nfunction matchByKey<ElementType extends Element>(\n captured: ReadonlyMap<string, LayoutEntry<ElementType>>,\n elements: readonly ElementType[],\n getKey: (element: ElementType) => string,\n): LayoutChange<ElementType>[] {\n const committed = new Set<string>();\n\n return elements.flatMap((element) => {\n const key = readKey(element, getKey, 'committed');\n const entry = captured.get(key);\n\n if (committed.has(key)) {\n throw new NecromancerConfigError('getKey returned duplicate keys for the committed elements.');\n }\n\n committed.add(key);\n\n return entry\n ? [changedElement(element, entry.rect)].filter((change): change is LayoutChange<ElementType> => Boolean(change))\n : [];\n });\n}\n\n/**\n * Captures the current position and size of unique elements for a later FLIP animation.\n *\n * Each element's `x`/`y` position and `width`/`height` are captured. Rotation and other\n * transforms are not captured or compensated. Capture before mutating the DOM, then call\n * the returned transition's `animate()` method after the browser has applied the resulting\n * layout: position changes animate via translate, and size changes via scale, both\n * additively composed on top of any authored transform.\n */\nexport function captureLayout<ElementType extends Element>(\n elements: Iterable<ElementType>,\n options: LayoutCaptureOptions<ElementType> = {},\n): LayoutTransition<ElementType> {\n const entries: LayoutEntry<ElementType>[] = uniqueElements(elements).map((element) => ({\n element,\n rect: measureLayout(element),\n }));\n const getKey = options.getKey;\n const captured = getKey ? createKeyMap(entries, getKey, 'captured') : undefined;\n\n let consumed = false;\n\n return {\n animate(options: LayoutAnimationOptions<ElementType> = {}) {\n if (consumed) throw new NecromancerConfigError('This layout transition has already been animated.');\n\n const { elements: committedElements, ...animationOptions } = options;\n const elements = uniqueElements(committedElements ?? entries.map(({ element }) => element));\n const changed = getKey && captured ? matchByKey(captured, elements, getKey) : matchByIdentity(entries, elements);\n\n consumed = true;\n\n return animateEach(\n changed.map(({ element }) => element),\n (_, index) => {\n // `changed` was mapped directly into the elements array above, so this index is always in range.\n const change = changed[index]!;\n const translate = `${round(change.x, 2)}px ${round(change.y, 2)}px`;\n const scale = `${round(change.scaleX, 4)} ${round(change.scaleY, 4)}`;\n\n return [\n { composite: 'add', scale, translate },\n { composite: 'add', scale: '1 1', translate: '0px 0px' },\n ];\n },\n animationOptions,\n );\n },\n };\n}\n"],"mappings":"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"}
|
package/dist/layout.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
1
|
+
import { uniqueElements as e } from "./_elements.js";
|
|
2
|
+
import { animateEach as t } from "./animate-each.js";
|
|
3
|
+
import { NecromancerConfigError as n } from "./errors.js";
|
|
4
|
+
// src/layout.ts
|
|
5
5
|
function round(e, t) {
|
|
6
6
|
let n = 10 ** t;
|
|
7
7
|
return Math.round(e * n) / n;
|
|
@@ -29,17 +29,17 @@ function measureLayout(e) {
|
|
|
29
29
|
function computeScale(e, t) {
|
|
30
30
|
return t === 0 ? 1 : e / t;
|
|
31
31
|
}
|
|
32
|
-
function readKey(
|
|
33
|
-
let i =
|
|
34
|
-
if (typeof i != "string" || i.length === 0) throw new
|
|
32
|
+
function readKey(e, t, r) {
|
|
33
|
+
let i = t(e);
|
|
34
|
+
if (typeof i != "string" || i.length === 0) throw new n(`getKey must return a non-empty string for every ${r} element.`);
|
|
35
35
|
return i;
|
|
36
36
|
}
|
|
37
|
-
function createKeyMap(
|
|
37
|
+
function createKeyMap(e, t, r) {
|
|
38
38
|
let i = /* @__PURE__ */ new Map();
|
|
39
|
-
for (let a of
|
|
40
|
-
let
|
|
41
|
-
if (i.has(
|
|
42
|
-
i.set(
|
|
39
|
+
for (let a of e) {
|
|
40
|
+
let e = readKey(a.element, t, r);
|
|
41
|
+
if (i.has(e)) throw new n(`getKey returned duplicate keys for the ${r} elements.`);
|
|
42
|
+
i.set(e, a);
|
|
43
43
|
}
|
|
44
44
|
return i;
|
|
45
45
|
}
|
|
@@ -61,23 +61,23 @@ function matchByIdentity(e, t) {
|
|
|
61
61
|
return t ? [changedElement(e, t.rect)].filter((e) => !!e) : [];
|
|
62
62
|
});
|
|
63
63
|
}
|
|
64
|
-
function matchByKey(
|
|
64
|
+
function matchByKey(e, t, r) {
|
|
65
65
|
let i = /* @__PURE__ */ new Set();
|
|
66
|
-
return
|
|
67
|
-
let a = readKey(
|
|
68
|
-
if (i.has(a)) throw new
|
|
69
|
-
return i.add(a), o ? [changedElement(
|
|
66
|
+
return t.flatMap((t) => {
|
|
67
|
+
let a = readKey(t, r, "committed"), o = e.get(a);
|
|
68
|
+
if (i.has(a)) throw new n("getKey returned duplicate keys for the committed elements.");
|
|
69
|
+
return i.add(a), o ? [changedElement(t, o.rect)].filter((e) => !!e) : [];
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
72
|
function captureLayout(r, i = {}) {
|
|
73
|
-
let a =
|
|
73
|
+
let a = e(r).map((e) => ({
|
|
74
74
|
element: e,
|
|
75
75
|
rect: measureLayout(e)
|
|
76
76
|
})), o = i.getKey, s = o ? createKeyMap(a, o, "captured") : void 0, c = !1;
|
|
77
77
|
return { animate(r = {}) {
|
|
78
|
-
if (c) throw new
|
|
79
|
-
let { elements: i, ...l } = r, u =
|
|
80
|
-
return c = !0,
|
|
78
|
+
if (c) throw new n("This layout transition has already been animated.");
|
|
79
|
+
let { elements: i, ...l } = r, u = e(i ?? a.map(({ element: e }) => e)), d = o && s ? matchByKey(s, u, o) : matchByIdentity(a, u);
|
|
80
|
+
return c = !0, t(d.map(({ element: e }) => e), (e, t) => {
|
|
81
81
|
let n = d[t], r = `${round(n.x, 2)}px ${round(n.y, 2)}px`;
|
|
82
82
|
return [{
|
|
83
83
|
composite: "add",
|
|
@@ -91,7 +91,6 @@ function captureLayout(r, i = {}) {
|
|
|
91
91
|
}, l);
|
|
92
92
|
} };
|
|
93
93
|
}
|
|
94
|
-
//#endregion
|
|
95
94
|
export { captureLayout };
|
|
96
95
|
|
|
97
96
|
//# sourceMappingURL=layout.js.map
|
package/dist/motion.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
// src/motion.ts
|
|
2
2
|
function shouldReduceMotion(e) {
|
|
3
3
|
return e === "full" ? !1 : e === "reduced" || typeof matchMedia == "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
4
4
|
}
|
|
5
|
-
//#endregion
|
|
6
5
|
export { shouldReduceMotion };
|
|
7
6
|
|
|
8
7
|
//# sourceMappingURL=motion.js.map
|
package/dist/necromancer.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function attachAbortSignalListener(e,t){let abort=()=>t(e.reason);return e.addEventListener(`abort`,abort,{once:!0}),e.aborted?(e.removeEventListener(`abort`,abort),t(e.reason),()=>void 0):()=>e.removeEventListener(`abort`,abort)}function createResultPromise(e,t){return e.finished.then(()=>(t(),{status:`finished`}),e=>(t(),{reason:e,status:`cancelled`}))}function createAnimationHandle(e,t){let n=!1,r,i,a=createResultPromise(e,()=>{i?.(),i=void 0}).then(e=>n&&e.status===`cancelled`?r===void 0?{status:`cancelled`}:{reason:r,status:`cancelled`}:e),dispose=t=>{n||(n=!0,r=t,i?.(),i=void 0,e.cancel())};return t&&(i=attachAbortSignalListener(t,dispose)),{animation:e,dispose,get disposed(){return n},result:a,[Symbol.dispose](){this.dispose()}}}function createAnimationGroup(e,t){let n=!1,r,i=Promise.all(e.map(e=>e.result)).then(e=>(r?.(),r=void 0,e)),dispose=t=>{if(!n){n=!0,r?.(),r=void 0;for(let n of e)n.dispose(t)}};return t&&(r=attachAbortSignalListener(t,dispose)),{dispose,get disposed(){return n},handles:e,results:i,[Symbol.dispose](){this.dispose()}}}function resolveAnimationOptions(e={},t=!1){let{motion:n,signal:r,...i}=e;return t?{...i,delay:0,duration:0,endDelay:0,iterations:1}:i}function withStaggeredDelay(e,t){if(t===0)return e;let n=e.delay??0;return{...e,delay:n+t}}var NecromancerError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},NecromancerConfigError=class extends NecromancerError{},NecromancerUnsupportedError=class extends NecromancerError{};function shouldReduceMotion(e){return e===`full`?!1:e===`reduced`||typeof matchMedia==`function`&&matchMedia(`(prefers-reduced-motion: reduce)`).matches}function animate(e,t,n={}){if(n.signal?.throwIfAborted(),typeof e.animate!=`function`)throw new NecromancerUnsupportedError(`The Web Animations API is not available in this environment.`);return createAnimationHandle(e.animate(t,resolveAnimationOptions(n,shouldReduceMotion(n.motion??`system`))),n.signal)}function uniqueElements(e){return[...new Set(e)]}function validateStagger(e,t){if(!Number.isFinite(e)||e<0)throw new NecromancerConfigError(`stagger must be a finite, non-negative number.`);if(e>0&&t!==void 0&&typeof t!=`number`)throw new NecromancerConfigError(`A non-zero stagger requires a numeric delay; received ${typeof t}.`)}function animateEach(e,t,n={}){let{signal:r,stagger:i=0,...a}=n;validateStagger(i,a.delay),r?.throwIfAborted();let o=uniqueElements(e),s=o.map((e,n)=>typeof t==`function`?t(e,n,o.length):t);r?.throwIfAborted();let c=[];try{for(let[e,t]of o.entries()){let n=s[e];c.push(animate(t,n,withStaggeredDelay(a,e*i)))}}catch(e){for(let t of c)t.dispose(e);throw e}return createAnimationGroup(c,r)}function round(e,t){let n=10**t;return Math.round(e*n)/n}function measureSize(e){if(`offsetWidth`in e&&`offsetHeight`in e)return{height:e.offsetHeight,width:e.offsetWidth};let t=e.getBoundingClientRect();return{height:t.height,width:t.width}}function measureLayout(e){let t=e.getBoundingClientRect(),n=measureSize(e);return{height:n.height,width:n.width,x:t.x,y:t.y}}function computeScale(e,t){return t===0?1:e/t}function readKey(e,t,n){let r=t(e);if(typeof r!=`string`||r.length===0)throw new NecromancerConfigError(`getKey must return a non-empty string for every ${n} element.`);return r}function createKeyMap(e,t,n){let r=new Map;for(let i of e){let e=readKey(i.element,t,n);if(r.has(e))throw new NecromancerConfigError(`getKey returned duplicate keys for the ${n} elements.`);r.set(e,i)}return r}function changedElement(e,t){if(!e.isConnected)return;let n=measureLayout(e),r=t.x-n.x,i=t.y-n.y,a=computeScale(t.width,n.width),o=computeScale(t.height,n.height);return r===0&&i===0&&a===1&&o===1?void 0:{element:e,scaleX:a,scaleY:o,x:r,y:i}}function matchByIdentity(e,t){let n=new Map(e.map(e=>[e.element,e]));return t.flatMap(e=>{let t=n.get(e);return t?[changedElement(e,t.rect)].filter(e=>!!e):[]})}function matchByKey(e,t,n){let r=new Set;return t.flatMap(t=>{let i=readKey(t,n,`committed`),a=e.get(i);if(r.has(i))throw new NecromancerConfigError(`getKey returned duplicate keys for the committed elements.`);return r.add(i),a?[changedElement(t,a.rect)].filter(e=>!!e):[]})}function captureLayout(e,t={}){let n=uniqueElements(e).map(e=>({element:e,rect:measureLayout(e)})),r=t.getKey,i=r?createKeyMap(n,r,`captured`):void 0,a=!1;return{animate(e={}){if(a)throw new NecromancerConfigError(`This layout transition has already been animated.`);let{elements:t,...o}=e,s=uniqueElements(t??n.map(({element:e})=>e)),c=r&&i?matchByKey(i,s,r):matchByIdentity(n,s);return a=!0,animateEach(c.map(({element:e})=>e),(e,t)=>{let n=c[t],r=`${round(n.x,2)}px ${round(n.y,2)}px`;return[{composite:`add`,scale:`${round(n.scaleX,4)} ${round(n.scaleY,4)}`,translate:r},{composite:`add`,scale:`1 1`,translate:`0px 0px`}]},o)}}}exports.NecromancerConfigError=NecromancerConfigError,exports.NecromancerError=NecromancerError,exports.NecromancerUnsupportedError=NecromancerUnsupportedError,exports.animate=animate,exports.animateEach=animateEach,exports.captureLayout=captureLayout,exports.shouldReduceMotion=shouldReduceMotion;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function attachAbortSignalListener(e,t){let abort=()=>t(e.reason);return e.addEventListener(`abort`,abort,{once:!0}),e.aborted?(e.removeEventListener(`abort`,abort),t(e.reason),()=>void 0):()=>e.removeEventListener(`abort`,abort)}function createResultPromise(e,t){return e.finished.then(()=>(t(),{status:`finished`}),e=>(t(),{reason:e,status:`cancelled`}))}function createAnimationHandle(e,t){let n=!1,r,i,a=createResultPromise(e,()=>{i?.(),i=void 0}).then(e=>n&&e.status===`cancelled`?r===void 0?{status:`cancelled`}:{reason:r,status:`cancelled`}:e),dispose=t=>{n||(n=!0,r=t,i?.(),i=void 0,e.cancel())};return t&&(i=attachAbortSignalListener(t,dispose)),{animation:e,dispose,get disposed(){return n},result:a,[Symbol.dispose](){this.dispose()}}}function createAnimationGroup(e,t){let n=!1,r,i=Promise.all(e.map(e=>e.result)).then(e=>(r?.(),r=void 0,e)),dispose=t=>{if(!n){n=!0,r?.(),r=void 0;for(let n of e)n.dispose(t)}};return t&&(r=attachAbortSignalListener(t,dispose)),{dispose,get disposed(){return n},handles:e,results:i,[Symbol.dispose](){this.dispose()}}}function resolveAnimationOptions(e={},t=!1){let{motion:n,signal:r,...i}=e;return t?{...i,delay:0,duration:0,endDelay:0,iterations:1}:i}function withStaggeredDelay(e,t){if(t===0)return e;let n=e.delay??0;return{...e,delay:n+t}}var NecromancerError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},NecromancerConfigError=class extends NecromancerError{},NecromancerUnsupportedError=class extends NecromancerError{};function shouldReduceMotion(e){return e===`full`?!1:e===`reduced`||typeof matchMedia==`function`&&matchMedia(`(prefers-reduced-motion: reduce)`).matches}function animate(e,t,n={}){if(n.signal?.throwIfAborted(),typeof e.animate!=`function`)throw new NecromancerUnsupportedError(`The Web Animations API is not available in this environment.`);return createAnimationHandle(e.animate(t,resolveAnimationOptions(n,shouldReduceMotion(n.motion??`system`))),n.signal)}function uniqueElements(e){return[...new Set(e)]}function validateStagger(e,t){if(!Number.isFinite(e)||e<0)throw new NecromancerConfigError(`stagger must be a finite, non-negative number.`);if(e>0&&t!==void 0&&typeof t!=`number`)throw new NecromancerConfigError(`A non-zero stagger requires a numeric delay; received ${typeof t}.`)}function animateEach(e,t,n={}){let{signal:r,stagger:i=0,...a}=n;validateStagger(i,a.delay),r?.throwIfAborted();let o=uniqueElements(e),s=o.map((e,n)=>typeof t==`function`?t(e,n,o.length):t);r?.throwIfAborted();let c=[];try{for(let[e,t]of o.entries()){let n=s[e];c.push(animate(t,n,withStaggeredDelay(a,e*i)))}}catch(e){for(let t of c)t.dispose(e);throw e}return createAnimationGroup(c,r)}function round(e,t){let n=10**t;return Math.round(e*n)/n}function measureSize(e){if(`offsetWidth`in e&&`offsetHeight`in e)return{height:e.offsetHeight,width:e.offsetWidth};let t=e.getBoundingClientRect();return{height:t.height,width:t.width}}function measureLayout(e){let t=e.getBoundingClientRect(),n=measureSize(e);return{height:n.height,width:n.width,x:t.x,y:t.y}}function computeScale(e,t){return t===0?1:e/t}function readKey(e,t,n){let r=t(e);if(typeof r!=`string`||r.length===0)throw new NecromancerConfigError(`getKey must return a non-empty string for every ${n} element.`);return r}function createKeyMap(e,t,n){let r=new Map;for(let i of e){let e=readKey(i.element,t,n);if(r.has(e))throw new NecromancerConfigError(`getKey returned duplicate keys for the ${n} elements.`);r.set(e,i)}return r}function changedElement(e,t){if(!e.isConnected)return;let n=measureLayout(e),r=t.x-n.x,i=t.y-n.y,a=computeScale(t.width,n.width),o=computeScale(t.height,n.height);return r===0&&i===0&&a===1&&o===1?void 0:{element:e,scaleX:a,scaleY:o,x:r,y:i}}function matchByIdentity(e,t){let n=new Map(e.map(e=>[e.element,e]));return t.flatMap(e=>{let t=n.get(e);return t?[changedElement(e,t.rect)].filter(e=>!!e):[]})}function matchByKey(e,t,n){let r=new Set;return t.flatMap(t=>{let i=readKey(t,n,`committed`),a=e.get(i);if(r.has(i))throw new NecromancerConfigError(`getKey returned duplicate keys for the committed elements.`);return r.add(i),a?[changedElement(t,a.rect)].filter(e=>!!e):[]})}function captureLayout(e,t={}){let n=uniqueElements(e).map(e=>({element:e,rect:measureLayout(e)})),r=t.getKey,i=r?createKeyMap(n,r,`captured`):void 0,a=!1;return{animate(e={}){if(a)throw new NecromancerConfigError(`This layout transition has already been animated.`);let{elements:t,...o}=e,s=uniqueElements(t??n.map(({element:e})=>e)),c=r&&i?matchByKey(i,s,r):matchByIdentity(n,s);return a=!0,animateEach(c.map(({element:e})=>e),(e,t)=>{let n=c[t],r=`${round(n.x,2)}px ${round(n.y,2)}px`;return[{composite:`add`,scale:`${round(n.scaleX,4)} ${round(n.scaleY,4)}`,translate:r},{composite:`add`,scale:`1 1`,translate:`0px 0px`}]},o)}}}var e={damping:26,mass:1,precision:.1,stiffness:170},t=1/30,n=1/240;function validate(e,t){if(!Number.isFinite(t)||t<=0)throw new NecromancerConfigError(`${e} must be a positive finite number, got: ${t}`);return t}function validateBound(e,t){if(!Number.isFinite(t))throw new NecromancerConfigError(`${e} must be a finite number, got: ${t}`);return t}function spring(r,i,a,o={}){validateBound(`from`,r),validateBound(`to`,i);let s=validate(`stiffness`,o.stiffness??e.stiffness),c=validate(`damping`,o.damping??e.damping),l=validate(`mass`,o.mass??e.mass),u=validate(`precision`,o.precision??e.precision),d=o.velocity??0;if(!Number.isFinite(d))throw new NecromancerConfigError(`velocity must be a finite number, got: ${d}`);o.signal?.throwIfAborted();let f=!1,p=!1,m=0,h,settle=()=>void 0,g=new Promise(e=>{if(settle=t=>{p=!0,h?.(),h=void 0,e(t)},shouldReduceMotion(o.motion??`system`)){a(i,0),settle({status:`finished`});return}let g=r,_=d,v,step=e=>{if(f)return;let r=v===void 0?0:Math.min((e-v)/1e3,t);v=e;let o=r;for(;o>0;){let e=Math.min(o,n);o-=e;let t=(-s*(g-i)-c*_)/l;_+=t*e,g+=_*e}Math.abs(g-i)<u&&Math.abs(_)<u?(a(i,0),settle({status:`finished`})):(a(g,_),m=requestAnimationFrame(step))};m=requestAnimationFrame(step)}),dispose=e=>{f||p||(f=!0,cancelAnimationFrame(m),settle({reason:e,status:`cancelled`}))};return o.signal&&!p&&(h=attachAbortSignalListener(o.signal,dispose)),{dispose,get disposed(){return f},result:g,[Symbol.dispose](){this.dispose()}}}exports.NecromancerConfigError=NecromancerConfigError,exports.NecromancerError=NecromancerError,exports.NecromancerUnsupportedError=NecromancerUnsupportedError,exports.animate=animate,exports.animateEach=animateEach,exports.captureLayout=captureLayout,exports.shouldReduceMotion=shouldReduceMotion,exports.spring=spring;
|
|
2
2
|
//# sourceMappingURL=necromancer.cjs.map
|