angular-movement 0.0.1 → 0.0.2

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.
@@ -1,12 +1,13 @@
1
1
  import { isPlatformBrowser, DOCUMENT } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { InjectionToken, inject, PLATFORM_ID, Injectable, input, ElementRef, Directive, computed, effect, forwardRef, afterEveryRender, NgZone, signal, ViewContainerRef, TemplateRef, makeEnvironmentProviders } from '@angular/core';
3
+ import { InjectionToken, inject, PLATFORM_ID, Injectable, input, ElementRef, computed, effect, forwardRef, Directive, afterEveryRender, NgZone, signal, ViewContainerRef, TemplateRef, makeEnvironmentProviders } from '@angular/core';
4
4
 
5
5
  const MOVEMENT_DEFAULTS = {
6
6
  duration: 300,
7
7
  easing: 'cubic-bezier(0.16, 1, 0.3, 1)',
8
8
  delay: 0,
9
9
  disabled: false,
10
+ iterations: 1,
10
11
  };
11
12
  const MOVEMENT_CONFIG = new InjectionToken('MOVEMENT_CONFIG', {
12
13
  factory: () => ({ ...MOVEMENT_DEFAULTS }),
@@ -52,6 +53,18 @@ function getInterpolated(arr, i1, i2, p) {
52
53
  const v2 = arr[Math.min(i2, arr.length - 1)];
53
54
  return v1 + (v2 - v1) * p;
54
55
  }
56
+ const KNOWN_KEYS = new Set([
57
+ 'opacity',
58
+ 'x',
59
+ 'y',
60
+ 'scale',
61
+ 'scaleX',
62
+ 'scaleY',
63
+ 'rotate',
64
+ 'rotateX',
65
+ 'rotateY',
66
+ 'blur',
67
+ ]);
55
68
  function buildKeyframe(frames, getVal) {
56
69
  const keyframe = {};
57
70
  const opacity = getVal(frames.opacity);
@@ -87,6 +100,16 @@ function buildKeyframe(frames, getVal) {
87
100
  if (rotateX !== undefined || rotateY !== undefined) {
88
101
  keyframe.transform = `perspective(${DEFAULT_PERSPECTIVE}) rotateX(${rotateX ?? 0}deg) rotateY(${rotateY ?? 0}deg)`;
89
102
  }
103
+ // Passthrough arbitrary properties for WAAPI (e.g. strokeDashoffset)
104
+ for (const key in frames) {
105
+ if (KNOWN_KEYS.has(key))
106
+ continue;
107
+ const arr = frames[key];
108
+ const val = getVal(arr);
109
+ if (val !== undefined) {
110
+ keyframe[key] = val;
111
+ }
112
+ }
90
113
  return keyframe;
91
114
  }
92
115
  function composeKeyframeAt(frames, index) {
@@ -101,27 +124,51 @@ function composeInitialStyle(frames) {
101
124
  function composeFinalStyle(frames) {
102
125
  return buildKeyframe(frames, (arr) => (arr && arr.length > 0 ? arr[arr.length - 1] : undefined));
103
126
  }
127
+ const KNOWN_STYLE_KEYS = new Set([
128
+ 'opacity',
129
+ 'translate',
130
+ 'scale',
131
+ 'rotate',
132
+ 'transform',
133
+ 'filter',
134
+ ]);
104
135
  function applyComposedStyle(el, style) {
136
+ const styledEl = el;
105
137
  if (style.opacity !== undefined)
106
- el.style.opacity = `${style.opacity}`;
138
+ styledEl.style.opacity = `${style.opacity}`;
107
139
  if (style.translate !== undefined)
108
- el.style.translate = style.translate;
140
+ styledEl.style.translate = style.translate;
109
141
  if (style.scale !== undefined)
110
- el.style.scale = style.scale;
142
+ styledEl.style.scale = style.scale;
111
143
  if (style.rotate !== undefined)
112
- el.style.rotate = style.rotate;
144
+ styledEl.style.rotate = style.rotate;
113
145
  if (style.transform !== undefined)
114
- el.style.transform = style.transform;
146
+ styledEl.style.transform = style.transform;
115
147
  if (style.filter !== undefined)
116
- el.style.filter = style.filter;
148
+ styledEl.style.filter = style.filter;
149
+ // Passthrough arbitrary properties (e.g. strokeDashoffset)
150
+ for (const key in style) {
151
+ if (KNOWN_STYLE_KEYS.has(key))
152
+ continue;
153
+ const val = style[key];
154
+ if (val !== undefined) {
155
+ styledEl.style[key] = String(val);
156
+ }
157
+ }
117
158
  }
118
- function clearComposedStyle(el) {
119
- el.style.opacity = '';
120
- el.style.translate = '';
121
- el.style.scale = '';
122
- el.style.rotate = '';
123
- el.style.transform = '';
124
- el.style.filter = '';
159
+ function clearComposedStyle(el, extraKeys) {
160
+ const styledEl = el;
161
+ styledEl.style.opacity = '';
162
+ styledEl.style.translate = '';
163
+ styledEl.style.scale = '';
164
+ styledEl.style.rotate = '';
165
+ styledEl.style.transform = '';
166
+ styledEl.style.filter = '';
167
+ if (extraKeys) {
168
+ for (const key of extraKeys) {
169
+ styledEl.style[key] = '';
170
+ }
171
+ }
125
172
  }
126
173
 
127
174
  const DEFAULT_FADE_OPACITY = [0, 1];
@@ -178,6 +225,7 @@ const MOVE_PRESETS = {
178
225
  'bounce-in': {
179
226
  enter: { opacity: DEFAULT_FADE_OPACITY, y: [30, 0], scale: [0.85, 1] },
180
227
  leave: { opacity: DEFAULT_LEAVE_OPACITY, y: [0, -20], scale: [1, 0.9] },
228
+ loop: { y: [0, -10, 0], scale: [1, 0.95, 1] },
181
229
  },
182
230
  'blur-in': {
183
231
  enter: { opacity: DEFAULT_FADE_OPACITY, blur: [10, 0] },
@@ -186,10 +234,12 @@ const MOVE_PRESETS = {
186
234
  spin: {
187
235
  enter: { opacity: DEFAULT_FADE_OPACITY, rotate: [-360, 0] },
188
236
  leave: { opacity: DEFAULT_LEAVE_OPACITY, rotate: [0, 360] },
237
+ loop: { rotate: [0, 360] },
189
238
  },
190
239
  pulse: {
191
240
  enter: { scale: [1, 1.05, 1] },
192
241
  leave: { scale: [1, 0.95, 1] },
242
+ loop: { scale: [1, 1.05, 1] },
193
243
  },
194
244
  none: {
195
245
  enter: { opacity: [1, 1] },
@@ -207,6 +257,7 @@ function resolveMovementConfig(defaults, overrides, reducedMotion) {
207
257
  easing: overrides.easing ?? defaults.easing,
208
258
  delay: Math.max(0, overrides.delay ?? defaults.delay),
209
259
  disabled: reducedMotion || (overrides.disabled ?? defaults.disabled),
260
+ iterations: overrides.iterations ?? defaults.iterations,
210
261
  };
211
262
  }
212
263
  function resolveMoveFrames(value, phase) {
@@ -216,9 +267,9 @@ function resolveMoveFrames(value, phase) {
216
267
  if (typeof ngDevMode !== 'undefined' && ngDevMode) {
217
268
  console.warn(`[Movement] Unknown preset: "${value}". Using "none" preset.`);
218
269
  }
219
- return MOVE_PRESETS['none'][phase];
270
+ return MOVE_PRESETS['none'][phase] ?? MOVE_PRESETS['none'].enter;
220
271
  }
221
- return preset[phase];
272
+ return preset[phase] ?? preset.enter;
222
273
  }
223
274
  return value;
224
275
  }
@@ -273,12 +324,18 @@ class WaapiPlayer {
273
324
  return;
274
325
  }
275
326
  const keyframes = this.#toWAAPIKeyframes(frames);
327
+ const iterations = config.iterations ?? 1;
276
328
  this.#animation = host.animate(keyframes, {
277
329
  duration: config.duration,
278
330
  easing: config.easing,
279
331
  delay: config.delay,
280
332
  fill: 'both',
333
+ iterations,
281
334
  });
335
+ if (iterations === Infinity) {
336
+ // Infinite loops never finish; consumer must call cancel() manually.
337
+ return;
338
+ }
282
339
  this.#animation.addEventListener('finish', () => {
283
340
  this.#animation?.commitStyles?.();
284
341
  this.#animation?.cancel();
@@ -328,22 +385,28 @@ class SpringPlayer {
328
385
  host;
329
386
  frames;
330
387
  delay;
388
+ iterations;
331
389
  onDone;
332
390
  #resolveFinished;
333
391
  finished = new Promise((resolve) => {
334
392
  this.#resolveFinished = resolve;
335
393
  });
336
394
  #animation = null;
337
- constructor(host, frames, userConfig, delay, onDone) {
395
+ constructor(host, frames, userConfig, delay, iterations = 1, onDone) {
338
396
  this.host = host;
339
397
  this.frames = frames;
340
398
  this.delay = delay;
399
+ this.iterations = iterations;
341
400
  this.onDone = onDone;
342
401
  if (typeof host.animate !== 'function') {
343
402
  this.#resolveFinished();
344
403
  onDone?.();
345
404
  return;
346
405
  }
406
+ if (this.iterations !== 1 && typeof ngDevMode !== 'undefined' && ngDevMode) {
407
+ console.warn('[Movement] Spring animations with iterations !== 1 may produce visual glitches. ' +
408
+ 'Consider using WaapiPlayer (no spring) for loops.');
409
+ }
347
410
  const config = {
348
411
  stiffness: 100,
349
412
  damping: 10,
@@ -366,7 +429,12 @@ class SpringPlayer {
366
429
  delay: this.delay,
367
430
  fill: 'both',
368
431
  easing: 'linear', // Spring physics already has the easing baked into the frames
432
+ iterations: this.iterations,
369
433
  });
434
+ if (this.iterations === Infinity) {
435
+ // Infinite loops never finish; consumer must call cancel() manually.
436
+ return;
437
+ }
370
438
  this.#animation.addEventListener('finish', () => {
371
439
  this.#animation?.commitStyles?.();
372
440
  this.#animation?.cancel();
@@ -459,8 +527,9 @@ class AnimationEngine {
459
527
  }
460
528
  const config = options.config ?? this.#defaults;
461
529
  const isSpring = options.spring || config.easing === 'spring';
530
+ const iterations = options.iterations ?? config.iterations;
462
531
  if (isSpring) {
463
- return new SpringPlayer(host, frames, options.spring ?? {}, options.delay ?? config.delay, options.onDone);
532
+ return new SpringPlayer(host, frames, options.spring ?? {}, options.delay ?? config.delay, iterations, options.onDone);
464
533
  }
465
534
  else {
466
535
  return new WaapiPlayer(host, frames, {
@@ -468,6 +537,7 @@ class AnimationEngine {
468
537
  easing: config.easing,
469
538
  delay: options.delay ?? config.delay,
470
539
  disabled: false,
540
+ iterations,
471
541
  }, options.onDone);
472
542
  }
473
543
  }
@@ -486,6 +556,81 @@ const MOVE_STAGGER_PARENT = new InjectionToken('MOVE_STAGGER_PARENT');
486
556
 
487
557
  const MOVE_PRESENCE_PARENT = new InjectionToken('MOVE_PRESENCE_PARENT');
488
558
 
559
+ const MOVE_VARIANTS_PARENT = new InjectionToken('MOVE_VARIANTS_PARENT');
560
+ class MoveVariantsDirective {
561
+ moveVariants = input.required(...(ngDevMode ? [{ debugName: "moveVariants" }] : /* istanbul ignore next */ []));
562
+ moveAnimate = input(undefined, ...(ngDevMode ? [{ debugName: "moveAnimate" }] : /* istanbul ignore next */ []));
563
+ moveDuration = input(undefined, ...(ngDevMode ? [{ debugName: "moveDuration" }] : /* istanbul ignore next */ []));
564
+ moveEasing = input(undefined, ...(ngDevMode ? [{ debugName: "moveEasing" }] : /* istanbul ignore next */ []));
565
+ moveDelay = input(undefined, ...(ngDevMode ? [{ debugName: "moveDelay" }] : /* istanbul ignore next */ []));
566
+ moveDisabled = input(undefined, ...(ngDevMode ? [{ debugName: "moveDisabled" }] : /* istanbul ignore next */ []));
567
+ moveSpring = input(undefined, ...(ngDevMode ? [{ debugName: "moveSpring" }] : /* istanbul ignore next */ []));
568
+ #parent = inject(MOVE_VARIANTS_PARENT, { optional: true, skipSelf: true });
569
+ #engine = inject(AnimationEngine);
570
+ #defaults = inject(MOVEMENT_CONFIG);
571
+ #documentRef = inject(DOCUMENT);
572
+ #host = inject((ElementRef));
573
+ #stagger = inject(MOVE_STAGGER_PARENT, { optional: true });
574
+ #currentPlayer = null;
575
+ #isReducedMotion = false;
576
+ activeVariant = computed(() => {
577
+ return this.moveAnimate() ?? this.#parent?.activeVariant();
578
+ }, ...(ngDevMode ? [{ debugName: "activeVariant" }] : /* istanbul ignore next */ []));
579
+ constructor() {
580
+ this.#isReducedMotion = prefersReducedMotion(this.#documentRef);
581
+ this.#stagger?.register(this.#host.nativeElement);
582
+ effect(() => {
583
+ const variantName = this.activeVariant();
584
+ if (!variantName)
585
+ return;
586
+ const variants = this.moveVariants();
587
+ if (!variants)
588
+ return;
589
+ const state = variants[variantName];
590
+ if (!state)
591
+ return;
592
+ this.#currentPlayer?.cancel();
593
+ const { spring, duration, easing, delay, ...keyframesMap } = state;
594
+ const keyframes = keyframesMap;
595
+ const staggerDelay = this.#stagger?.getDelay(this.#host.nativeElement) ?? 0;
596
+ const config = resolveMovementConfig(this.#defaults, {
597
+ duration: duration ?? this.moveDuration(),
598
+ easing: easing ?? this.moveEasing(),
599
+ delay: (delay ?? this.moveDelay() ?? 0) + staggerDelay,
600
+ disabled: this.moveDisabled(),
601
+ }, this.#isReducedMotion);
602
+ this.#currentPlayer = this.#engine.play(this.#host.nativeElement, keyframes, {
603
+ config,
604
+ spring: spring ?? this.moveSpring(),
605
+ disabled: config.disabled,
606
+ });
607
+ });
608
+ }
609
+ ngOnDestroy() {
610
+ this.#stagger?.unregister(this.#host.nativeElement);
611
+ this.#currentPlayer?.cancel();
612
+ }
613
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MoveVariantsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
614
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.2", type: MoveVariantsDirective, isStandalone: true, selector: "[moveVariants]", inputs: { moveVariants: { classPropertyName: "moveVariants", publicName: "moveVariants", isSignal: true, isRequired: true, transformFunction: null }, moveAnimate: { classPropertyName: "moveAnimate", publicName: "moveAnimate", isSignal: true, isRequired: false, transformFunction: null }, moveDuration: { classPropertyName: "moveDuration", publicName: "moveDuration", isSignal: true, isRequired: false, transformFunction: null }, moveEasing: { classPropertyName: "moveEasing", publicName: "moveEasing", isSignal: true, isRequired: false, transformFunction: null }, moveDelay: { classPropertyName: "moveDelay", publicName: "moveDelay", isSignal: true, isRequired: false, transformFunction: null }, moveDisabled: { classPropertyName: "moveDisabled", publicName: "moveDisabled", isSignal: true, isRequired: false, transformFunction: null }, moveSpring: { classPropertyName: "moveSpring", publicName: "moveSpring", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
615
+ {
616
+ provide: MOVE_VARIANTS_PARENT,
617
+ useExisting: forwardRef(() => MoveVariantsDirective),
618
+ },
619
+ ], ngImport: i0 });
620
+ }
621
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MoveVariantsDirective, decorators: [{
622
+ type: Directive,
623
+ args: [{
624
+ selector: '[moveVariants]',
625
+ providers: [
626
+ {
627
+ provide: MOVE_VARIANTS_PARENT,
628
+ useExisting: forwardRef(() => MoveVariantsDirective),
629
+ },
630
+ ],
631
+ }]
632
+ }], ctorParameters: () => [], propDecorators: { moveVariants: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveVariants", required: true }] }], moveAnimate: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveAnimate", required: false }] }], moveDuration: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveDuration", required: false }] }], moveEasing: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveEasing", required: false }] }], moveDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveDelay", required: false }] }], moveDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveDisabled", required: false }] }], moveSpring: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveSpring", required: false }] }] } });
633
+
489
634
  class MoveAnimateDirective {
490
635
  move = input(undefined, ...(ngDevMode ? [{ debugName: "move" }] : /* istanbul ignore next */ []));
491
636
  moveAnimate = input(undefined, ...(ngDevMode ? [{ debugName: "moveAnimate" }] : /* istanbul ignore next */ []));
@@ -501,10 +646,15 @@ class MoveAnimateDirective {
501
646
  #engine = inject(AnimationEngine);
502
647
  #stagger = inject(MOVE_STAGGER_PARENT, { optional: true });
503
648
  #presence = inject(MOVE_PRESENCE_PARENT, { optional: true });
649
+ #variantsParent = inject(MOVE_VARIANTS_PARENT, { optional: true });
504
650
  #config = this.#defaults;
505
651
  #enterPlayer = null;
506
652
  #leavePlayer = null;
507
653
  ngOnInit() {
654
+ // If moveVariants is on the same host, let it handle animation
655
+ if (this.#variantsParent) {
656
+ return;
657
+ }
508
658
  this.#stagger?.register(this.#host.nativeElement);
509
659
  this.#presence?.register(this);
510
660
  Promise.resolve().then(() => {
@@ -530,7 +680,7 @@ class MoveAnimateDirective {
530
680
  this.#leavePlayer?.cancel();
531
681
  }
532
682
  playLeave() {
533
- if (this.#config.disabled) {
683
+ if (this.#variantsParent || this.#config.disabled) {
534
684
  return Promise.resolve();
535
685
  }
536
686
  this.#enterPlayer?.cancel();
@@ -871,81 +1021,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
871
1021
  }]
872
1022
  }], propDecorators: { moveWhileTap: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveWhileTap", required: true }] }], moveDuration: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveDuration", required: false }] }], moveEasing: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveEasing", required: false }] }], moveDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveDelay", required: false }] }], moveDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveDisabled", required: false }] }], moveSpring: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveSpring", required: false }] }] } });
