@tamagui/animations-css 2.0.0-rc.4 → 2.0.0-rc.40

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.
@@ -2,48 +2,130 @@ var __create = Object.create;
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf,
6
- __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
7
  var __export = (target, all) => {
8
- for (var name in all) __defProp(target, name, {
9
- get: all[name],
10
- enumerable: !0
11
- });
12
- },
13
- __copyProps = (to, from, except, desc) => {
14
- if (from && typeof from == "object" || typeof from == "function") for (let key of __getOwnPropNames(from)) !__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, {
8
+ for (var name in all) __defProp(target, name, {
9
+ get: all[name],
10
+ enumerable: true
11
+ });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
15
16
  get: () => from[key],
16
17
  enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
17
18
  });
18
- return to;
19
- };
19
+ }
20
+ return to;
21
+ };
20
22
  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
26
- value: mod,
27
- enumerable: !0
28
- }) : target, mod)),
29
- __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
30
- value: !0
31
- }), mod);
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
28
+ value: mod,
29
+ enumerable: true
30
+ }) : target, mod));
31
+ var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
32
+ value: true
33
+ }), mod);
32
34
  var createAnimations_exports = {};
33
35
  __export(createAnimations_exports, {
34
36
  createAnimations: () => createAnimations
35
37
  });
36
38
  module.exports = __toCommonJS(createAnimations_exports);
