@anguless/angulux-motion 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 angulux contributors
4
+ Copyright (c) 2016-2026 PrimeTek
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # angulux-motion
2
+
3
+ Transition and animation primitives used by angulux.
4
+
5
+ Part of [angulux](https://github.com/anguless-com/angulux). Published so the library has
6
+ no runtime dependency on packages whose upstream has moved to a commercial license.
7
+
8
+ ## Provenance
9
+
10
+ Forked from `@primeuix/motion`, MIT, at the final public commit of
11
+ [primefaces/primeuix](https://github.com/primefaces/primeuix) (`b9467bc`, repository
12
+ archived 2026-06-28\), which published as `@primeuix/motion 0.0.10`.
13
+
14
+ This package is versioned **independently of angulux**. angulux locks its major to Angular's,
15
+ but nothing here depends on Angular — there are no Angular imports and no Angular peer
16
+ dependency — so tying these versions to an Angular release would claim a relationship that
17
+ does not exist. The lineage lives in this file and in `PROVENANCE.md`, not in the version
18
+ number.
19
+
20
+ The public API is verified to cover that release completely; see `PROVENANCE.md` in the
21
+ repository root for the checksummed record and the command to verify it yourself.
22
+
23
+ ## License
24
+
25
+ MIT. See `LICENSE` and the repository's `NOTICE`. angulux is not affiliated with,
26
+ endorsed by, or sponsored by PrimeTek.
@@ -0,0 +1,299 @@
1
+ type MotionType = 'transition' | 'animation';
2
+ type MotionPhase = 'enter' | 'leave';
3
+ type MotionState = 'from' | 'to';
4
+ type MotionStage = 'Before' | 'Start' | 'After' | 'Cancelled';
5
+ /**
6
+ * Defines the duration of motion effects.
7
+ * It can be a single number representing the duration in milliseconds,
8
+ * or an object specifying different durations for 'enter' and 'leave' phases.
9
+ */
10
+ type MotionDuration = number | {
11
+ [P in MotionPhase]?: number;
12
+ } | undefined;
13
+ /**
14
+ * Options for specifying class names during different phases of motion.
15
+ * These class names are applied at the start, during, and at the end of the motion.
16
+ */
17
+ type ClassNameOptions = {
18
+ /**
19
+ * The class name to apply at the start of the motion.
20
+ */
21
+ from?: string | undefined;
22
+ /**
23
+ * The class name to apply while the motion is active.
24
+ */
25
+ active?: string | undefined;
26
+ /**
27
+ * The class name to apply at the end of the motion.
28
+ */
29
+ to?: string | undefined;
30
+ };
31
+ /**
32
+ * Defines class names for both 'enter' and 'leave' motion phases.
33
+ */
34
+ interface MotionClassNames {
35
+ /**
36
+ * Class names for the 'enter' motion phase.
37
+ * @see ClassNameOptions
38
+ */
39
+ enterClass?: ClassNameOptions | undefined;
40
+ /**
41
+ * Class names for the 'leave' motion phase.
42
+ * @see ClassNameOptions
43
+ */
44
+ leaveClass?: ClassNameOptions | undefined;
45
+ }
46
+ /**
47
+ * Metadata about the motion effect, including its type, timeout, and count.
48
+ */
49
+ type MotionMetadata = {
50
+ /**
51
+ * The type of motion effect, either 'transition' or 'animation'.
52
+ * @see MotionType
53
+ */
54
+ type: MotionType | undefined;
55
+ /**
56
+ * The timeout duration for the motion effect in milliseconds.
57
+ */
58
+ timeout: number | 0;
59
+ /**
60
+ * The count of transition or animation properties involved in the motion.
61
+ */
62
+ count: number | 0;
63
+ };
64
+ /**
65
+ * Event object passed to motion hooks, containing the target element.
66
+ */
67
+ interface MotionEvent {
68
+ /**
69
+ * The target element of the motion event.
70
+ */
71
+ element: Element;
72
+ }
73
+ /**
74
+ * Hooks for various stages of the motion lifecycle.
75
+ */
76
+ interface MotionHooks {
77
+ /**
78
+ * Called before the enter motion starts.
79
+ * @param event - The motion event object.
80
+ * @returns
81
+ */
82
+ onBeforeEnter?: (event?: MotionEvent) => void;
83
+ /**
84
+ * Called when the enter motion starts.
85
+ * @param event - The motion event object.
86
+ * @returns
87
+ */
88
+ onEnter?: (event?: MotionEvent) => void;
89
+ /**
90
+ * Called after the enter motion ends.
91
+ * @param event - The motion event object.
92
+ * @returns
93
+ */
94
+ onAfterEnter?: (event?: MotionEvent) => void;
95
+ /**
96
+ * Called if the enter motion is cancelled.
97
+ * @param event - The motion event object.
98
+ * @returns
99
+ */
100
+ onEnterCancelled?: (event?: MotionEvent) => void;
101
+ /**
102
+ * Called before the leave motion starts.
103
+ * @param event - The motion event object.
104
+ * @returns
105
+ */
106
+ onBeforeLeave?: (event?: MotionEvent) => void;
107
+ /**
108
+ * Called when the leave motion starts.
109
+ * @param event - The motion event object.
110
+ * @returns
111
+ */
112
+ onLeave?: (event?: MotionEvent) => void;
113
+ /**
114
+ * Called after the leave motion ends.
115
+ * @param event - The motion event object.
116
+ * @returns
117
+ */
118
+ onAfterLeave?: (event?: MotionEvent) => void;
119
+ /**
120
+ * Called if the leave motion is cancelled.
121
+ * @param event - The motion event object.
122
+ * @returns
123
+ */
124
+ onLeaveCancelled?: (event?: MotionEvent) => void;
125
+ }
126
+ /**
127
+ * Hooks organized by motion phase and stage.
128
+ */
129
+ type MotionHooksWithPhase = {
130
+ [P in MotionPhase]?: {
131
+ [S in MotionStage as `on${S}`]?: (MotionHooks & {
132
+ [key: string]: unknown;
133
+ })[`on${S extends 'Start' | 'Cancelled' ? '' : S}${Capitalize<P>}${S extends 'Cancelled' ? S : ''}`];
134
+ };
135
+ };
136
+ /**
137
+ * Class names organized by motion phase.
138
+ */
139
+ type MotionClassNamesWithPhase = {
140
+ [P in MotionPhase]: Required<ClassNameOptions>;
141
+ };
142
+ /**
143
+ * Options for configuring motion effects.
144
+ */
145
+ interface MotionOptions extends MotionClassNames, MotionHooks {
146
+ /**
147
+ * The base name used for generating default class names.
148
+ */
149
+ name?: string | undefined;
150
+ /**
151
+ * The type of motion effect to use.
152
+ * @see MotionType
153
+ */
154
+ type?: MotionType | undefined;
155
+ /**
156
+ * Indicates whether to respect the user's reduced motion preference.
157
+ */
158
+ safe?: boolean | undefined;
159
+ /**
160
+ * Indicates whether motion effects are disabled.
161
+ */
162
+ disabled?: boolean | undefined;
163
+ /**
164
+ * Indicates whether the motion should run on the initial render (appear phase).
165
+ */
166
+ appear?: boolean | undefined;
167
+ /**
168
+ * Indicates whether to perform enter motions.
169
+ */
170
+ enter?: boolean | undefined;
171
+ /**
172
+ * Indicates whether to perform leave motions.
173
+ */
174
+ leave?: boolean | undefined;
175
+ /**
176
+ * The duration of the motion effect.
177
+ * @see MotionDuration
178
+ */
179
+ duration?: MotionDuration | undefined;
180
+ /**
181
+ * Indicates whether to automatically adjust height during the motion.
182
+ */
183
+ autoHeight?: boolean | undefined;
184
+ /**
185
+ * Indicates whether to automatically adjust width during the motion.
186
+ */
187
+ autoWidth?: boolean | undefined;
188
+ }
189
+ /**
190
+ * Represents an instance of a motion effect applied to an element.
191
+ */
192
+ type MotionInstance = {
193
+ /**
194
+ * Starts the enter motion.
195
+ * @returns - A promise that resolves to a cancellation function or void.
196
+ */
197
+ enter: () => Promise<(() => void) | void>;
198
+ /**
199
+ * Starts the leave motion.
200
+ * @returns - A promise that resolves to a cancellation function or void.
201
+ */
202
+ leave: () => Promise<(() => void) | void>;
203
+ /**
204
+ * Cancels the motion.
205
+ * @returns
206
+ */
207
+ cancel: () => void;
208
+ /**
209
+ * Updates the motion instance with a new element and options.
210
+ * @param element - The target element.
211
+ * @param options - The motion options.
212
+ * @returns
213
+ */
214
+ update: (element: Element, options?: MotionOptions) => void;
215
+ };
216
+
217
+ declare const DEFAULT_MOTION_OPTIONS: MotionOptions;
218
+ /**
219
+ * Creates a MotionInstance for the given element with the specified options.
220
+ * @param element - The target element for motion effects.
221
+ * @param options - Configuration options for the motion instance.
222
+ * @returns A MotionInstance that can be used to control the motion.
223
+ */
224
+ declare function createMotion(element: Element, options?: MotionOptions): MotionInstance;
225
+
226
+ declare const ANIMATION = "animation";
227
+ declare const TRANSITION = "transition";
228
+ /**
229
+ * Determines whether motion effects should be skipped based on the provided options.
230
+ * @param options - The motion options to evaluate.
231
+ * @returns A boolean indicating whether motion should be skipped.
232
+ */
233
+ declare function shouldSkipMotion(options: MotionOptions | undefined): boolean;
234
+ /**
235
+ * Merges the provided motion options with the default options.
236
+ * @param inOptions - The motion options to merge.
237
+ * @param defaultOptions - The default motion options.
238
+ * @returns The merged motion options.
239
+ */
240
+ declare function mergeOptions(inOptions: MotionOptions | undefined, defaultOptions: MotionOptions): MotionOptions;
241
+ /**
242
+ * Resolves class names for motion phases based on the provided options.
243
+ * @param options - The motion options containing class names and base name.
244
+ * @returns The resolved class names organized by motion phase.
245
+ */
246
+ declare function resolveClassNames(options: MotionOptions | undefined): MotionClassNamesWithPhase;
247
+ /**
248
+ * Retrieves the motion hooks organized by phase based on the provided options.
249
+ * @param options - The motion options containing hooks.
250
+ * @returns The motion hooks organized by phase.
251
+ */
252
+ declare function getMotionHooks(options: MotionOptions | undefined): MotionHooksWithPhase;
253
+ /**
254
+ * Retrieves motion metadata including type, timeout, and count for the given element.
255
+ * @param element - The target element to retrieve motion metadata from.
256
+ * @param expectedType - The expected type of motion ('transition' or 'animation').
257
+ * @returns The motion metadata including type, timeout, and count.
258
+ */
259
+ declare function getMotionMetadata(element: Element, expectedType?: MotionMetadata['type']): MotionMetadata;
260
+ /**
261
+ * Resolves the duration for a given animation phase.
262
+ * @param duration - The duration can be a number or an object with `enter` and `leave` properties.
263
+ * @param phase - The phase of the transition/animation, either 'enter' or 'leave'.
264
+ * @returns The resolved duration in milliseconds or null if not specified.
265
+ */
266
+ declare function resolveDuration(duration: MotionOptions['duration'], phase: MotionPhase): number | null;
267
+ /**
268
+ * Sets CSS custom properties for auto height and/or width on the given element.
269
+ * @param element - The target HTML element.
270
+ * @param autoHeight - Whether to set the auto height CSS variable.
271
+ * @param autoWidth - Whether to set the auto width CSS variable.
272
+ * @returns
273
+ */
274
+ declare function setAutoDimensionVariables(element: HTMLElement, autoHeight?: boolean, autoWidth?: boolean): void;
275
+ /**
276
+ * Sets the current motion phase on the given element.
277
+ * @param element - The target HTML element.
278
+ * @param phase - The current motion phase.
279
+ */
280
+ declare function setMotionPhase(element: HTMLElement, phase: MotionPhase): void;
281
+ /**
282
+ * Sets the current motion state on the given element.
283
+ * @param element - The target HTML element.
284
+ * @param phase - The current motion phase.
285
+ * @param state - The current motion state.
286
+ */
287
+ declare function setMotionState(element: HTMLElement, phase: MotionPhase, state: MotionState): void;
288
+ /**
289
+ * Removes the motion phase attribute from the given element.
290
+ * @param element - The target HTML element.
291
+ */
292
+ declare function removeMotionPhase(element: HTMLElement): void;
293
+ /**
294
+ * Removes the motion state attributes from the given element.
295
+ * @param element - The target HTML element.
296
+ */
297
+ declare function removeMotionState(element: HTMLElement): void;
298
+
299
+ export { ANIMATION, type ClassNameOptions, DEFAULT_MOTION_OPTIONS, type MotionClassNames, type MotionClassNamesWithPhase, type MotionDuration, type MotionEvent, type MotionHooks, type MotionHooksWithPhase, type MotionInstance, type MotionMetadata, type MotionOptions, type MotionPhase, type MotionStage, type MotionState, type MotionType, TRANSITION, createMotion, getMotionHooks, getMotionMetadata, mergeOptions, removeMotionPhase, removeMotionState, resolveClassNames, resolveDuration, setAutoDimensionVariables, setMotionPhase, setMotionState, shouldSkipMotion };
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import{addClass as T,removeClass as $}from"@anguless/angulux-utils";import{getHiddenElementDimensions as R,isPrefersReducedMotion as j,setCSSProperty as x,toMs as y}from"@anguless/angulux-utils";var h="animation",p="transition";function L(t){return t?t.disabled||!!(t.safe&&j()):!1}function A(t,o){return t?{...t,...Object.entries(o).reduce((e,[i,f])=>(e[i]=t[i]??f,e),{})}:o}function S(t){let{name:o,enterClass:e,leaveClass:i}=t||{};return{enter:{from:e?.from||`${o}-enter-from`,to:e?.to||`${o}-enter-to`,active:e?.active||`${o}-enter-active`},leave:{from:i?.from||`${o}-leave-from`,to:i?.to||`${o}-leave-to`,active:i?.active||`${o}-leave-active`}}}function D(t){return{enter:{onBefore:t?.onBeforeEnter,onStart:t?.onEnter,onAfter:t?.onAfterEnter,onCancelled:t?.onEnterCancelled},leave:{onBefore:t?.onBeforeLeave,onStart:t?.onLeave,onAfter:t?.onAfterLeave,onCancelled:t?.onLeaveCancelled}}}function N(t,o){let e=window.getComputedStyle(t),i=u=>{let v=e[`${u}Delay`],d=e[`${u}Duration`];return[v.split(", ").map(y),d.split(", ").map(y)]},[f,r]=i(p),[m,c]=i(h),l=Math.max(...r.map((u,v)=>u+f[v])),s=Math.max(...c.map((u,v)=>u+m[v])),n,a=0,M=0;return o===p?l>0&&(n=p,a=l,M=r.length):o===h?s>0&&(n=h,a=s,M=c.length):(a=Math.max(l,s),n=a>0?l>s?p:h:void 0,M=n?n===p?r.length:c.length:0),{type:n,timeout:a,count:M}}function I(t,o){return typeof t=="number"?t:typeof t=="object"&&t[o]!=null?t[o]:null}function w(t,o=!0,e=!1){if(!o&&!e)return;let i=R(t);o&&x(t,"--height",i.height+"px"),e&&x(t,"--width",i.width+"px")}function k(t,o){t.setAttribute("data-phase",o)}function E(t,o,e){t.removeAttribute("data-enter"),t.removeAttribute("data-leave"),t.setAttribute(`data-${o}`,e)}function P(t){t.removeAttribute("data-phase")}function W(t){t.removeAttribute("data-enter"),t.removeAttribute("data-leave")}var q={name:"p",safe:!0,disabled:!1,enter:!0,leave:!0,autoHeight:!0,autoWidth:!1};function K(t,o){if(!t)throw new Error("Element is required.");let e={},i=!1,f={},r=null,m={},c=n=>{if(Object.assign(e,A(n,q)),!e.enter&&!e.leave)throw new Error("Enter or leave must be true.");m=D(e),i=L(e),f=S(e),r=null},l=async n=>{r?.();let{onBefore:a,onStart:M,onAfter:u,onCancelled:v}=m[n]||{},d={element:t};if(k(t,n),i){a?.(d),M?.(d),u?.(d),P(t);return}let{from:b,active:H,to:C}=f[n]||{};return w(t,e.autoHeight,e.autoWidth),a?.(d),T(t,b),T(t,H),E(t,n,"from"),t.offsetHeight,$(t,b),T(t,C),E(t,n,"to"),M?.(d),new Promise(O=>{let B=I(e.duration,n),g=()=>{$(t,[C,H]),r=null,W(t),P(t)},_=()=>{g(),u?.(d),O()};r=()=>{g(),v?.(d),O()},F(t,e.type,B,_)})};c(o);let s={enter:()=>e.enter?l("enter"):Promise.resolve(),leave:()=>e.leave?l("leave"):Promise.resolve(),cancel:()=>{r?.(),r=null},update:(n,a)=>{if(!n)throw new Error("Element is required.");t=n,s.cancel(),c(a)}};return e.appear&&s.enter(),s}var V=0;function F(t,o,e,i){let f=t._motionEndId=++V,r=()=>{f===t._motionEndId&&i()};if(e!=null)return setTimeout(r,e);let{type:m,timeout:c,count:l}=N(t,o);if(!m){i();return}let s=m+"end",n=0,a=()=>{t.removeEventListener(s,M,!0),r()},M=u=>{u.target===t&&++n>=l&&a()};t.addEventListener(s,M,{capture:!0,once:!0}),setTimeout(()=>{n<l&&a()},c+1)}export{h as ANIMATION,q as DEFAULT_MOTION_OPTIONS,p as TRANSITION,K as createMotion,D as getMotionHooks,N as getMotionMetadata,A as mergeOptions,P as removeMotionPhase,W as removeMotionState,S as resolveClassNames,I as resolveDuration,w as setAutoDimensionVariables,k as setMotionPhase,E as setMotionState,L as shouldSkipMotion};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/index.ts","../src/utils/index.ts"],"sourcesContent":["import { addClass, removeClass } from '@anguless/angulux-utils';\nimport type { MotionClassNamesWithPhase, MotionHooksWithPhase, MotionInstance, MotionOptions, MotionPhase, MotionType } from '../../types';\nimport { getMotionHooks, getMotionMetadata, mergeOptions, removeMotionPhase, removeMotionState, resolveClassNames, resolveDuration, setAutoDimensionVariables, setMotionPhase, setMotionState, shouldSkipMotion } from '../utils';\n\nexport const DEFAULT_MOTION_OPTIONS: MotionOptions = {\n name: 'p',\n safe: true,\n disabled: false,\n enter: true,\n leave: true,\n autoHeight: true,\n autoWidth: false\n};\n\n/**\n * Creates a MotionInstance for the given element with the specified options.\n * @param element - The target element for motion effects.\n * @param options - Configuration options for the motion instance.\n * @returns A MotionInstance that can be used to control the motion.\n */\nexport function createMotion(element: Element, options?: MotionOptions): MotionInstance {\n if (!element) throw new Error('Element is required.');\n\n const opts: MotionOptions = {};\n let skipMotion = false;\n let classNames: MotionClassNamesWithPhase = {} as MotionClassNamesWithPhase;\n let cancelCurrent: (() => void) | null = null;\n let hooks: MotionHooksWithPhase = {};\n\n const init = (newOpts?: MotionOptions) => {\n Object.assign(opts, mergeOptions(newOpts, DEFAULT_MOTION_OPTIONS));\n if (!opts.enter && !opts.leave) throw new Error('Enter or leave must be true.');\n\n hooks = getMotionHooks(opts);\n skipMotion = shouldSkipMotion(opts);\n classNames = resolveClassNames(opts);\n cancelCurrent = null;\n };\n\n const run = async (phase: MotionPhase): Promise<void> => {\n cancelCurrent?.();\n\n const { onBefore, onStart, onAfter, onCancelled } = hooks[phase] || {};\n const event = { element };\n\n setMotionPhase(element as HTMLElement, phase);\n\n if (skipMotion) {\n onBefore?.(event);\n onStart?.(event);\n onAfter?.(event);\n\n removeMotionPhase(element as HTMLElement);\n\n return;\n }\n\n const { from: fromClass, active: activeClass, to: toClass } = classNames[phase] || {};\n\n setAutoDimensionVariables(element as HTMLElement, opts.autoHeight, opts.autoWidth);\n\n onBefore?.(event);\n addClass(element, fromClass);\n addClass(element, activeClass);\n setMotionState(element as HTMLElement, phase, 'from');\n\n //await nextFrame();\n void (element as HTMLElement).offsetHeight; // force reflow\n\n removeClass(element, fromClass);\n addClass(element, toClass);\n setMotionState(element as HTMLElement, phase, 'to');\n onStart?.(event);\n\n return new Promise((resolve) => {\n const duration = resolveDuration(opts.duration, phase);\n\n const cleanup = () => {\n removeClass(element, [toClass, activeClass]);\n cancelCurrent = null;\n removeMotionState(element as HTMLElement);\n removeMotionPhase(element as HTMLElement);\n };\n\n const onDone = () => {\n cleanup();\n onAfter?.(event);\n resolve();\n };\n\n cancelCurrent = () => {\n cleanup();\n onCancelled?.(event);\n resolve();\n };\n\n whenEnd(element, opts.type, duration, onDone);\n });\n };\n\n init(options);\n\n const instance: MotionInstance = {\n enter: () => {\n if (!opts.enter) return Promise.resolve();\n\n return run('enter');\n },\n leave: () => {\n if (!opts.leave) return Promise.resolve();\n\n return run('leave');\n },\n cancel: () => {\n cancelCurrent?.();\n cancelCurrent = null;\n },\n update: (newElement?: Element, newOptions?: MotionOptions) => {\n if (!newElement) throw new Error('Element is required.');\n\n element = newElement as HTMLElement;\n instance.cancel();\n init(newOptions);\n }\n };\n\n if (opts.appear) instance.enter();\n\n return instance;\n}\n\nlet endId = 0;\n\n/**\n * Ported from Vue.js Transition Component;\n * @see https://github.com/vuejs/core/blob/main/packages/runtime-dom/src/components/Transition.ts#L348\n *\n * When the transition is triggered, it waits for the end of the motion (transition or animation)\n * @param element - The element to wait for the motion end.\n * @param expectedType - The expected type of motion (transition or animation).\n * @param explicitTimeout - An optional explicit timeout in milliseconds.\n * @param resolve - A function to call when the motion ends.\n * @returns A timeout ID if an explicit timeout is provided, otherwise undefined.\n */\nfunction whenEnd(element: Element & { _motionEndId?: number }, expectedType: MotionType | undefined, explicitTimeout: number | null, resolve: () => void) {\n const id = (element._motionEndId = ++endId);\n\n const resolveIfNotStale = () => {\n if (id === element._motionEndId) {\n resolve();\n }\n };\n\n if (explicitTimeout != null) {\n return setTimeout(resolveIfNotStale, explicitTimeout);\n }\n\n const { type, timeout, count } = getMotionMetadata(element, expectedType);\n\n if (!type) {\n resolve();\n\n return;\n }\n\n const endEvent = type + 'end';\n let ended = 0;\n\n const end = () => {\n element.removeEventListener(endEvent, onEnd, true);\n resolveIfNotStale();\n };\n\n const onEnd = (event: Event) => {\n if (event.target === element && ++ended >= count) {\n end();\n }\n };\n\n element.addEventListener(endEvent, onEnd, { capture: true, once: true });\n setTimeout(() => {\n if (ended < count) {\n end();\n }\n }, timeout + 1);\n}\n","import { getHiddenElementDimensions, isPrefersReducedMotion, setCSSProperty, toMs } from '@anguless/angulux-utils';\nimport type { MotionClassNamesWithPhase, MotionHooksWithPhase, MotionMetadata, MotionOptions, MotionPhase, MotionState, MotionType } from '../../types';\n\nexport const ANIMATION = 'animation';\nexport const TRANSITION = 'transition';\n\n/**\n * Determines whether motion effects should be skipped based on the provided options.\n * @param options - The motion options to evaluate.\n * @returns A boolean indicating whether motion should be skipped.\n */\nexport function shouldSkipMotion(options: MotionOptions | undefined): boolean {\n if (!options) {\n return false;\n }\n\n return options.disabled || !!(options.safe && isPrefersReducedMotion());\n}\n\n/**\n * Merges the provided motion options with the default options.\n * @param inOptions - The motion options to merge.\n * @param defaultOptions - The default motion options.\n * @returns The merged motion options.\n */\nexport function mergeOptions(inOptions: MotionOptions | undefined, defaultOptions: MotionOptions): MotionOptions {\n if (!inOptions) {\n return defaultOptions;\n }\n\n return {\n ...inOptions,\n ...(Object.entries(defaultOptions).reduce((acc: Record<string, unknown>, [key, value]) => {\n acc[key] = (inOptions as Record<string, unknown>)[key] ?? value;\n\n return acc;\n }, {}) as MotionOptions)\n };\n}\n\n/**\n * Resolves class names for motion phases based on the provided options.\n * @param options - The motion options containing class names and base name.\n * @returns The resolved class names organized by motion phase.\n */\nexport function resolveClassNames(options: MotionOptions | undefined): MotionClassNamesWithPhase {\n const { name, enterClass, leaveClass } = options || {};\n\n return {\n enter: {\n from: enterClass?.from || `${name}-enter-from`,\n to: enterClass?.to || `${name}-enter-to`,\n active: enterClass?.active || `${name}-enter-active`\n },\n leave: {\n from: leaveClass?.from || `${name}-leave-from`,\n to: leaveClass?.to || `${name}-leave-to`,\n active: leaveClass?.active || `${name}-leave-active`\n }\n };\n}\n\n/**\n * Retrieves the motion hooks organized by phase based on the provided options.\n * @param options - The motion options containing hooks.\n * @returns The motion hooks organized by phase.\n */\nexport function getMotionHooks(options: MotionOptions | undefined): MotionHooksWithPhase {\n return {\n enter: {\n onBefore: options?.onBeforeEnter,\n onStart: options?.onEnter,\n onAfter: options?.onAfterEnter,\n onCancelled: options?.onEnterCancelled\n },\n leave: {\n onBefore: options?.onBeforeLeave,\n onStart: options?.onLeave,\n onAfter: options?.onAfterLeave,\n onCancelled: options?.onLeaveCancelled\n }\n };\n}\n\n/**\n * Retrieves motion metadata including type, timeout, and count for the given element.\n * @param element - The target element to retrieve motion metadata from.\n * @param expectedType - The expected type of motion ('transition' or 'animation').\n * @returns The motion metadata including type, timeout, and count.\n */\nexport function getMotionMetadata(element: Element, expectedType?: MotionMetadata['type']): MotionMetadata {\n const styles = window.getComputedStyle(element);\n\n const getDelaysAndDurations = (type: MotionType): [number[], number[]] => {\n const delays = styles[`${type}Delay`];\n const durations = styles[`${type}Duration`];\n\n return [delays.split(', ').map(toMs), durations.split(', ').map(toMs)];\n };\n\n const [transitionDelays, transitionDurations] = getDelaysAndDurations(TRANSITION);\n const [animationDelays, animationDurations] = getDelaysAndDurations(ANIMATION);\n\n const transitionTimeout = Math.max(...transitionDurations.map((d, i) => d + transitionDelays[i]));\n const animationTimeout = Math.max(...animationDurations.map((d, i) => d + animationDelays[i]));\n\n let type: MotionMetadata['type'] = undefined;\n let timeout = 0;\n let count = 0;\n\n if (expectedType === TRANSITION) {\n if (transitionTimeout > 0) {\n type = TRANSITION;\n timeout = transitionTimeout;\n count = transitionDurations.length;\n }\n } else if (expectedType === ANIMATION) {\n if (animationTimeout > 0) {\n type = ANIMATION;\n timeout = animationTimeout;\n count = animationDurations.length;\n }\n } else {\n timeout = Math.max(transitionTimeout, animationTimeout);\n type = timeout > 0 ? (transitionTimeout > animationTimeout ? TRANSITION : ANIMATION) : undefined;\n count = type ? (type === TRANSITION ? transitionDurations.length : animationDurations.length) : 0;\n }\n\n return {\n type,\n timeout,\n count\n };\n}\n\n/**\n * Resolves the duration for a given animation phase.\n * @param duration - The duration can be a number or an object with `enter` and `leave` properties.\n * @param phase - The phase of the transition/animation, either 'enter' or 'leave'.\n * @returns The resolved duration in milliseconds or null if not specified.\n */\nexport function resolveDuration(duration: MotionOptions['duration'], phase: MotionPhase): number | null {\n if (typeof duration === 'number') {\n return duration;\n } else if (typeof duration === 'object' && duration[phase] != null) {\n return duration[phase];\n }\n\n return null;\n}\n\n/**\n * Sets CSS custom properties for auto height and/or width on the given element.\n * @param element - The target HTML element.\n * @param autoHeight - Whether to set the auto height CSS variable.\n * @param autoWidth - Whether to set the auto width CSS variable.\n * @returns\n */\nexport function setAutoDimensionVariables(element: HTMLElement, autoHeight: boolean = true, autoWidth: boolean = false): void {\n if (!autoHeight && !autoWidth) return;\n\n const dimensions = getHiddenElementDimensions(element);\n\n if (autoHeight) {\n setCSSProperty(element, '--height', dimensions.height + 'px');\n }\n\n if (autoWidth) {\n setCSSProperty(element, '--width', dimensions.width + 'px');\n }\n}\n\n/**\n * Sets the current motion phase on the given element.\n * @param element - The target HTML element.\n * @param phase - The current motion phase.\n */\nexport function setMotionPhase(element: HTMLElement, phase: MotionPhase): void {\n element.setAttribute('data-phase', phase);\n}\n\n/**\n * Sets the current motion state on the given element.\n * @param element - The target HTML element.\n * @param phase - The current motion phase.\n * @param state - The current motion state.\n */\nexport function setMotionState(element: HTMLElement, phase: MotionPhase, state: MotionState): void {\n element.removeAttribute('data-enter');\n element.removeAttribute('data-leave');\n\n element.setAttribute(`data-${phase}`, state);\n}\n\n/**\n * Removes the motion phase attribute from the given element.\n * @param element - The target HTML element.\n */\nexport function removeMotionPhase(element: HTMLElement): void {\n element.removeAttribute('data-phase');\n}\n\n/**\n * Removes the motion state attributes from the given element.\n * @param element - The target HTML element.\n */\nexport function removeMotionState(element: HTMLElement): void {\n element.removeAttribute('data-enter');\n element.removeAttribute('data-leave');\n}\n"],"mappings":"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","names":["addClass","removeClass","getHiddenElementDimensions","isPrefersReducedMotion","setCSSProperty","toMs","ANIMATION","TRANSITION","shouldSkipMotion","options","mergeOptions","inOptions","defaultOptions","acc","key","value","resolveClassNames","name","enterClass","leaveClass","getMotionHooks","getMotionMetadata","element","expectedType","styles","getDelaysAndDurations","type","delays","durations","transitionDelays","transitionDurations","animationDelays","animationDurations","transitionTimeout","d","i","animationTimeout","timeout","count","resolveDuration","duration","phase","setAutoDimensionVariables","autoHeight","autoWidth","dimensions","setMotionPhase","setMotionState","state","removeMotionPhase","removeMotionState","DEFAULT_MOTION_OPTIONS","createMotion","element","options","opts","skipMotion","classNames","cancelCurrent","hooks","init","newOpts","mergeOptions","getMotionHooks","shouldSkipMotion","resolveClassNames","run","phase","onBefore","onStart","onAfter","onCancelled","event","setMotionPhase","removeMotionPhase","fromClass","activeClass","toClass","setAutoDimensionVariables","addClass","setMotionState","removeClass","resolve","duration","resolveDuration","cleanup","removeMotionState","onDone","whenEnd","instance","newElement","newOptions","endId","expectedType","explicitTimeout","id","resolveIfNotStale","type","timeout","count","getMotionMetadata","endEvent","ended","end","onEnd"]}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@anguless/angulux-motion",
3
+ "version": "1.0.0",
4
+ "author": "PrimeTek Informatics",
5
+ "description": "Transition and animation primitives used by angulux.",
6
+ "keywords": [
7
+ "motion",
8
+ "animation",
9
+ "transition",
10
+ "composition utilities",
11
+ "primeng",
12
+ "primereact",
13
+ "primevue",
14
+ "primeuix"
15
+ ],
16
+ "license": "MIT",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/anguless-com/angulux.git",
20
+ "directory": "packages/angulux-motion"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/anguless-com/angulux/issues"
24
+ },
25
+ "main": "./dist/index.mjs",
26
+ "module": "./dist/index.mjs",
27
+ "types": "./dist/index.d.mts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.mts",
31
+ "import": "./dist/index.mjs",
32
+ "default": "./dist/index.mjs"
33
+ }
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "sideEffects": false,
39
+ "files": [
40
+ "dist",
41
+ "README.md",
42
+ "LICENSE"
43
+ ],
44
+ "dependencies": {
45
+ "@anguless/angulux-utils": "^1.0.0"
46
+ },
47
+ "engines": {
48
+ "node": ">=12.11.0"
49
+ },
50
+ "private": false,
51
+ "scripts": {
52
+ "build": "cross-env NODE_ENV=production tsup",
53
+ "build:dev": "tsup --watch"
54
+ }
55
+ }