873
1023
 
874
- const MOVE_VARIANTS_PARENT = new InjectionToken('MOVE_VARIANTS_PARENT');
875
- class MoveVariantsDirective {
876
- moveVariants = input.required(...(ngDevMode ? [{ debugName: "moveVariants" }] : /* istanbul ignore next */ []));
877
- moveAnimate = input(undefined, ...(ngDevMode ? [{ debugName: "moveAnimate" }] : /* istanbul ignore next */ []));
878
- moveDuration = input(undefined, ...(ngDevMode ? [{ debugName: "moveDuration" }] : /* istanbul ignore next */ []));
879
- moveEasing = input(undefined, ...(ngDevMode ? [{ debugName: "moveEasing" }] : /* istanbul ignore next */ []));
880
- moveDelay = input(undefined, ...(ngDevMode ? [{ debugName: "moveDelay" }] : /* istanbul ignore next */ []));
881
- moveDisabled = input(undefined, ...(ngDevMode ? [{ debugName: "moveDisabled" }] : /* istanbul ignore next */ []));
882
- moveSpring = input(undefined, ...(ngDevMode ? [{ debugName: "moveSpring" }] : /* istanbul ignore next */ []));
883
- #parent = inject(MOVE_VARIANTS_PARENT, { optional: true, skipSelf: true });
884
- #engine = inject(AnimationEngine);
885
- #defaults = inject(MOVEMENT_CONFIG);
886
- #documentRef = inject(DOCUMENT);
887
- #host = inject((ElementRef));
888
- #stagger = inject(MOVE_STAGGER_PARENT, { optional: true });
889
- #currentPlayer = null;
890
- #isReducedMotion = false;
891
- activeVariant = computed(() => {
892
- return this.moveAnimate() ?? this.#parent?.activeVariant();
893
- }, ...(ngDevMode ? [{ debugName: "activeVariant" }] : /* istanbul ignore next */ []));
894
- constructor() {
895
- this.#isReducedMotion = prefersReducedMotion(this.#documentRef);
896
- this.#stagger?.register(this.#host.nativeElement);
897
- effect(() => {
898
- const variantName = this.activeVariant();
899
- if (!variantName)
900
- return;
901
- const variants = this.moveVariants();
902
- if (!variants)
903
- return;
904
- const state = variants[variantName];
905
- if (!state)
906
- return;
907
- this.#currentPlayer?.cancel();
908
- const { spring, duration, easing, delay, ...keyframesMap } = state;
909
- const keyframes = keyframesMap;
910
- const staggerDelay = this.#stagger?.getDelay(this.#host.nativeElement) ?? 0;
911
- const config = resolveMovementConfig(this.#defaults, {
912
- duration: duration ?? this.moveDuration(),
913
- easing: easing ?? this.moveEasing(),
914
- delay: (delay ?? this.moveDelay() ?? 0) + staggerDelay,
915
- disabled: this.moveDisabled(),
916
- }, this.#isReducedMotion);
917
- this.#currentPlayer = this.#engine.play(this.#host.nativeElement, keyframes, {
918
- config,
919
- spring: spring ?? this.moveSpring(),
920
- disabled: config.disabled,
921
- });
922
- });
923
- }
924
- ngOnDestroy() {
925
- this.#stagger?.unregister(this.#host.nativeElement);
926
- this.#currentPlayer?.cancel();
927
- }
928
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MoveVariantsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
929
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.2", type: MoveVariantsDirective, isStandalone: true, selector: "[moveVariants]", inputs: { moveVariants: { classPropertyName: "moveVariants", publicName: "moveVariants", isSignal: true, isRequired: true, transformFunction: null }, moveAnimate: { classPropertyName: "moveAnimate", publicName: "moveAnimate", isSignal: true, isRequired: false, transformFunction: null }, moveDuration: { classPropertyName: "moveDuration", publicName: "moveDuration", isSignal: true, isRequired: false, transformFunction: null }, moveEasing: { classPropertyName: "moveEasing", publicName: "moveEasing", isSignal: true, isRequired: false, transformFunction: null }, moveDelay: { classPropertyName: "moveDelay", publicName: "moveDelay", isSignal: true, isRequired: false, transformFunction: null }, moveDisabled: { classPropertyName: "moveDisabled", publicName: "moveDisabled", isSignal: true, isRequired: false, transformFunction: null }, moveSpring: { classPropertyName: "moveSpring", publicName: "moveSpring", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
930
- {
931
- provide: MOVE_VARIANTS_PARENT,
932
- useExisting: forwardRef(() => MoveVariantsDirective),
933
- },
934
- ], ngImport: i0 });
935
- }
936
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MoveVariantsDirective, decorators: [{
937
- type: Directive,
938
- args: [{
939
- selector: '[moveVariants]',
940
- providers: [
941
- {
942
- provide: MOVE_VARIANTS_PARENT,
943
- useExisting: forwardRef(() => MoveVariantsDirective),
944
- },
945
- ],
946
- }]
947
- }], ctorParameters: () => [], propDecorators: { moveVariants: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveVariants", required: true }] }], moveAnimate: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveAnimate", required: false }] }], moveDuration: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveDuration", required: false }] }], moveEasing: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveEasing", required: false }] }], moveDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveDelay", required: false }] }], moveDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveDisabled", required: false }] }], moveSpring: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveSpring", required: false }] }] } });
948
-
949
1024
  class MoveStaggerDirective {
950
1025
  moveStagger = input.required(...(ngDevMode ? [{ debugName: "moveStagger" }] : /* istanbul ignore next */ []));
951
1026
  moveStaggerDirection = input('first', ...(ngDevMode ? [{ debugName: "moveStaggerDirection" }] : /* istanbul ignore next */ []));
@@ -1029,7 +1104,11 @@ class MoveLayoutDirective {
1029
1104
  this.#isAnimating) {
1030
1105
  return null;
1031
1106
  }
1032
- const currentRect = this.#host.nativeElement.getBoundingClientRect();
1107
+ const el = this.#host.nativeElement;
1108
+ if (typeof el.getBoundingClientRect !== 'function') {
1109
+ return null;
1110
+ }
1111
+ const currentRect = el.getBoundingClientRect();
1033
1112
  if (this.#snapshot) {
1034
1113
  const dx = this.#snapshot.left - currentRect.left;
1035
1114
  const dy = this.#snapshot.top - currentRect.top;
@@ -1338,7 +1417,7 @@ class MoveScrollDirective {
1338
1417
  if (!view)
1339
1418
  return;
1340
1419
  this.#player = this.#engine.play(this.#host.nativeElement, keyframes, {
1341
- config: { duration: 1000, easing: 'linear', delay: 0, disabled: false },
1420
+ config: { duration: 1000, easing: 'linear', delay: 0, disabled: false, iterations: 1 },
1342
1421
  });
1343
1422
  this.#player?.pause();
1344
1423
  // Sync the new player to the current scroll position immediately.
@@ -2101,7 +2180,7 @@ class MoveParallaxDirective {
2101
2180
  }
2102
2181
  this.#player?.cancel();
2103
2182
  this.#player = this.#engine.play(this.#host.nativeElement, frames, {
2104
- config: { duration: 1000, delay: 0, easing: 'linear', disabled: false },
2183
+ config: { duration: 1000, delay: 0, easing: 'linear', disabled: false, iterations: 1 },
2105
2184
  });