37
- var import_animation_helpers = require("@tamagui/animation-helpers"),
38
- import_constants = require("@tamagui/constants"),
39
- import_use_presence = require("@tamagui/use-presence"),
40
- import_web = require("@tamagui/web"),
41
- import_react = __toESM(require("react"), 1);
39
+ var import_animation_helpers = require("@tamagui/animation-helpers");
40
+ var import_constants = require("@tamagui/constants");
41
+ var import_use_presence = require("@tamagui/use-presence");
42
+ var import_web = require("@tamagui/web");
43
+ var import_react = __toESM(require("react"), 1);
44
+ const EXTRACT_MS_REGEX = /(\d+(?:\.\d+)?)\s*ms/;
45
+ const EXTRACT_S_REGEX = /(\d+(?:\.\d+)?)\s*s/;
42
46
  function extractDuration(animation) {
43
- const msMatch = animation.match(/(\d+(?:\.\d+)?)\s*ms/);
44
- if (msMatch) return Number.parseInt(msMatch[1], 10);
45
- const sMatch = animation.match(/(\d+(?:\.\d+)?)\s*s/);
46
- return sMatch ? Math.round(Number.parseFloat(sMatch[1]) * 1e3) : 300;
47
+ const msMatch = animation.match(EXTRACT_MS_REGEX);
48
+ if (msMatch) {
49
+ return Number.parseInt(msMatch[1], 10);
50
+ }
51
+ const sMatch = animation.match(EXTRACT_S_REGEX);
52
+ if (sMatch) {
53
+ return Math.round(Number.parseFloat(sMatch[1]) * 1e3);
54
+ }
55
+ return 300;
56
+ }
57
+ const MS_DURATION_REGEX = /(\d+(?:\.\d+)?)\s*ms/;
58
+ const S_DURATION_REGEX = /(\d+(?:\.\d+)?)\s*s(?!tiffness)/;
59
+ function applyDurationOverride(animation, durationMs) {
60
+ const msReplaced = animation.replace(MS_DURATION_REGEX, `${durationMs}ms`);
61
+ if (msReplaced !== animation) {
62
+ return msReplaced;
63
+ }
64
+ const sReplaced = animation.replace(S_DURATION_REGEX, `${durationMs}ms`);
65
+ if (sReplaced !== animation) {
66
+ return sReplaced;
67
+ }
68
+ return `${durationMs}ms ${animation}`;
69
+ }
70
+ const TRANSFORM_KEYS = ["x", "y", "scale", "scaleX", "scaleY", "rotate", "rotateX", "rotateY", "rotateZ", "skewX", "skewY"];
71
+ function buildTransformString(style) {
72
+ if (!style) return "";
73
+ const parts = [];
74
+ if (style.x !== void 0 || style.y !== void 0) {
75
+ const x = style.x ?? 0;
76
+ const y = style.y ?? 0;
77
+ parts.push(`translate(${x}px, ${y}px)`);
78
+ }
79
+ if (style.scale !== void 0) {
80
+ parts.push(`scale(${style.scale})`);
81
+ }
82
+ if (style.scaleX !== void 0) {
83
+ parts.push(`scaleX(${style.scaleX})`);
84
+ }
85
+ if (style.scaleY !== void 0) {
86
+ parts.push(`scaleY(${style.scaleY})`);
87
+ }
88
+ if (style.rotate !== void 0) {
89
+ const val = style.rotate;
90
+ const unit = typeof val === "string" && val.includes("deg") ? "" : "deg";
91
+ parts.push(`rotate(${val}${unit})`);
92
+ }
93
+ if (style.rotateX !== void 0) {
94
+ parts.push(`rotateX(${style.rotateX}deg)`);
95
+ }
96
+ if (style.rotateY !== void 0) {
97
+ parts.push(`rotateY(${style.rotateY}deg)`);
98
+ }
99
+ if (style.rotateZ !== void 0) {
100
+ parts.push(`rotateZ(${style.rotateZ}deg)`);
101
+ }
102
+ if (style.skewX !== void 0) {
103
+ parts.push(`skewX(${style.skewX}deg)`);
104
+ }
105
+ if (style.skewY !== void 0) {
106
+ parts.push(`skewY(${style.skewY}deg)`);
107
+ }
108
+ return parts.join(" ");
109
+ }
110
+ function applyStylesToNode(node, style) {
111
+ if (!style) return;
112
+ const transformStr = buildTransformString(style);
113
+ if (transformStr) {
114
+ node.style.transform = transformStr;
115
+ }
116
+ for (const [key, value] of Object.entries(style)) {
117
+ if (TRANSFORM_KEYS.includes(key)) continue;
118
+ if (value === void 0) continue;
119
+ if (key === "opacity") {
120
+ node.style.opacity = String(value);
121
+ } else if (key === "backgroundColor") {
122
+ node.style.backgroundColor = String(value);
123
+ } else if (key === "color") {
124
+ node.style.color = String(value);
125
+ } else {
126
+ node.style[key] = typeof value === "number" ? `${value}px` : String(value);
127
+ }
128
+ }
47
129
  }
