deja-vue 0.2.0 → 2.0.1

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.
Files changed (4) hide show
  1. package/README.md +41 -13
  2. package/dist/index.d.ts +497 -97
  3. package/dist/index.js +680 -187
  4. package/package.json +22 -24
package/dist/index.js CHANGED
@@ -1,5 +1,8 @@
1
1
  import { gsap } from "gsap";
2
- import { Fragment, computed, createBlock, defineComponent, inject, nextTick, onMounted, onUnmounted, openBlock, provide, renderSlot, resolveDynamicComponent, shallowRef, toValue, unref, useTemplateRef, watch, withCtx } from "vue";
2
+ import { ScrollTrigger } from "gsap/ScrollTrigger";
3
+ import { computed, createBlock, defineComponent, effectScope, inject, mergeModels, nextTick, onUnmounted, openBlock, provide, reactive, ref, renderSlot, shallowReactive, shallowRef, toRefs, toValue, unref, useAttrs, useModel, watch } from "vue";
4
+ import { getNodeElement, useUnwrap } from "vue-unwrap";
5
+ import { SplitText } from "gsap/SplitText";
3
6
  //#region src/constants.ts
4
7
  const ANIMATION_EVENTS = [
5
8
  "complete",
@@ -9,9 +12,82 @@ const ANIMATION_EVENTS = [
9
12
  "start",
10
13
  "update"
11
14
  ];
12
- const parentAnimationInjectionKey = Symbol("parent-animation");
15
+ const dejaVueParentInstance = Symbol("deja-vue-parent-instance");
13
16
  //#endregion
14
- //#region src/utils/EventBus.ts
17
+ //#region src/utils/index.ts
18
+ function cloneObject(target) {
19
+ return Array.isArray(target) ? target.map((target) => ({ ...target })) : { ...target };
20
+ }
21
+ function isObject(value) {
22
+ return typeof value === "object" && value !== null && !Array.isArray(value);
23
+ }
24
+ function patchArray(target, changes) {
25
+ for (let i = 0; i < changes.length; i++) if (!syncData(target[i], changes[i])) target[i] = changes[i];
26
+ target.length = changes.length;
27
+ }
28
+ function patchObject(target, changes) {
29
+ for (const key in changes) if (!syncData(target[key], changes[key])) target[key] = changes[key];
30
+ for (const key in target) if (!(key in changes)) delete target[key];
31
+ }
32
+ function syncData(target, changes) {
33
+ if (Object.is(target, changes)) return true;
34
+ if (!isObject(target) && !Array.isArray(target)) return false;
35
+ try {
36
+ if (JSON.stringify(target) === JSON.stringify(changes)) return true;
37
+ } catch {}
38
+ if (isObject(changes) && isObject(target)) {
39
+ patchObject(target, changes);
40
+ return true;
41
+ }
42
+ if (Array.isArray(changes) && Array.isArray(target)) {
43
+ patchArray(target, changes);
44
+ return true;
45
+ }
46
+ return false;
47
+ }
48
+ function toNonEmptyArray(data) {
49
+ return data.length ? data : null;
50
+ }
51
+ //#endregion
52
+ //#region src/utils/gsap.ts
53
+ function applyTimelineTotalDuration(timeline) {
54
+ const totalDuration = "totalDuration" in (timeline.data || {}) ? timeline.data.totalDuration : void 0;
55
+ if (totalDuration) timeline.duration(totalDuration);
56
+ }
57
+ function getScrollTriggerToggleActionByEvent(event, instance) {
58
+ if (!instance?.vars.toggleActions) return;
59
+ const actions = instance.vars.toggleActions.split(" ");
60
+ switch (event) {
61
+ case "enter": return actions[0];
62
+ case "enterBack": return actions[2];
63
+ case "leave": return actions[1];
64
+ case "leaveBack": return actions[3];
65
+ default: return;
66
+ }
67
+ }
68
+ function isEmptyTarget(target) {
69
+ return !target || Array.isArray(target) && !target.length;
70
+ }
71
+ function resolveTimelinePosition(timeline, position) {
72
+ if (!timeline) return null;
73
+ if (position === void 0) return timeline.duration();
74
+ if (typeof position === "number") return position;
75
+ if (typeof position === "string" && position in timeline.labels) return timeline.labels[position];
76
+ timeline.add(timelinePositionProbe, position);
77
+ const time = timeline.getTweensOf(timelinePositionProbe)[0]?.startTime();
78
+ timeline.remove(timelinePositionProbe);
79
+ return time ?? null;
80
+ }
81
+ function stripScrollTriggerVars(vars, defaultTrigger) {
82
+ if (!vars?.scrollTrigger) return null;
83
+ const scrollTriggerVars = typeof vars.scrollTrigger === "string" || typeof vars.scrollTrigger === "number" || "nodeType" in vars.scrollTrigger ? { trigger: vars.scrollTrigger } : vars.scrollTrigger;
84
+ delete vars.scrollTrigger;
85
+ if (!scrollTriggerVars.trigger && defaultTrigger) scrollTriggerVars.trigger = defaultTrigger;
86
+ return scrollTriggerVars;
87
+ }
88
+ function timelinePositionProbe() {}
89
+ //#endregion
90
+ //#region src/core/EventBus.ts
15
91
  var EventBus = class {
16
92
  bus;
17
93
  constructor(events) {
@@ -19,6 +95,7 @@ var EventBus = class {
19
95
  events?.forEach((event) => this.bus.set(event, /* @__PURE__ */ new Set()));
20
96
  }
21
97
  on(event, callback) {
98
+ if (!this.bus) return;
22
99
  if (Array.isArray(event)) event.forEach((e) => this.on(e, callback));
23
100
  else {
24
101
  if (!this.bus.has(event)) this.bus.set(event, /* @__PURE__ */ new Set());
@@ -33,190 +110,508 @@ var EventBus = class {
33
110
  this.on(event, fn);
34
111
  }
35
112
  off(event, callback) {
113
+ if (!this.bus) return;
36
114
  if (Array.isArray(event)) event.forEach((e) => this.off(e, callback));
37
115
  else if (this.bus.has(event) && this.bus.get(event).has(callback)) this.bus.get(event).delete(callback);
38
116
  }
39
117
  dispatch(event, ...args) {
40
- if (!this.bus.has(event)) return;
118
+ if (!this.bus?.has(event)) return;
41
119
  this.bus.get(event).forEach((callback) => callback(...args));
42
120
  }
43
121
  dispose() {
44
- this.bus.clear();
122
+ this.bus?.clear();
45
123
  this.bus = null;
46
124
  }
47
125
  };
48
126
  //#endregion
49
- //#region src/utils/Animation.ts
127
+ //#region src/core/Animation.ts
128
+ gsap.registerPlugin(ScrollTrigger);
50
129
  var Animation = class extends EventBus {
130
+ ctx;
131
+ scrollTrigger;
51
132
  timeline;
52
133
  constructor(options) {
53
- super([...ANIMATION_EVENTS]);
134
+ super(ANIMATION_EVENTS);
54
135
  this.timeline = gsap.timeline({
55
136
  ...options,
56
- onComplete: () => this.dispatch("complete"),
57
- onRepeat: () => this.dispatch("repeat"),
58
- onReverseComplete: () => this.dispatch("reverseComplete"),
59
- onStart: () => this.dispatch("start"),
60
- onUpdate: () => this.dispatch("update")
137
+ onComplete: () => this.dispatch("complete", this),
138
+ onInterrupt: () => this.dispatch("interrupt", this),
139
+ onRepeat: () => this.dispatch("repeat", this),
140
+ onReverseComplete: () => this.dispatch("reverseComplete", this),
141
+ onStart: () => this.dispatch("start", this),
142
+ onUpdate: () => this.dispatch("update", this)
61
143
  });
62
- (options ? Object.entries(options).filter(([key]) => key.startsWith("on")) : []).forEach(([key, fn]) => {
144
+ for (const [key, callback] of options ? Object.entries(options).filter(([key]) => key.startsWith("on")) : []) {
63
145
  const [firstChar, ...rest] = key.replace(/^on/, "");
64
146
  const event = `${firstChar.toLowerCase()}${rest.join("")}`;
65
- this.on(event, fn);
147
+ this.on(event, callback);
148
+ }
149
+ }
150
+ attachScrollTrigger(vars) {
151
+ this.scrollTrigger?.kill();
152
+ if (!vars) return;
153
+ this.scrollTrigger = ScrollTrigger.create({
154
+ ...vars,
155
+ animation: this.timeline,
156
+ onEnter: (self) => {
157
+ this.handleScrollTriggerReset("enter");
158
+ vars.onEnter?.(self);
159
+ },
160
+ onEnterBack: (self) => {
161
+ this.handleScrollTriggerReset("enterBack");
162
+ vars.onEnterBack?.(self);
163
+ },
164
+ onLeave: (self) => {
165
+ this.handleScrollTriggerReset("leave");
166
+ vars.onLeave?.(self);
167
+ },
168
+ onLeaveBack: (self) => {
169
+ this.handleScrollTriggerReset("leaveBack");
170
+ vars.onLeaveBack?.(self);
171
+ }
66
172
  });
67
173
  }
68
- add(child, position) {
174
+ compose(definition, withContext = true) {
175
+ if (!this.timeline || !definition.target || Array.isArray(definition.target) && !definition.target.length) return;
176
+ if (withContext) {
177
+ this.ctx = gsap.context(() => this.compose(definition, false));
178
+ return;
179
+ }
180
+ let scrollTriggerVars = null;
181
+ if (definition.method === "fromTo") {
182
+ const [from, to] = definition.vars;
183
+ scrollTriggerVars = stripScrollTriggerVars(to, definition.scope);
184
+ this.timeline.fromTo(definition.target, from, to);
185
+ } else if (definition.method in this.timeline) {
186
+ const vars = definition.vars;
187
+ scrollTriggerVars = stripScrollTriggerVars(vars, definition.scope);
188
+ this.timeline[definition.method](definition.target, vars);
189
+ } else console.warn("[deja-vue] Missing or unknown gsap effect.");
190
+ this.attachScrollTrigger(scrollTriggerVars);
191
+ }
192
+ add(child, position, timeShift = false) {
193
+ if (!this.timeline) return;
69
194
  if (typeof child === "string") this.timeline.addLabel(child, position);
70
195
  else if (typeof child === "function") this.timeline.add(child, position);
196
+ else if (this.timeline.isActive()) this.once(["complete", "reverseComplete"], () => this.add(child, position, timeShift));
71
197
  else {
72
- this.timeline.add(child.timeline, position);
73
- const totalDuration = "totalDuration" in (this.timeline.data || {}) ? this.timeline.data.totalDuration : void 0;
74
- if (totalDuration) this.timeline.duration(totalDuration);
198
+ const targetTime = timeShift ? resolveTimelinePosition(this.timeline, position) : null;
199
+ if (targetTime !== null) {
200
+ this.timeline.shiftChildren(child.timeline.duration(), false, targetTime + Number.EPSILON);
201
+ this.timeline.add(child.timeline, targetTime);
202
+ } else this.timeline.add(child.timeline, position);
203
+ applyTimelineTotalDuration(this.timeline);
75
204
  }
76
205
  }
77
- remove(child) {
206
+ remove(child, force) {
207
+ if (!this.timeline) return;
78
208
  if (typeof child === "string") this.timeline.removeLabel(child);
79
209
  else if (typeof child === "function") this.timeline.remove(child);
80
- else this.once(["complete", "reverseComplete"], () => {
210
+ else if (!child.timeline) return;
211
+ else if (this.timeline.isActive() && !force) this.once(["complete", "reverseComplete"], () => this.remove(child, force));
212
+ else {
81
213
  const startTime = child.timeline.startTime();
82
214
  const duration = child.timeline.duration();
215
+ const endTime = startTime + duration;
83
216
  this.timeline.remove(child.timeline);
84
- this.timeline.getChildren(false, true, true).forEach((child) => child.startTime() > startTime && child.startTime(child.startTime() - duration));
85
- this.timeline.invalidate();
86
- });
87
- }
88
- compose(target, definition) {
89
- if (Array.isArray(definition)) definition.forEach((tween) => this.compose(target, tween));
90
- else if (definition.method.startsWith("effect")) try {
91
- const [_, effect] = definition.method.split(":");
92
- this.timeline[effect](target, structuredClone(definition.vars));
93
- } catch {
94
- console.warn("Missing or unknown effect");
217
+ this.timeline.shiftChildren(-duration, false, endTime);
218
+ applyTimelineTotalDuration(this.timeline);
95
219
  }
96
- else if (definition.method === "fromTo") this.timeline.fromTo(target, ...structuredClone(definition.vars));
97
- else this.timeline[definition.method](target, structuredClone(definition.vars));
220
+ }
221
+ getChildPosition(child) {
222
+ if (!this.timeline) return;
223
+ if (typeof child === "string") return this.timeline.labels[child];
224
+ if (typeof child === "function") return this.timeline.getTweensOf(child)?.[0]?.startTime();
225
+ return this.timeline.getTweensOf(child.timeline)?.[0]?.startTime();
226
+ }
227
+ run(action = "play", ...args) {
228
+ if (action === "reset") {
229
+ const [atTime = 0, ...rest] = args;
230
+ this.timeline?.pause(atTime, ...rest);
231
+ } else this.timeline?.[action]?.(...args);
232
+ }
233
+ clear(revert) {
234
+ if (revert) this.ctx?.revert();
235
+ this.timeline?.clear(true);
98
236
  }
99
237
  dispose() {
100
238
  super.dispose();
101
- this.timeline.kill();
239
+ this.ctx?.revert();
240
+ this.scrollTrigger?.kill();
241
+ this.timeline?.kill();
242
+ this.ctx = null;
243
+ this.scrollTrigger = null;
102
244
  this.timeline = null;
103
245
  }
246
+ handleScrollTriggerReset(event) {
247
+ if (getScrollTriggerToggleActionByEvent(event, this.scrollTrigger) !== "reset") return;
248
+ gsap.delayedCall(0, () => this.dispatch("update", this));
249
+ }
104
250
  };
105
251
  //#endregion
252
+ //#region src/composables/useStableObjectProp.ts
253
+ function useStableObjectProp(objectProp, watchOptions) {
254
+ const computedObject = computed(() => toValue(objectProp) || {});
255
+ const stableObject = reactive(cloneObject(computedObject.value));
256
+ watch(computedObject, (updatedObject) => patchObject(stableObject, updatedObject), {
257
+ flush: "sync",
258
+ ...watchOptions
259
+ });
260
+ return stableObject;
261
+ }
262
+ //#endregion
106
263
  //#region src/composables/useAnimationControls.ts
107
264
  function useAnimationControls(animation, controls) {
108
- const progress = computed(() => toValue(controls.progress));
109
- const toggle = computed(() => toValue(controls.toggle));
110
- const controlled = computed(() => typeof progress.value === "number" || typeof toggle.value === "boolean");
111
- if (controlled.value && toggle.value !== true) animation.timeline.pause();
112
- watch(progress, (value) => {
113
- if (typeof value !== "number") return;
114
- animation.timeline.progress(value);
265
+ const direction = ref(0);
266
+ const trigger = computed(() => toValue(controls.trigger));
267
+ const triggerOptions = useStableObjectProp(controls.triggerOptions);
268
+ let triggerScope;
269
+ let lastProgress = animation.timeline.progress();
270
+ const controlled = controls.progress.value !== void 0 || trigger.value !== void 0;
271
+ if (controlled) animation.timeline.pause();
272
+ if (controls.progress.value === void 0) controls.progress.value = 0;
273
+ function onAnimationUpdate() {
274
+ const progress = animation.timeline.progress();
275
+ if (progress !== lastProgress) {
276
+ direction.value = progress > lastProgress ? 1 : -1;
277
+ lastProgress = progress;
278
+ }
279
+ if (progress === controls.progress.value) return;
280
+ controls.progress.value = progress;
281
+ }
282
+ animation.on("update", onAnimationUpdate);
283
+ watch(controls.progress, (current, previous) => {
284
+ if (previous === void 0 || current === void 0 || current === previous) return;
285
+ direction.value = current > previous ? 1 : -1;
286
+ if (animation.timeline.progress() === current) return;
287
+ animation.timeline.pause();
288
+ animation.timeline.progress(current);
289
+ });
290
+ watch(triggerOptions, (options) => {
291
+ triggerScope?.stop();
292
+ triggerScope = effectScope();
293
+ triggerScope.run(() => {
294
+ const { actionArgs, ...watchOptions } = options;
295
+ watch(trigger, () => {
296
+ const action = toValue(controls.triggerAction) || "play";
297
+ animation.run(action, ...actionArgs || []);
298
+ }, watchOptions);
299
+ });
115
300
  }, { immediate: true });
116
- watch(toggle, (value) => {
117
- if (typeof value !== "boolean") return;
118
- animation.timeline[value ? "play" : "reverse"]();
301
+ onUnmounted(() => {
302
+ animation.off("update", onAnimationUpdate);
303
+ triggerScope?.stop();
119
304
  });
120
- return { controlled };
305
+ return {
306
+ controlled,
307
+ direction
308
+ };
121
309
  }
122
310
  //#endregion
123
311
  //#region src/composables/useAnimationNesting.ts
124
- function useAnimationNesting(child, options) {
125
- const parent = options?.parent === null ? null : options?.parent || inject(parentAnimationInjectionKey, null);
126
- onMounted(async () => {
127
- if (!parent || !child) return;
312
+ function useAnimationNesting(target, options) {
313
+ const parent = options?.parent === null ? null : options?.parent || inject(dejaVueParentInstance, null);
314
+ const position = computed(() => toValue(options?.position));
315
+ const children = computed(() => toNonEmptyArray([target].flat().map((child) => {
316
+ if (!child) return "";
317
+ if ("animation" in child) return child.animation;
318
+ if ("callback" in child) return child.callback;
319
+ return toValue(child.label) || "";
320
+ }).filter(Boolean)));
321
+ if (parent) watch([children, position], async ([currentChildren, currentPosition], [previousChildren, previousPosition], onCleanup) => {
322
+ let skip = false;
323
+ onCleanup(() => skip = true);
324
+ if (!currentChildren && !previousChildren) return;
325
+ if (!currentChildren) {
326
+ previousChildren.forEach((child) => parent.animation.remove(child));
327
+ return;
328
+ }
329
+ if (previousChildren && currentChildren.length < previousChildren.length) previousChildren.slice(currentChildren.length).forEach((child) => parent.animation.remove(child));
128
330
  await nextTick();
129
- parent.add(child, options?.position);
130
- });
331
+ /* v8 ignore next */
332
+ if (skip) return;
333
+ currentChildren.forEach((currentChild, index) => {
334
+ const previousChild = previousChildren?.[index];
335
+ if (currentChild === previousChild && currentPosition === previousPosition) return;
336
+ if (previousChild) parent.animation.remove(previousChild);
337
+ parent.animation.add(currentChild, currentPosition);
338
+ });
339
+ }, { immediate: true });
131
340
  onUnmounted(() => {
132
- if (!child) return;
133
- if (parent) parent.remove(child);
134
- else if (child instanceof Animation) child.dispose();
341
+ if (!children.value) return;
342
+ if (parent) children.value.forEach((child) => {
343
+ parent.animation.remove(child, true);
344
+ if (child instanceof Animation) child.dispose();
345
+ });
346
+ else children.value.forEach((child) => child instanceof Animation && child.dispose());
135
347
  });
136
348
  return { parent };
137
349
  }
138
350
  //#endregion
139
- //#region src/composables/useAnimation.ts
140
- function useAnimation(wrapper, props, emit, options) {
141
- const animation = new Animation(options);
142
- const ready = shallowRef(false);
143
- const { controlled } = useAnimationControls(animation, {
144
- progress: () => props.progress,
145
- toggle: () => props.toggle
351
+ //#region src/composables/useAnimationScope.ts
352
+ const AnimationScopePropTypes = {
353
+ animation: Object,
354
+ direction: Number,
355
+ parent: Object,
356
+ progress: Number
357
+ };
358
+ function resolveChildrenTweenTarget(children) {
359
+ return children.map((child) => child && "seamless" in child && child.seamless ? child.tweenTarget : getNodeElement(child)).flat().filter(Boolean);
360
+ }
361
+ function useAnimationScope(options) {
362
+ const attrs = useAttrs();
363
+ const { children, root, Unwrap: AnimationScope } = useUnwrap(AnimationScopePropTypes);
364
+ const tweenTarget = computed(() => {
365
+ const target = toValue(options?.tweenTarget);
366
+ if (!attrs.is || !target || target === "children") return toNonEmptyArray((options?.resolveChildrenTweenTarget || resolveChildrenTweenTarget)(children));
367
+ if (typeof target !== "string") return target;
368
+ if (attrs.is && target === "self") return root.value;
369
+ return toNonEmptyArray(gsap.utils.toArray(target, root.value));
146
370
  });
147
- const { parent } = useAnimationNesting(animation, props);
148
- ANIMATION_EVENTS.forEach((event) => animation.on(event, () => emit(event, animation.timeline)));
149
- onMounted(() => {
150
- if (wrapper.value) {
151
- const target = props.group ? wrapper.value.children : wrapper.value;
152
- const definition = "tweens" in props ? props.tweens || [] : props;
153
- animation.compose(target, definition);
154
- }
155
- ready.value = true;
371
+ watch(tweenTarget, (_, previousTarget) => {
372
+ if (!previousTarget) return;
373
+ gsap.killTweensOf(previousTarget);
374
+ gsap.set(previousTarget, { clearProps: true });
156
375
  });
157
- onUnmounted(() => animation.dispose());
158
376
  return {
159
- animation,
160
- controlled,
161
- parent,
162
- ready
377
+ AnimationScope,
378
+ root,
379
+ tweenTarget
380
+ };
381
+ }
382
+ //#endregion
383
+ //#region src/composables/useSplitText.ts
384
+ gsap.registerPlugin(SplitText);
385
+ function useSplitText(domTarget, options) {
386
+ const instance = shallowRef();
387
+ const state = shallowReactive({
388
+ chars: [],
389
+ lines: [],
390
+ words: []
391
+ });
392
+ watch([() => toValue(domTarget), () => toValue(options)], ([target, vars]) => {
393
+ instance.value?.kill();
394
+ if (!target || !vars) return;
395
+ instance.value = new SplitText(target, {
396
+ ...cloneObject(vars),
397
+ type: vars.type || "lines,words,chars",
398
+ onSplit(self) {
399
+ vars.onSplit?.(self);
400
+ state.lines = self.lines;
401
+ state.words = self.words;
402
+ state.chars = self.chars;
403
+ }
404
+ });
405
+ }, { immediate: true });
406
+ onUnmounted(() => instance.value?.kill());
407
+ return {
408
+ instance,
409
+ state,
410
+ ...toRefs(state)
411
+ };
412
+ }
413
+ //#endregion
414
+ //#region src/composables/useTweenVars.ts
415
+ function useTweenVars(definition) {
416
+ const to = useStableObjectProp(() => definition.to);
417
+ const from = useStableObjectProp(() => definition.from);
418
+ const effectOptions = useStableObjectProp(() => definition.effectOptions);
419
+ const method = computed(() => {
420
+ if (definition.effect) return definition.effect;
421
+ if (definition.from && definition.to) return "fromTo";
422
+ return definition.from ? "from" : "to";
423
+ });
424
+ return {
425
+ method,
426
+ vars: computed(() => {
427
+ switch (method.value) {
428
+ case "fromTo": return [from, to];
429
+ case "from": return from;
430
+ case "to": return to;
431
+ default: return effectOptions;
432
+ }
433
+ })
163
434
  };
164
435
  }
165
436
  //#endregion
166
- //#region src/components/Callback.vue
167
- var Callback_default = /* @__PURE__ */ defineComponent({
168
- __name: "Callback",
437
+ //#region src/components/Marker.vue
438
+ var Marker_default = /* @__PURE__ */ defineComponent({
439
+ __name: "Marker",
169
440
  props: {
170
441
  parent: {
171
- type: [Object, null],
442
+ type: null,
172
443
  required: false
173
444
  },
174
445
  position: {
175
446
  type: null,
176
447
  required: false
177
448
  },
178
- fn: {
179
- type: Function,
180
- required: true
449
+ label: {
450
+ type: String,
451
+ required: false
181
452
  }
182
453
  },
183
- setup(__props) {
454
+ emits: ["cross"],
455
+ setup(__props, { expose: __expose, emit: __emit }) {
184
456
  const props = __props;
185
- useAnimationNesting(props.fn, props);
457
+ const emit = __emit;
458
+ const crossed = ref(false);
459
+ const { parent } = useAnimationNesting([{ callback: onCross }, { label: () => props.label }], {
460
+ parent: props.parent,
461
+ position: () => props.position
462
+ });
463
+ const instance = {
464
+ crossed,
465
+ parent
466
+ };
467
+ function onCross() {
468
+ emit("cross", toValue(parent.direction));
469
+ }
470
+ function onUpdate() {
471
+ const currentTime = parent.animation.timeline.time();
472
+ crossed.value = currentTime > (parent.animation.getChildPosition(onCross) ?? 0) || currentTime === parent.animation.timeline.duration();
473
+ }
474
+ parent?.animation.on("update", onUpdate);
475
+ onUnmounted(() => parent?.animation.off("update", onUpdate));
476
+ __expose(instance);
186
477
  return (_ctx, _cache) => {
187
- return openBlock(), createBlock(Fragment);
478
+ return renderSlot(_ctx.$slots, "default", {
479
+ crossed: crossed.value,
480
+ parent: unref(parent)
481
+ });
188
482
  };
189
483
  }
190
484
  });
191
485
  //#endregion
192
- //#region src/components/PositionMarker.vue
193
- var PositionMarker_default = /* @__PURE__ */ defineComponent({
194
- __name: "PositionMarker",
486
+ //#region src/components/SplitText.vue?vue&type=script&setup=true&lang.ts
487
+ const seamless = true;
488
+ //#endregion
489
+ //#region src/components/SplitText.vue
490
+ var SplitText_default = /* @__PURE__ */ defineComponent({
491
+ __name: "SplitText",
195
492
  props: {
196
- parent: {
197
- type: [Object, null],
493
+ type: {
494
+ type: String,
198
495
  required: false
199
496
  },
200
- position: {
497
+ mask: {
498
+ type: String,
499
+ required: false
500
+ },
501
+ wordDelimiter: {
201
502
  type: null,
202
503
  required: false
203
504
  },
204
- label: {
505
+ linesClass: {
506
+ type: String,
507
+ required: false
508
+ },
509
+ wordsClass: {
510
+ type: String,
511
+ required: false
512
+ },
513
+ charsClass: {
514
+ type: String,
515
+ required: false
516
+ },
517
+ aria: {
205
518
  type: String,
206
- required: true
519
+ required: false
520
+ },
521
+ tag: {
522
+ type: String,
523
+ required: false
524
+ },
525
+ propIndex: {
526
+ type: Boolean,
527
+ required: false
528
+ },
529
+ deepSlice: {
530
+ type: Boolean,
531
+ required: false
532
+ },
533
+ smartWrap: {
534
+ type: Boolean,
535
+ required: false
536
+ },
537
+ specialChars: {
538
+ type: null,
539
+ required: false
540
+ },
541
+ reduceWhiteSpace: {
542
+ type: Boolean,
543
+ required: false
544
+ },
545
+ autoSplit: {
546
+ type: Boolean,
547
+ required: false
548
+ },
549
+ ignore: {
550
+ type: null,
551
+ required: false
552
+ },
553
+ prepareText: {
554
+ type: null,
555
+ required: false
556
+ },
557
+ onSplit: {
558
+ type: Function,
559
+ required: false
560
+ },
561
+ onRevert: {
562
+ type: Function,
563
+ required: false
564
+ },
565
+ overwrite: {
566
+ type: Boolean,
567
+ required: false
568
+ },
569
+ tweenTarget: {
570
+ type: String,
571
+ required: false
207
572
  }
208
573
  },
209
- emits: ["cross"],
210
- setup(__props, { emit: __emit }) {
574
+ emits: ["split", "revert"],
575
+ setup(__props, { expose: __expose, emit: __emit }) {
576
+ const SplitTextPropTypes = {
577
+ chars: Object,
578
+ lines: Object,
579
+ words: Object
580
+ };
211
581
  const props = __props;
212
582
  const emit = __emit;
213
- const { parent } = useAnimationNesting(props.label, props);
214
- useAnimationNesting(() => emit("cross"), {
215
- parent,
216
- position: props.label
583
+ const attrs = useAttrs();
584
+ const { children, root, Unwrap } = useUnwrap(SplitTextPropTypes);
585
+ const { chars, lines, state, words } = useSplitText(computed(() => attrs.is ? root.value : toNonEmptyArray(children.map((child) => getNodeElement(child)).filter(Boolean))), computed(() => {
586
+ const { tweenTarget, ...rest } = props;
587
+ return {
588
+ ...rest,
589
+ onRevert: (self) => emit("revert", self),
590
+ onSplit: (self) => emit("split", self)
591
+ };
592
+ }));
593
+ __expose({
594
+ $el: root,
595
+ chars,
596
+ lines,
597
+ seamless,
598
+ tweenTarget: computed(() => {
599
+ const key = props.tweenTarget || props.type?.split(",").pop()?.trim() || "chars";
600
+ if (!(key in state)) return null;
601
+ return toNonEmptyArray(state[key]);
602
+ }),
603
+ words
217
604
  });
218
605
  return (_ctx, _cache) => {
219
- return openBlock(), createBlock(Fragment);
606
+ return openBlock(), createBlock(unref(Unwrap), {
607
+ chars: unref(chars),
608
+ lines: unref(lines),
609
+ words: unref(words)
610
+ }, null, 8, [
611
+ "chars",
612
+ "lines",
613
+ "words"
614
+ ]);
220
615
  };
221
616
  }
222
617
  });
@@ -224,33 +619,38 @@ var PositionMarker_default = /* @__PURE__ */ defineComponent({
224
619
  //#region src/components/Timeline.vue
225
620
  var Timeline_default = /* @__PURE__ */ defineComponent({
226
621
  __name: "Timeline",
227
- props: {
228
- progress: {
229
- type: Number,
622
+ props: /*@__PURE__*/ mergeModels({
623
+ seamless: {
624
+ type: Boolean,
230
625
  required: false
231
626
  },
232
- toggle: {
233
- type: Boolean,
234
- required: false,
235
- skipCheck: true,
236
- default: void 0
627
+ tweenTarget: {
628
+ type: null,
629
+ required: false
237
630
  },
238
631
  parent: {
239
- type: [Object, null],
632
+ type: null,
240
633
  required: false
241
634
  },
242
635
  position: {
243
636
  type: null,
244
637
  required: false
245
638
  },
246
- group: {
247
- type: Boolean,
639
+ progress: {
640
+ type: Number,
248
641
  required: false
249
642
  },
250
- tag: {
251
- type: String,
252
- required: false,
253
- default: "div"
643
+ trigger: {
644
+ type: null,
645
+ required: false
646
+ },
647
+ triggerAction: {
648
+ type: null,
649
+ required: false
650
+ },
651
+ triggerOptions: {
652
+ type: null,
653
+ required: false
254
654
  },
255
655
  duration: {
256
656
  type: Number,
@@ -259,52 +659,90 @@ var Timeline_default = /* @__PURE__ */ defineComponent({
259
659
  options: {
260
660
  type: null,
261
661
  required: false
262
- },
263
- tweens: {
264
- type: Array,
265
- required: false
266
662
  }
267
- },
268
- emits: [...ANIMATION_EVENTS],
663
+ }, {
664
+ "progress": {
665
+ type: Number,
666
+ default: void 0
667
+ },
668
+ "progressModifiers": {}
669
+ }),
670
+ emits: /*@__PURE__*/ mergeModels(ANIMATION_EVENTS, ["update:progress"]),
269
671
  setup(__props, { expose: __expose, emit: __emit }) {
270
672
  const props = __props;
271
673
  const emit = __emit;
272
- const wrapper = useTemplateRef("wrapper");
273
- const { animation, controlled, parent, ready } = useAnimation(wrapper, props, emit, {
274
- ...props.options,
275
- data: {
276
- ...props.options?.data,
277
- totalDuration: Number(props.duration)
278
- }
674
+ const { AnimationScope, root, tweenTarget } = useAnimationScope({ tweenTarget: () => props.tweenTarget });
675
+ const options = useStableObjectProp(() => props.options);
676
+ const vars = computed(() => {
677
+ const tl = cloneObject(options);
678
+ return {
679
+ scrollTrigger: stripScrollTriggerVars(tl, root.value),
680
+ timeline: tl
681
+ };
279
682
  });
280
- watch(() => props.duration, (duration) => animation.timeline.data.totalDuration = Number(duration));
281
- provide(parentAnimationInjectionKey, animation);
282
- __expose({
683
+ const animation = new Animation({
684
+ ...vars.value?.timeline,
685
+ data: typeof props.duration === "number" && props.duration > 0 ? {
686
+ ...vars.value?.timeline.data,
687
+ totalDuration: props.duration
688
+ } : vars.value?.timeline.data
689
+ });
690
+ const progress = useModel(__props, "progress");
691
+ const { controlled, direction } = useAnimationControls(animation, {
692
+ progress,
693
+ trigger: () => props.trigger,
694
+ triggerAction: () => props.triggerAction,
695
+ triggerOptions: () => props.triggerOptions
696
+ });
697
+ const { parent } = useAnimationNesting({ animation }, {
698
+ parent: props.parent,
699
+ position: () => props.position
700
+ });
701
+ const instance = {
702
+ $el: root,
283
703
  animation,
284
704
  controlled,
705
+ direction,
285
706
  parent,
286
- ready
707
+ progress,
708
+ seamless: computed(() => props.seamless),
709
+ tweenTarget
710
+ };
711
+ for (const event of ANIMATION_EVENTS) animation.on(event, () => emit(event, animation, parent));
712
+ watch(() => vars.value.timeline, (vars) => {
713
+ Object.assign(animation.timeline.vars, cloneObject(vars));
714
+ animation.timeline.invalidate();
287
715
  });
716
+ watch(() => vars.value.scrollTrigger, async (vars) => {
717
+ if (Object.keys(vars || {}).length) {
718
+ await nextTick();
719
+ animation.attachScrollTrigger(vars);
720
+ } else animation.attachScrollTrigger(null);
721
+ }, { immediate: true });
722
+ watch(() => props.duration, (duration) => {
723
+ if (typeof duration === "number" && duration > 0) {
724
+ animation.timeline.data.totalDuration = duration;
725
+ animation.timeline.duration(duration);
726
+ } else {
727
+ delete animation.timeline.data.totalDuration;
728
+ animation.timeline.timeScale(1);
729
+ animation.timeline.invalidate();
730
+ }
731
+ });
732
+ provide(dejaVueParentInstance, instance);
733
+ __expose(instance);
288
734
  return (_ctx, _cache) => {
289
- return __props.tweens ? (openBlock(), createBlock(resolveDynamicComponent(__props.tag), {
290
- key: 0,
291
- ref_key: "wrapper",
292
- ref: wrapper
293
- }, {
294
- default: withCtx(() => [renderSlot(_ctx.$slots, "default", {
295
- animation: unref(animation),
296
- controlled: unref(controlled),
297
- parent: unref(parent),
298
- ready: unref(ready)
299
- })]),
300
- _: 3
301
- }, 512)) : renderSlot(_ctx.$slots, "default", {
302
- key: 1,
735
+ return openBlock(), createBlock(unref(AnimationScope), {
303
736
  animation: unref(animation),
304
- controlled: unref(controlled),
737
+ direction: unref(direction),
305
738
  parent: unref(parent),
306
- ready: unref(ready)
307
- });
739
+ progress: progress.value
740
+ }, null, 8, [
741
+ "animation",
742
+ "direction",
743
+ "parent",
744
+ "progress"
745
+ ]);
308
746
  };
309
747
  }
310
748
  });
@@ -312,70 +750,125 @@ var Timeline_default = /* @__PURE__ */ defineComponent({
312
750
  //#region src/components/Tween.vue
313
751
  var Tween_default = /* @__PURE__ */ defineComponent({
314
752
  __name: "Tween",
315
- props: {
316
- progress: {
317
- type: Number,
753
+ props: /*@__PURE__*/ mergeModels({
754
+ seamless: {
755
+ type: Boolean,
318
756
  required: false
319
757
  },
320
- toggle: {
321
- type: Boolean,
322
- required: false,
323
- skipCheck: true,
324
- default: void 0
758
+ tweenTarget: {
759
+ type: null,
760
+ required: false
325
761
  },
326
762
  parent: {
327
- type: [Object, null],
763
+ type: null,
328
764
  required: false
329
765
  },
330
766
  position: {
331
- type: void 0,
767
+ type: null,
332
768
  required: false
333
769
  },
334
- group: {
335
- type: Boolean,
770
+ progress: {
771
+ type: Number,
336
772
  required: false
337
773
  },
338
- tag: {
339
- type: String,
340
- required: false,
341
- default: "div"
774
+ trigger: {
775
+ type: null,
776
+ required: false
777
+ },
778
+ triggerAction: {
779
+ type: null,
780
+ required: false
781
+ },
782
+ triggerOptions: {
783
+ type: null,
784
+ required: false
785
+ },
786
+ from: {
787
+ type: null,
788
+ required: false
789
+ },
790
+ to: {
791
+ type: null,
792
+ required: false
342
793
  },
343
- method: {
794
+ effect: {
344
795
  type: null,
345
- required: true
796
+ required: false
346
797
  },
347
- vars: {
798
+ effectOptions: {
348
799
  type: null,
349
800
  required: false
350
801
  }
351
- },
352
- emits: [...ANIMATION_EVENTS],
802
+ }, {
803
+ "progress": {
804
+ type: Number,
805
+ default: void 0
806
+ },
807
+ "progressModifiers": {}
808
+ }),
809
+ emits: /*@__PURE__*/ mergeModels(ANIMATION_EVENTS, ["update:progress"]),
353
810
  setup(__props, { expose: __expose, emit: __emit }) {
354
811
  const props = __props;
355
812
  const emit = __emit;
356
- const wrapper = useTemplateRef("wrapper");
357
- const { animation, controlled, parent, ready } = useAnimation(wrapper, props, emit);
358
- __expose({
813
+ const { AnimationScope, root, tweenTarget } = useAnimationScope({ tweenTarget: () => props.tweenTarget });
814
+ const animation = new Animation();
815
+ const progress = useModel(__props, "progress");
816
+ const { controlled, direction } = useAnimationControls(animation, {
817
+ progress,
818
+ trigger: () => props.trigger,
819
+ triggerAction: () => props.triggerAction,
820
+ triggerOptions: () => props.triggerOptions
821
+ });
822
+ const { parent } = useAnimationNesting({ animation }, {
823
+ parent: props.parent,
824
+ position: () => props.position
825
+ });
826
+ const { method: tweenMethod, vars: tweenVars } = useTweenVars(props);
827
+ const instance = {
828
+ $el: root,
359
829
  animation,
360
830
  controlled,
831
+ direction,
361
832
  parent,
362
- ready
833
+ progress,
834
+ seamless: computed(() => props.seamless),
835
+ tweenTarget
836
+ };
837
+ for (const event of ANIMATION_EVENTS) animation.on(event, () => emit(event, animation, parent));
838
+ watch([
839
+ root,
840
+ tweenMethod,
841
+ tweenTarget,
842
+ tweenVars
843
+ ], ([scope, method, target, vars]) => {
844
+ if (isEmptyTarget(target) || !method) return;
845
+ animation.clear(true);
846
+ const definition = {
847
+ method,
848
+ scope,
849
+ target,
850
+ vars: cloneObject(vars)
851
+ };
852
+ animation.compose(definition);
853
+ }, {
854
+ deep: true,
855
+ immediate: true
363
856
  });
857
+ __expose(instance);
364
858
  return (_ctx, _cache) => {
365
- return openBlock(), createBlock(resolveDynamicComponent(__props.tag), {
366
- ref_key: "wrapper",
367
- ref: wrapper
368
- }, {
369
- default: withCtx(() => [renderSlot(_ctx.$slots, "default", {
370
- animation: unref(animation),
371
- controlled: unref(controlled),
372
- parent: unref(parent),
373
- ready: unref(ready)
374
- })]),
375
- _: 3
376
- }, 512);
859
+ return openBlock(), createBlock(unref(AnimationScope), {
860
+ animation: unref(animation),
861
+ direction: unref(direction),
862
+ parent: unref(parent),
863
+ progress: progress.value
864
+ }, null, 8, [
865
+ "animation",
866
+ "direction",
867
+ "parent",
868
+ "progress"
869
+ ]);
377
870
  };
378
871
  }
379
872
  });
380
873
  //#endregion
381
- export { ANIMATION_EVENTS, Animation, Callback_default as Callback, PositionMarker_default as PositionMarker, Timeline_default as Timeline, Tween_default as Tween, parentAnimationInjectionKey, useAnimation, useAnimationControls, useAnimationNesting };
874
+ export { ANIMATION_EVENTS, Animation, Marker_default as Marker, SplitText_default as SplitText, Timeline_default as Timeline, Tween_default as Tween, applyTimelineTotalDuration, cloneObject, dejaVueParentInstance, getScrollTriggerToggleActionByEvent, isEmptyTarget, isObject, patchArray, patchObject, resolveChildrenTweenTarget, resolveTimelinePosition, stripScrollTriggerVars, syncData, toNonEmptyArray, useAnimationControls, useAnimationNesting, useAnimationScope, useSplitText, useStableObjectProp, useTweenVars };