2106
2185
  this.#player?.pause();
2107
2186
  if (this.#player) {
@@ -2146,6 +2225,51 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
2146
2225
  }]
2147
2226
  }], ctorParameters: () => [], propDecorators: { moveParallax: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveParallax", required: false }] }], moveParallaxAxis: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveParallaxAxis", required: false }] }] } });
2148
2227
 
2228
+ class MoveLoopDirective {
2229
+ moveLoop = input('none', ...(ngDevMode ? [{ debugName: "moveLoop" }] : /* istanbul ignore next */ []));
2230
+ moveDuration = input(undefined, ...(ngDevMode ? [{ debugName: "moveDuration" }] : /* istanbul ignore next */ []));
2231
+ moveEasing = input(undefined, ...(ngDevMode ? [{ debugName: "moveEasing" }] : /* istanbul ignore next */ []));
2232
+ moveDelay = input(undefined, ...(ngDevMode ? [{ debugName: "moveDelay" }] : /* istanbul ignore next */ []));
2233
+ moveDisabled = input(undefined, ...(ngDevMode ? [{ debugName: "moveDisabled" }] : /* istanbul ignore next */ []));
2234
+ moveSpring = input(undefined, ...(ngDevMode ? [{ debugName: "moveSpring" }] : /* istanbul ignore next */ []));
2235
+ #defaults = inject(MOVEMENT_CONFIG);
2236
+ #documentRef = inject(DOCUMENT);
2237
+ #host = inject((ElementRef));
2238
+ #engine = inject(AnimationEngine);
2239
+ #player = null;
2240
+ ngOnInit() {
2241
+ const frames = resolveMoveFrames(this.moveLoop(), 'loop');
2242
+ // Skip noop presets to avoid creating an infinite animation that does nothing
2243
+ if (this.moveLoop() === 'none' || Object.keys(frames).length === 0) {
2244
+ return;
2245
+ }
2246
+ const isReduced = prefersReducedMotion(this.#documentRef);
2247
+ const config = resolveMovementConfig(this.#defaults, {
2248
+ duration: this.moveDuration(),
2249
+ easing: this.moveEasing(),
2250
+ delay: this.moveDelay(),
2251
+ disabled: this.moveDisabled(),
2252
+ }, isReduced);
2253
+ this.#player = this.#engine.play(this.#host.nativeElement, frames, {
2254
+ config,
2255
+ spring: this.moveSpring(),
2256
+ disabled: config.disabled,
2257
+ iterations: Infinity,
2258
+ });
2259
+ }
2260
+ ngOnDestroy() {
2261
+ this.#player?.cancel();
2262
+ }
2263
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MoveLoopDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2264
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.2", type: MoveLoopDirective, isStandalone: true, selector: "[moveLoop]", inputs: { moveLoop: { classPropertyName: "moveLoop", publicName: "moveLoop", isSignal: true, isRequired: false, transformFunction: null }, moveDuration: { classPropertyName: "moveDuration", publicName: "moveDuration", isSignal: true, isRequired: false, transformFunction: null }, moveEasing: { classPropertyName: "moveEasing", publicName: "moveEasing", isSignal: true, isRequired: false, transformFunction: null }, moveDelay: { classPropertyName: "moveDelay", publicName: "moveDelay", isSignal: true, isRequired: false, transformFunction: null }, moveDisabled: { classPropertyName: "moveDisabled", publicName: "moveDisabled", isSignal: true, isRequired: false, transformFunction: null }, moveSpring: { classPropertyName: "moveSpring", publicName: "moveSpring", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
2265
+ }
2266
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MoveLoopDirective, decorators: [{
2267
+ type: Directive,
2268
+ args: [{
2269
+ selector: '[moveLoop]',
2270
+ }]
2271
+ }], propDecorators: { moveLoop: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveLoop", required: false }] }], moveDuration: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveDuration", required: false }] }], moveEasing: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveEasing", required: false }] }], moveDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveDelay", required: false }] }], moveDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveDisabled", required: false }] }], moveSpring: [{ type: i0.Input, args: [{ isSignal: true, alias: "moveSpring", required: false }] }] } });
2272
+
2149
2273
  function provideMovement(config = {}) {
2150
2274
  return makeEnvironmentProviders([
2151
2275
  {
@@ -2176,6 +2300,7 @@ const MOVEMENT_DIRECTIVES = [
2176
2300
  MoveFocusDirective,
2177
2301
  MoveParallaxDirective,
2178
2302
  MoveAnimationDirective,
2303
+ MoveLoopDirective,
2179
2304
  ];
2180
2305
 
2181
2306
  /*
@@ -2186,5 +2311,5 @@ const MOVEMENT_DIRECTIVES = [
2186
2311
  * Generated bundle index. Do not edit.
2187
2312
  */
2188
2313
 
2189
- export { AnimationEngine, MOVEMENT_CONFIG, MOVEMENT_DEFAULTS, MOVEMENT_DIRECTIVES, MOVE_PRESENCE_PARENT, MOVE_PRESETS, MOVE_STAGGER_PARENT, MOVE_VARIANTS_PARENT, MoveAnimateDirective, MoveAnimationDirective, MoveDragDirective, MoveEnterDirective, MoveFocusDirective, MoveHoverDirective, MoveInViewDirective, MoveLayoutDirective, MoveLeaveDirective, MoveParallaxDirective, MovePresenceDirective, MoveScrollDirective, MoveSmoothScrollDirective, MoveStaggerDirective, MoveTapDirective, MoveTextDirective, MoveVariantsDirective, SmoothScrollService, SpringPlayer, WaapiPlayer, provideMovement };
2314
+ export { AnimationEngine, MOVEMENT_CONFIG, MOVEMENT_DEFAULTS, MOVEMENT_DIRECTIVES, MOVE_PRESENCE_PARENT, MOVE_PRESETS, MOVE_STAGGER_PARENT, MOVE_VARIANTS_PARENT, MoveAnimateDirective, MoveAnimationDirective, MoveDragDirective, MoveEnterDirective, MoveFocusDirective, MoveHoverDirective, MoveInViewDirective, MoveLayoutDirective, MoveLeaveDirective, MoveLoopDirective, MoveParallaxDirective, MovePresenceDirective, MoveScrollDirective, MoveSmoothScrollDirective, MoveStaggerDirective, MoveTapDirective, MoveTextDirective, MoveVariantsDirective, SmoothScrollService, SpringPlayer, WaapiPlayer, applyInitialStyles, clearInitialStyles, prefersReducedMotion, provideMovement, resolveMoveFrames, resolveMovementConfig, reverseFrames };
2190
2315
  //# sourceMappingURL=angular-movement.mjs.map