48
130
  function createAnimations(animations) {
49
131
  const reactionListeners = /* @__PURE__ */new WeakMap();
@@ -51,28 +133,43 @@ function createAnimations(animations) {
51
133
  animations,
52
134
  usePresence: import_use_presence.usePresence,
53
135
  ResetPresence: import_use_presence.ResetPresence,
54
- supportsCSS: !0,
55
136
  inputStyle: "css",
56
137
  outputStyle: "css",
57
- classNameAnimation: !0,
58
138
  useAnimatedNumber(initial) {
59
- const [val, setVal] = import_react.default.useState(initial),
60
- [onFinish, setOnFinish] = (0, import_react.useState)();
61
- return (0, import_constants.useIsomorphicLayoutEffect)(() => {
62
- onFinish && (onFinish?.(), setOnFinish(void 0));
63
- }, [onFinish]), {
139
+ const [val, setVal] = import_react.default.useState(initial);
140
+ const finishTimerRef = import_react.default.useRef(null);
141
+ return {
64
142
  getInstance() {
65
143
  return setVal;
66
144
  },
67
145
  getValue() {
68
146
  return val;
69
147
  },
70
- setValue(next, config, onFinish2) {
71
- setVal(next), setOnFinish(onFinish2);
148
+ setValue(next, config, onFinish) {
149
+ setVal(next);
150
+ if (finishTimerRef.current) {
151
+ clearTimeout(finishTimerRef.current);
152
+ finishTimerRef.current = null;
153
+ }
154
+ if (onFinish) {
155
+ if (!config || config.type === "direct" || config.type === "timing" && config.duration === 0) {
156
+ onFinish();
157
+ } else {
158
+ const duration = config.type === "timing" ? config.duration : 300;
159
+ finishTimerRef.current = setTimeout(onFinish, duration);
160
+ }
161
+ }
72
162
  const listeners = reactionListeners.get(setVal);
73
- listeners && listeners.forEach(listener => listener(next));
163
+ if (listeners) {
164
+ listeners.forEach(listener => listener(next));
165
+ }
74
166
  },
75
- stop() {}
167
+ stop() {
168
+ if (finishTimerRef.current) {
169
+ clearTimeout(finishTimerRef.current);
170
+ finishTimerRef.current = null;
171
+ }
172
+ }
76
173
  };
77
174
  },
78
175
  useAnimatedNumberReaction({
@@ -83,9 +180,11 @@ function createAnimations(animations) {
83
180
  let queue = reactionListeners.get(instance);
84
181
  if (!queue) {
85
182
  const next = /* @__PURE__ */new Set();
86
- reactionListeners.set(instance, next), queue = next;
183
+ reactionListeners.set(instance, next);
184
+ queue = next;
87
185
  }
88
- return queue.add(onValue), () => {
186
+ queue.add(onValue);
187
+ return () => {
89
188
  queue?.delete(onValue);
90
189
  };
91
190
  }, []);
@@ -93,60 +192,232 @@ function createAnimations(animations) {
93
192
  useAnimatedNumberStyle(val, getStyle) {
94
193
  return getStyle(val.getValue());
95
194
  },
195
+ useAnimatedNumbersStyle(vals, getStyle) {
196
+ return getStyle(...vals.map(v => v.getValue()));
197
+ },
198
+ // @ts-ignore - styleState is added by createComponent
96
199
  useAnimations: ({
97
200
  props,
98
201
  presence,
99
202
  style,
100
203
  componentState,
101
- stateRef
204
+ stateRef,
205
+ styleState
102
206
  }) => {
103
- const isEntering = !!componentState.unmounted,
104
- isExiting = presence?.[0] === !1,
105
- sendExitComplete = presence?.[1],
106
- wasEnteringRef = import_react.default.useRef(isEntering),
107
- justFinishedEntering = wasEnteringRef.current && !isEntering;
207
+ const isHydrating = componentState.unmounted === true;
208
+ const isEntering = !!componentState.unmounted;
209
+ const isExiting = presence?.[0] === false;
210
+ const sendExitComplete = presence?.[1];
211
+ const wasEnteringRef = import_react.default.useRef(isEntering);
212
+ const justFinishedEntering = wasEnteringRef.current && !isEntering;
108
213
  import_react.default.useEffect(() => {
109
214
  wasEnteringRef.current = isEntering;
110
215
  });
111
- const normalized = (0, import_animation_helpers.normalizeTransition)(props.transition),
112
- effectiveAnimationKey = (0, import_animation_helpers.getEffectiveAnimation)(normalized, isExiting ? "exit" : isEntering || justFinishedEntering ? "enter" : "default"),
113
- defaultAnimation = effectiveAnimationKey ? animations[effectiveAnimationKey] : null,
114
- animatedProperties = (0, import_animation_helpers.getAnimatedProperties)(normalized),
115
- hasDefault = normalized.default !== null || normalized.enter !== null || normalized.exit !== null,
116
- hasPerPropertyConfigs = animatedProperties.length > 0;
216
+ const exitCycleIdRef = import_react.default.useRef(0);
217
+ const exitCompletedRef = import_react.default.useRef(false);
218
+ const wasExitingRef = import_react.default.useRef(false);
219
+ const exitInterruptedRef = import_react.default.useRef(false);
220
+ const justStartedExiting = isExiting && !wasExitingRef.current;
221
+ const justStoppedExiting = !isExiting && wasExitingRef.current;
222
+ if (justStartedExiting) {
223
+ exitCycleIdRef.current++;
224
+ exitCompletedRef.current = false;
225
+ }
226
+ if (justStoppedExiting) {
227
+ exitCycleIdRef.current++;
228
+ exitInterruptedRef.current = true;
229
+ }
230
+ import_react.default.useEffect(() => {
231
+ wasExitingRef.current = isExiting;
232
+ });
233
+ const effectiveTransition = styleState?.effectiveTransition ?? props.transition;
234
+ const normalized = (0, import_animation_helpers.normalizeTransition)(effectiveTransition);
235
+ const animationState = isExiting ? "exit" : isEntering || justFinishedEntering ? "enter" : "default";
236
+ const effectiveAnimationKey = (0, import_animation_helpers.getEffectiveAnimation)(normalized, animationState);
237
+ const defaultAnimation = effectiveAnimationKey ? animations[effectiveAnimationKey] : null;
238
+ const animatedProperties = (0, import_animation_helpers.getAnimatedProperties)(normalized);
239
+ const hasDefault = normalized.default !== null || normalized.enter !== null || normalized.exit !== null;
240
+ const hasPerPropertyConfigs = animatedProperties.length > 0;
117
241
  let keys;
118
- if (props.animateOnly ? keys = props.animateOnly : hasPerPropertyConfigs && !hasDefault ? keys = animatedProperties : hasPerPropertyConfigs && hasDefault ? keys = ["all", ...animatedProperties] : keys = ["all"], (0, import_constants.useIsomorphicLayoutEffect)(() => {
242
+ if (props.animateOnly) {
243
+ keys = props.animateOnly;
244
+ } else if (hasPerPropertyConfigs && !hasDefault) {
245
+ keys = animatedProperties;
246
+ } else if (hasPerPropertyConfigs && hasDefault) {
247
+ keys = ["all", ...animatedProperties];
248
+ } else {
249
+ keys = ["all"];
250
+ }
251
+ (0, import_constants.useIsomorphicLayoutEffect)(() => {
119
252
  const host = stateRef.current.host;
120
253
  if (!sendExitComplete || !isExiting || !host) return;
121
- const node = host,
122
- animationDuration = defaultAnimation ? extractDuration(defaultAnimation) : 200,
123
- delay = normalized.delay ?? 0,
124
- fallbackTimeout = animationDuration + delay,
125
- timeoutId = setTimeout(() => {
126
- sendExitComplete?.();
127
- }, fallbackTimeout),
128
- onFinishAnimation = () => {
129
- clearTimeout(timeoutId), sendExitComplete?.();
130
- };
131
- return node.addEventListener("transitionend", onFinishAnimation), node.addEventListener("transitioncancel", onFinishAnimation), () => {
132
- clearTimeout(timeoutId), node.removeEventListener("transitionend", onFinishAnimation), node.removeEventListener("transitioncancel", onFinishAnimation);
254
+ const node = host;
255
+ const cycleId = exitCycleIdRef.current;
256
+ const completeExit = () => {
257
+ if (cycleId !== exitCycleIdRef.current) return;
258
+ if (exitCompletedRef.current) return;
259
+ exitCompletedRef.current = true;
260
+ sendExitComplete();
133
261
  };
134
- }, [sendExitComplete, isExiting]), !(0, import_animation_helpers.hasAnimation)(normalized)) return null;
135
- Array.isArray(style.transform) && (style.transform = (0, import_web.transformsToString)(style.transform));
262
+ if (keys.length === 0) {
263
+ completeExit();
264
+ return;
265
+ }
266
+ let rafId;
267
+ const wasInterrupted = exitInterruptedRef.current;
268
+ let ignoreCancelEvents = wasInterrupted;
269
+ const enterStyle = props.enterStyle;
270
+ const exitStyle = props.exitStyle;
271
+ const delayStr2 = normalized.delay ? ` ${normalized.delay}ms` : "";
272
+ const durationOverride2 = normalized.config?.duration;
273
+ const exitTransitionString = keys.map(key => {
274
+ const propAnimation = normalized.properties[key];
275
+ let animationValue = null;
276
+ if (typeof propAnimation === "string") {
277
+ animationValue = animations[propAnimation];
278
+ } else if (propAnimation && typeof propAnimation === "object" && propAnimation.type) {
279
+ animationValue = animations[propAnimation.type];
280
+ } else if (defaultAnimation) {
281
+ animationValue = defaultAnimation;
282
+ }
283
+ if (animationValue && durationOverride2) {
284
+ animationValue = applyDurationOverride(animationValue, durationOverride2);
285
+ }
286
+ return animationValue ? `${key} ${animationValue}${delayStr2}` : null;
287
+ }).filter(Boolean).join(", ");
288
+ if (wasInterrupted) {
289
+ exitInterruptedRef.current = false;
290
+ node.style.transition = "none";
291
+ if (exitStyle) {
292
+ const resetStyle = {};
293
+ for (const key of Object.keys(exitStyle)) {
294
+ if (key === "opacity") {
295
+ resetStyle[key] = 1;
296
+ } else if (TRANSFORM_KEYS.includes(key)) {
297
+ resetStyle[key] = key === "scale" || key === "scaleX" || key === "scaleY" ? 1 : 0;
298
+ } else if (enterStyle?.[key] !== void 0) {
299
+ resetStyle[key] = enterStyle[key];
300
+ }
301
+ }
302
+ applyStylesToNode(node, resetStyle);
303
+ } else {
304
+ node.style.opacity = "1";
305
+ node.style.transform = "none";
306
+ }
307
+ void node.offsetHeight;
308
+ } else if (exitStyle) {
309
+ ignoreCancelEvents = true;
310
+ node.style.transition = "none";
311
+ const resetStyle = {};
312
+ for (const key of Object.keys(exitStyle)) {
313
+ if (key === "opacity") {
314
+ resetStyle[key] = 1;
315
+ } else if (TRANSFORM_KEYS.includes(key)) {
316
+ resetStyle[key] = key === "scale" || key === "scaleX" || key === "scaleY" ? 1 : 0;
317
+ } else if (enterStyle?.[key] !== void 0) {
318
+ resetStyle[key] = enterStyle[key];
319
+ }
320
+ }
321
+ applyStylesToNode(node, resetStyle);
322
+ void node.offsetHeight;
323
+ rafId = requestAnimationFrame(() => {
324
+ if (cycleId !== exitCycleIdRef.current) return;
325
+ node.style.transition = exitTransitionString;
326
+ void node.offsetHeight;
327
+ applyStylesToNode(node, exitStyle);
328
+ ignoreCancelEvents = false;
329
+ });
330
+ }
331
+ let maxDuration = defaultAnimation ? extractDuration(defaultAnimation) : 200;
332
+ const animationConfigs = (0, import_animation_helpers.getAnimationConfigsForKeys)(normalized, animations, keys, defaultAnimation);
333
+ for (const animationValue of animationConfigs.values()) {
334
+ if (animationValue) {
335
+ const duration = extractDuration(animationValue);
336
+ if (duration > maxDuration) {
337
+ maxDuration = duration;
338
+ }
339
+ }
340
+ }
341
+ const delay = normalized.delay ?? 0;
342
+ const fallbackTimeout = maxDuration + delay;
343
+ const timeoutId = setTimeout(() => {
344
+ completeExit();
345
+ }, fallbackTimeout);
346
+ const transitioningProps = new Set(keys);
347
+ let completedCount = 0;
348
+ const onFinishAnimation = event => {
349
+ if (event.target !== node) return;
350
+ const eventProp = event.propertyName;
351
+ if (transitioningProps.has(eventProp) || eventProp === "all") {
352
+ completedCount++;
353
+ if (completedCount >= transitioningProps.size) {
354
+ clearTimeout(timeoutId);
355
+ completeExit();
356
+ }
357
+ }
358
+ };
359
+ const onCancelAnimation = () => {
360
+ if (ignoreCancelEvents) return;
361
+ clearTimeout(timeoutId);
362
+ completeExit();
363
+ };
364
+ node.addEventListener("transitionend", onFinishAnimation);
365
+ node.addEventListener("transitioncancel", onCancelAnimation);
366
+ if (wasInterrupted) {
367
+ rafId = requestAnimationFrame(() => {
368
+ if (cycleId !== exitCycleIdRef.current) return;
369
+ node.style.transition = exitTransitionString;
370
+ void node.offsetHeight;
371
+ applyStylesToNode(node, exitStyle);
372
+ ignoreCancelEvents = false;
373
+ });
374
+ }
375
+ return () => {
376
+ clearTimeout(timeoutId);
377
+ if (rafId !== void 0) cancelAnimationFrame(rafId);
378
+ node.removeEventListener("transitionend", onFinishAnimation);
379
+ node.removeEventListener("transitioncancel", onCancelAnimation);
380
+ node.style.transition = "";
381
+ };
382
+ }, [sendExitComplete, isExiting, stateRef, keys, normalized, defaultAnimation, props.enterStyle, props.exitStyle]);
383
+ if (isHydrating) {
384
+ return null;
385
+ }
386
+ if (!(0, import_animation_helpers.hasAnimation)(normalized)) {
387
+ return null;
388
+ }
389
+ if (Array.isArray(style.transform)) {
390
+ style.transform = (0, import_web.transformsToString)(style.transform);
391
+ }
136
392
  const delayStr = normalized.delay ? ` ${normalized.delay}ms` : "";
137
- return style.transition = keys.map(key => {
393
+ const durationOverride = normalized.config?.duration;
394
+ style.transition = keys.map(key => {
138
395
  const propAnimation = normalized.properties[key];
139
396
  let animationValue = null;
140
- return typeof propAnimation == "string" ? animationValue = animations[propAnimation] : propAnimation && typeof propAnimation == "object" && propAnimation.type ? animationValue = animations[propAnimation.type] : defaultAnimation && (animationValue = defaultAnimation), animationValue ? `${key} ${animationValue}${delayStr}` : null;
141
- }).filter(Boolean).join(", "), process.env.NODE_ENV === "development" && props.debug === "verbose" && console.info("CSS animation", {
142
- props,
143
- animations,
144
- normalized,
145
- defaultAnimation,
146
- style,
147
- isEntering,
148
- isExiting
149
- }), {
397
+ if (typeof propAnimation === "string") {
398
+ animationValue = animations[propAnimation];
399
+ } else if (propAnimation && typeof propAnimation === "object" && propAnimation.type) {
400
+ animationValue = animations[propAnimation.type];
401
+ } else if (defaultAnimation) {
402
+ animationValue = defaultAnimation;
403
+ }
404
+ if (animationValue && durationOverride) {
405
+ animationValue = applyDurationOverride(animationValue, durationOverride);
406
+ }
407
+ return animationValue ? `${key} ${animationValue}${delayStr}` : null;
408
+ }).filter(Boolean).join(", ");
409
+ if (process.env.NODE_ENV === "development" && props["debug"] === "verbose") {
410
+ console.info("CSS animation", {
411
+ props,
412
+ animations,
413
+ normalized,
414
+ defaultAnimation,
415
+ style,
416
+ isEntering,
417
+ isExiting
418
+ });
419
+ }
420
+ return {
150
421
  style,
151
422
  className: isEntering ? "t_unmounted" : ""
152
423
  };