react-ui-animate 5.3.1 → 5.4.0-next.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/README.md +12 -706
- package/dist/index.d.ts +60 -9
- package/dist/index.mjs +4041 -1
- package/package.json +7 -3
package/dist/index.mjs
CHANGED
|
@@ -1 +1,4041 @@
|
|
|
1
|
-
import{createContext as t,useRef as n,useState as i,useMemo as s,Children as e,isValidElement as o,useLayoutEffect as r,useEffect as a,useCallback as c,useContext as l,forwardRef as u,createElement as h}from"react";import{jsx as f,Fragment as d}from"react/jsx-runtime";function p(t,n){var i={};for(var s in t)({}).hasOwnProperty.call(t,s)&&0>n.indexOf(s)&&(i[s]=t[s]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var e=0;for(s=Object.getOwnPropertySymbols(t);e<s.length;e++)0>n.indexOf(s[e])&&{}.propertyIsEnumerable.call(t,s[e])&&(i[s[e]]=t[s[e]])}return i}"function"==typeof SuppressedError&&SuppressedError;const m=.01,v=["inset","normal","italic","bold","thin","medium","thick"],y=["translate","rotate","scale","skew","matrix","perspective"],g={transparent:"#00000000",black:"#000000ff",white:"#ffffffff",red:"#ff0000ff",green:"#008000ff",blue:"#0000ffff",yellow:"#ffff00ff",cyan:"#00ffffff",magenta:"#ff00ffff",gray:"#808080ff",grey:"#808080ff",darkgray:"#a9a9a9ff",darkgrey:"#a9a9a9ff",lightgray:"#d3d3d3ff",lightgrey:"#d3d3d3ff",dimgray:"#696969ff",dimgrey:"#696969ff",slategray:"#708090ff",slategrey:"#708090ff",darkslategray:"#2f4f4fff",darkslategrey:"#2f4f4fff",orange:"#ffa500ff",purple:"#800080ff",pink:"#ffc0cbff",brown:"#a52a2aff",navy:"#000080ff",teal:"#008080ff",silver:"#c0c0c0ff",gold:"#ffd700ff",aqua:"#00ffffff",fuchsia:"#ff00ffff",lime:"#00ff00ff",maroon:"#800000ff",olive:"#808000ff",rebeccapurple:"#663399ff"},b=/-?\d+(\.\d+)?/g,w=/^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i,x=/^rgba?\(\s*-?\d+(\.\d+)?%?(?:\s*,\s*-?\d+(\.\d+)?%?){2}(?:\s*,\s*(0|1|0?\.\d+))?\s*\)$/i,O=/^hsl\(\s*\d+(\.\d+)?(?:\s*,\s*\d+(\.\d+)?%){2}\s*\)$/i,j=/^hsla\(\s*\d+(\.\d+)?(?:\s*,\s*\d+(\.\d+)?%){2}\s*,\s*(0|1|0?\.\d+)\s*\)$/i,M=/^\s*(linear|radial|conic)-gradient\s*\(/i,$=/^([a-zA-Z$_][\w$]*)\((-?\d*\.?\d+)([a-zA-Z%]*)\)$/,F=/^(-?\d+(\.\d+)?)([a-zA-Z%]*)$/,k=/(\s+|[(),])/g;function S(t){const n=t.trim().toLowerCase();if(w.test(n)||void 0!==g[n])return!0;if(x.test(n)){const t=n.includes("%"),i=[...n.matchAll(b)].map((t=>+t[0])),[s,e,o,r=1]=i;return t?!(0>s||s>100||0>e||e>100||0>o||o>100||0>r||r>1):!(0>s||s>255||0>e||e>255||0>o||o>255||0>r||r>1)}if(O.test(n)){const t=[...n.matchAll(b)].map((t=>+t[0])),[i,s,e]=t;return!(0>i||i>360||0>s||s>100||0>e||e>100)}if(j.test(n)){const t=[...n.matchAll(b)].map((t=>+t[0])),[i,s,e,o]=t;return!(0>i||i>360||0>s||s>100||0>e||e>100||0>o||o>1)}return!1}function A(t){let n=t.trim().toLowerCase();if(g[n]&&(n=g[n]),w.test(n)){let t=n.slice(1);3===t.length?t=t[0]+t[0]+t[1]+t[1]+t[2]+t[2]:4===t.length&&(t=t[0]+t[0]+t[1]+t[1]+t[2]+t[2]+t[3]+t[3]);const i=8===t.length,s=parseInt(t,16);return[s>>(i?24:16)&255,s>>(i?16:8)&255,s>>(i?8:0)&255,i?(255&s)/255:1]}if(x.test(n)){const t=n.includes("%"),i=[...n.matchAll(b)].map((t=>+t[0]));let[s,e,o,r=1]=i;return t&&(s=Math.round(s/100*255),e=Math.round(e/100*255),o=Math.round(o/100*255)),[s,e,o,r]}const i=n.match(/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)%\s*,\s*(\d+(?:\.\d+)?)%\s*(?:,\s*(\d+(?:\.\d+)?))?\s*\)/i);if(i){const t=parseFloat(i[1])%360,n=parseFloat(i[2])/100,s=parseFloat(i[3])/100,e=void 0!==i[4]?parseFloat(i[4]):1,[o,r,a]=function(t,n,i){const s=(1-Math.abs(2*i-1))*n,e=s*(1-Math.abs(t/60%2-1)),o=i-s/2;let r=0,a=0,c=0;return t>=0&&60>t?[r,a,c]=[s,e,0]:t>=60&&120>t?[r,a,c]=[e,s,0]:t>=120&&180>t?[r,a,c]=[0,s,e]:t>=180&&240>t?[r,a,c]=[0,e,s]:t>=240&&300>t?[r,a,c]=[e,0,s]:t>=300&&360>t&&([r,a,c]=[s,0,e]),[Math.round(255*(r+o)),Math.round(255*(a+o)),Math.round(255*(c+o))]}(t,n,s);return[o,r,a,e]}throw Error("Unrecognized CSS color: "+t)}function E(t){return t.toFixed(3).replace(/\.?0+$/,"")}function C(t){if(!t.includes(","))return!1;if(!t.includes("(")&&!t.includes(")"))return!0;if(/\)\s*,/.test(t))return!0;let n=0;for(let i=0;i<t.length;i++)if("("===t[i])n++;else if(")"===t[i])n--;else if(","===t[i]&&0===n)return!0;return!1}function I(t){if(!t.includes(","))return[t];const n=[];let i="",s=0;for(let e=0;e<t.length;e++){const o=t[e];"("===o?(s++,i+=o):")"===o?(s--,i+=o):","===o&&0===s?(n.push(i.trim()),i=""):i+=o}return i.trim()&&n.push(i.trim()),n.length>0?n:[t]}function T(t){const n=t.match(M);if(!n)return null;const i=n[1].toLowerCase(),s=n[0].length;let e=1,o=s;for(let r=s;r<t.length&&e>0;r++)if("("===t[r]?e++:")"===t[r]&&e--,0===e){o=r;break}return 0!==e?null:{type:i,content:t.substring(s,o)}}function q(t){let n=0,i=-1;for(let s=0;s<t.length;s++)if("("===t[s])n++;else if(")"===t[s])n--;else if(","===t[s]&&0===n){i=s;break}return-1===i?{firstParam:"",colorStops:t.trim()}:{firstParam:t.substring(0,i).trim(),colorStops:t.substring(i+1).trim()}}function L(t){return y.some((n=>t.toLowerCase().includes(n)))}function Y(t,n){if(!L(t)||!L(n))return!1;const i=t.split(k).filter((t=>""!==t)),s=n.split(k).filter((t=>""!==t));if(i.length!==s.length)return!0;const e=t.toLowerCase().includes("matrix"),o=n.toLowerCase().includes("matrix"),r=["translate","rotate","scale","skew"].some((n=>t.toLowerCase().includes(n))),a=["translate","rotate","scale","skew"].some((t=>n.toLowerCase().includes(t)));return e&&a||o&&r}function z(t,n,i){const s=T(t),e=T(n);return s&&e?function(t,n,i){const s=m>i?t.type:n.type,e=q(t.content),o=q(n.content);let r;if(t.type===n.type)if(e.firstParam&&o.firstParam)try{r=D(e.firstParam,o.firstParam,i)}catch(c){r=m>i?e.firstParam:o.firstParam}else r=m>i?e.firstParam:o.firstParam;else r=m>i?e.firstParam:o.firstParam;const a=D(e.colorStops,o.colorStops,i).replace(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/g,"rgba($1,$2,$3,1.000)");return`${s}-gradient(${r?`${r}, ${a}`:a})`}(s,e,i):(s||e||Y(t,n),m>i?t:n)}function H(t){const n=t.match(/^(\S+\s+)?(-?\d+(?:\.\d+)?)(px|rem|em|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)?\s+(-?\d+(?:\.\d+)?)(px|rem|em|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)?\s+(-?\d+(?:\.\d+)?)(px|rem|em|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)?(?:\s+(-?\d+(?:\.\d+)?)(px|rem|em|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)?)?\s+(.+)$/);if(n){const t=n[1]||"",i=[];return n[2]&&i.push({num:n[2],unit:n[3]||""}),n[4]&&i.push({num:n[4],unit:n[5]||""}),n[6]&&i.push({num:n[6],unit:n[7]||""}),n[8]&&i.push({num:n[8],unit:n[9]||""}),{keyword:t,values:i,color:n[10]}}return null}function N(t){const n=H(t);if(n){const{keyword:t,values:i,color:s}=n,e=i.map((t=>t.unit?"0"+t.unit:"0")),o=s.replace(/rgba?\([^)]+\)/gi,(t=>t.toLowerCase().includes("rgba")||t.toLowerCase().includes("rgb")?"rgba(0,0,0,0)":t));return`${t}${e.join(" ")} ${o}`}return""}function D(t,n,i){var s,e;if(!t||""===t.trim()){const s=N(n);return s?D(s,n,i):m>i?t||"":n}if(!n||""===n.trim()){const s=N(t);if(s){const n=D(t,s,Math.min(2*i,1));if(i>=.5){const t=2*(i-.5),s=n.match(/^(\S+\s+)?(-?\d+(?:\.\d+)?)(px|rem|em|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)?\s+(-?\d+(?:\.\d+)?)(px|rem|em|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)?\s+(-?\d+(?:\.\d+)?)(px|rem|em|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)?\s+(.+)$/);if(s){const n=Math.abs(parseFloat(s[2])),i=Math.abs(parseFloat(s[4])),e=Math.abs(parseFloat(s[6])),o=s[8].match(/rgba?\((\d+),\s*(\d+),\s*(\d+),\s*([-\d.]+)\)/),r=o?Math.abs(parseFloat(o[4])):0;if(.01>n&&.01>i&&.01>e&&.01>r&&t>.9)return""}return n}return n}return m>i?t:n||""}const o=t.match($),r=n.match($);if(o&&r&&o[1]===r[1]&&o[3]===r[3]){const t=parseFloat(o[2])+(parseFloat(r[2])-parseFloat(o[2]))*i;return`${o[1]}(${E(t)}${o[3]})`}if(S(t)&&S(n)){const[s,e,o,r]=A(t),[a,c,l,u]=A(n),h=Math.round(s+(a-s)*i),f=Math.round(e+(c-e)*i),d=Math.round(o+(l-o)*i),p=r+(u-r)*i,m=E(p);return 1>p?`rgba(${h},${f},${d},${m})`:`rgb(${h},${f},${d})`}const a=t.trim().match(M),c=n.trim().match(M);if(a||c)return z(t.trim(),n.trim(),i);if(Y(t,n))return z(t,n,i);if((t.includes(",")||n.includes(","))&&(C(t)||C(n)))return function(t,n,i){const s=I(t),e=I(n),o=Math.max(s.length,e.length),r=[];for(let a=0;o>a;a++){const t=s[a]||s[s.length-1]||s[0],n=e[a]||e[e.length-1]||e[0];r.push(D(t,n,i))}return r.join(", ")}(t,n,i);const l=t.trim().split(/\s+/),u=n.trim().split(/\s+/),h=null===(s=l[0])||void 0===s?void 0:s.toLowerCase(),f=null===(e=u[0])||void 0===e?void 0:e.toLowerCase(),d=h&&v.includes(h),p=f&&v.includes(f);if(d||p){const s=m>i?d?h:"":p?f:"",e=D(d?l.slice(1).join(" "):t,p?u.slice(1).join(" "):n,i);return s?`${s} ${e}`:e}const y=H(t),g=H(n);if(y&&g){const t=Math.max(y.values.length,g.values.length),n=(t,n)=>{if(!t)return null;const{keyword:i,values:s,color:e}=t;if(3===s.length&&4===n){const t=s[2].unit||"px";return{keyword:i,values:[...s,{num:"0",unit:t}],color:e}}return{keyword:i,values:s,color:e}},s=n(y,t),e=n(g,t);if(s&&e){const n=s.values.map(((t,n)=>{const s=e.values[n];if(!s)return t;if(t.unit===s.unit){const n=parseFloat(t.num);return{num:E(n+(parseFloat(s.num)-n)*i),unit:t.unit}}return m>i?t:s}));let o=s.color;if(s.color!==e.color)try{o=D(s.color,e.color,i)}catch(x){o=m>i?s.color:e.color}return`${m>i?s.keyword||e.keyword:e.keyword||s.keyword}${(4===t?n:n.slice(0,3)).map((t=>t.unit?`${t.num}${t.unit}`:t.num)).join(" ")} ${o}`}}const b=t.split(k).filter((t=>""!==t)),w=n.split(k).filter((t=>""!==t));if(b.length!==w.length){if(function(t,n){const i=t.match(M),s=n.match(M);return!(!i||!s)||Y(t,n)}(t,n))return z(t,n,i);throw Error(`interpolate: template mismatch:\n "${t}"\n vs "${n}"`)}return b.map(((t,n)=>{const s=w[n];if(t===s&&/\s+/.test(t))return()=>t;const e=t.match(F),o=s.match(F);if(e&&o&&e[3]===o[3]){const t=parseFloat(e[1])+(parseFloat(o[1])-parseFloat(e[1]))*i;return()=>`${E(t)}${e[3]}`}if(S(t)&&S(s))return()=>D(t,s,i);if(t===s)return()=>t;if(/^[a-zA-Z-]+$/.test(t)&&/^[a-zA-Z-]+$/.test(s)&&!(n+1<b.length&&"("===b[n+1]||n+1<w.length&&"("===w[n+1]))return()=>m>i?t:s;throw Error(`interpolate: cannot interpolate tokens "${t}" vs "${s}"`)})).map((t=>t())).join("")}function X(t,n,i){var s,e,o,r;const a=t.length;if(2>a||n.length!==a)throw Error("interpolate: inRange and outRange must be arrays of the same length >= 2");const c=null!==(e=null!==(s=null==i?void 0:i.extrapolateLeft)&&void 0!==s?s:null==i?void 0:i.extrapolate)&&void 0!==e?e:"extend",l=null!==(r=null!==(o=null==i?void 0:i.extrapolateRight)&&void 0!==o?o:null==i?void 0:i.extrapolate)&&void 0!==r?r:"extend",u=n.map((t=>"string"==typeof t?function(t){const n=Object.keys(g).map((t=>t.replace(/[-\\^$*+?.()|[\]{}]/g,"\\$&"))).join("|"),i=RegExp(`(#(?:[0-9A-Fa-f]{3,4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})\\b|rgba?\\([^)]*\\)|hsla?\\([^)]*\\)|\\b(?:${n})\\b)`,"gi");return t.replace(i,(t=>{try{const[n,i,s,e]=A(t);return`rgba(${Math.round(n)},${Math.round(i)},${Math.round(s)},${parseFloat(e.toFixed(3)).toString()})`}catch(s){return t}}))}(t):t));return n=>{let s=n;n<t[0]&&"clamp"===c?s=t[0]:n>t[a-1]&&"clamp"===l&&(s=t[a-1]);let e=0;if(s>t[0])if(s<t[a-1]){for(let i=0;a-1>i;i++)if(s>=t[i]&&s<=t[i+1]){e=i;break}}else e=a-2;else e=0;const o=t[e];let r=(s-o)/(t[e+1]-o);(null==i?void 0:i.easing)&&(r=i.easing(r));const h=u[e],f=u[e+1];return"number"==typeof h&&"number"==typeof f?h+(f-h)*r:D(h+"",f+"",r)}}class P{constructor(t){this.subscribers=new Set,this.t=t,this.i=t}get current(){return this.t}get initial(){return this.i}set(t){var n;null===(n=this.controller)||void 0===n||n.cancel(),this.controller=void 0,this.o(t)}o(t){if(t!==this.t){this.t=t;for(const n of this.subscribers)n(t)}}subscribe(t){return this.subscribers.add(t),t(this.t),()=>{this.subscribers.delete(t)}}reset(){this.set(this.i)}destroy(){var t;this.subscribers.clear(),null===(t=this.controller)||void 0===t||t.cancel(),this.controller=void 0}to(t,n,i){if("function"==typeof t){const n=t,i=new P(n(this.t));return this.subscribe((t=>i.set(n(t)))),i}const s=X(t,n,i),e=new P(s(this.current));return this.subscribe((t=>e.set(s(t)))),e}setAnimationController(t){var n;null===(n=this.controller)||void 0===n||n.cancel(),this.controller=t}getAnimationController(){return this.controller}}function R(t){return t instanceof P}const U=new Set(["borderImageOutset","borderImageSlice","borderImageWidth","fontWeight","lineHeight","opacity","orphans","tabSize","widows","zIndex","zoom","fillOpacity","floodOpacity","stopOpacity","strokeDasharray","strokeDashoffset","strokeMiterlimit","strokeOpacity","strokeWidth","animationIterationCount","boxFlex","boxFlexGroup","boxOrdinalGroup","columnCount","flex","flexGrow","flexPositive","flexShrink","flexNegative","flexOrder","gridRow","gridColumn","order","lineClamp"]),V=["translateX","translateY","translateZ","rotate","rotateX","rotateY","rotateZ","scale","scaleX","scaleY","skewX","skewY","perspective"];function W(t,n,i){const s="number"!=typeof i||U.has(n)?i+"":i+"px";t.style[n]=s}function Z(t,n){const i=n&&"function"==typeof n.subscribe?n.current:n;if(Array.isArray(i))return`${t}(${i.join(",")})`;const s=i+"",{value:e,unit:o}=function(t){var n,i,s,e;return{value:+(null!==(i=null===(n=t.match(/-?\d+(\.\d+)?/))||void 0===n?void 0:n[0])&&void 0!==i?i:"0"),unit:null!==(e=null===(s=t.match(/px|rem|em|ex|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax/))||void 0===s?void 0:s[0])&&void 0!==e?e:""}}(s),r=o||function(t){return"perspective"===t||t.startsWith("translate")?"px":t.startsWith("rotate")||t.startsWith("skew")?"deg":""}(t);return`${t}(${e}${r})`}function G(t){return V.includes(t)}function _(t,n){const i=[];for(const[s,e]of Object.entries(n))R(e)?i.push(e.subscribe((n=>W(t,s,n)))):W(t,s,e);return i}function B(t,n){const i=[];for(const[s,e]of Object.entries(n)){const n=n=>{n?t.setAttribute(s,""):t.removeAttribute(s)},o=n=>{t.setAttribute(s,n+"")};R(e)?i.push(e.subscribe((i=>{"boolean"==typeof i?n(i):"string"==typeof i||"number"==typeof i?o(i):t.removeAttribute(s)}))):"boolean"==typeof e?n(e):("string"==typeof e||"number"==typeof e)&&o(e)}return i}function J(t,n){return function(t,n){const i=Object.keys(n).filter(G),s=i.length>0,e=()=>{if(s){const s=i.map((t=>Z(t,n[t])));t.style.transform=s.join(" ")}else"string"==typeof n.transform&&(t.style.transform=n.transform)};e();const o=[];if(s)for(const r of i){const t=n[r];t&&"function"==typeof t.subscribe&&o.push(t.subscribe(e))}return o}(t,n)}function K(t,n){return()=>{const i=Object.keys(n).filter(G);if(i.length>0){const s=i.map((t=>Z(t,n[t])));t.style.transform=s.join(" ")}}}function Q(t,n,i){return Math.min(Math.max(t,n),i)}function tt(t,n,i){return 0===n||Infinity===Math.abs(n)?function(t,n){return Math.pow(t,5*n)}(t,i):t*n*i/(n+i*t)}function nt(t,n,i,s=.15){return 0===s?Q(t,n,i):n>t?-tt(n-t,i-n,s)+n:t>i?+tt(t-i,i-n,s)+i:t}function it(t,n,i){const s=t+.2*n,e=t=>Math.abs(t-s),o=i.map(e),r=Math.min(...o);return i.reduce((function(t,n){return e(n)===r?n:t}))}function st(t,n,i){const s=t[n],e=t.length,o=n-i;if(o>0)return[...t.slice(0,i),s,...t.slice(i,n),...t.slice(n+1,e)];if(0>o){const o=i+1;return[...t.slice(0,n),...t.slice(n+1,o),s,...t.slice(o,e)]}return t}class et{constructor(t,n,i,s){this.value=t,this.velocity=n,this.deceleration=i,this.hooks=s,this.restSpeed=.01,this.isPaused=!1,this.isCancelled=!1,this.pausedAt=0,this.animate=t=>{var n,i,s,e,o,r;if(this.isCancelled||this.isPaused)return;const a=t-this.startTime,c=1-this.deceleration,l=this.velocity*Math.exp(-c*a);if(this.position=this.from+this.velocity/c*(1-Math.exp(-c*a)),this.clampBounds){const[t,n]=this.clampBounds;void 0!==this.elasticConstant?this.position=nt(this.position,t,n,this.elasticConstant):this.position<t?this.position=t:this.position>n&&(this.position=n)}this.value.o(this.position),null===(i=(n=this.hooks).onChange)||void 0===i||i.call(n,this.position),Math.abs(l)>this.restSpeed?this.frameId=requestAnimationFrame(this.animate):(cancelAnimationFrame(this.frameId),null===(e=(s=this.hooks).onChange)||void 0===e||e.call(s,this.position),null===(r=(o=this.hooks).onComplete)||void 0===r||r.call(o))},this.clampBounds=s.clamp,!0===s.elastic?this.elasticConstant=.15:"number"==typeof s.elastic&&(this.elasticConstant=s.elastic)}start(){var t,n;const i=this.value.getAnimationController();i instanceof et&&(this.velocity=i.velocity,this.deceleration=i.deceleration),null===(n=(t=this.hooks).onStart)||void 0===n||n.call(t),this.value.setAnimationController(this),this.isPaused=!1,this.isCancelled=!1,this.from=this.position=this.value.current,this.startTime=performance.now(),this.frameId=requestAnimationFrame(this.animate)}pause(){var t,n;this.isCancelled||this.isPaused||(this.isPaused=!0,this.pausedAt=performance.now(),cancelAnimationFrame(this.frameId),null===(n=(t=this.hooks).onPause)||void 0===n||n.call(t))}resume(){var t,n;if(this.isCancelled||!this.isPaused)return;const i=performance.now();this.startTime+=i-this.pausedAt,this.isPaused=!1,null===(n=(t=this.hooks).onResume)||void 0===n||n.call(t),this.frameId=requestAnimationFrame(this.animate)}cancel(){this.isCancelled=!0,cancelAnimationFrame(this.frameId)}reset(){this.cancel(),this.isPaused=!1,this.pausedAt=0,this.startTime=0}setOnComplete(t){this.hooks.onComplete=t}}function ot(t,n,i={}){const{decay:s=.998}=i,e=p(i,["decay"]);return new et(t,n,s,e)}class rt{constructor(t,n,i,s,e,o){this.value=t,this.target=n,this.stiffness=i,this.damping=s,this.mass=e,this.hooks=o,this.velocity=0,this.restDisplacement=.001,this.restSpeed=.001,this.isPaused=!1,this.isCancelled=!1,this.animate=()=>{var t,n,i,s,e,o;if(this.isCancelled||this.isPaused)return;const r=Date.now(),a=Math.min(r-this.startTime,64);this.startTime=r;const c=this.damping,l=this.mass,u=this.stiffness,h=-this.velocity,f=this.target-this.position,d=c/(2*Math.sqrt(u*l)),p=Math.sqrt(u/l),m=p*Math.sqrt(1-d**2),v=a/1e3,y=Math.sin(m*v),g=Math.cos(m*v),b=Math.exp(-d*p*v),w=b*(y*((h+d*p*f)/m)+f*g),x=this.target-w,O=d*p*w-b*(g*(h+d*p*f)-m*f*y),j=Math.exp(-p*v),M=this.target-j*(f+(h+p*f)*v),$=j*(h*(v*p-1)+v*f*p*p);this.value.o(this.position),null===(n=(t=this.hooks).onChange)||void 0===n||n.call(t,this.position);const F=Math.abs(this.velocity)<this.restSpeed,k=0===this.stiffness||Math.abs(this.target-this.position)<this.restDisplacement;if(1>d?(this.position=x,this.velocity=O):(this.position=M,this.velocity=$),F&&k)return this.velocity=0,this.position=this.target,this.value.o(this.position),null===(s=(i=this.hooks).onChange)||void 0===s||s.call(i,this.position),void(null===(o=(e=this.hooks).onComplete)||void 0===o||o.call(e));this.frameId=requestAnimationFrame(this.animate)},this.explicitFrom=o.from}start(){var t,n;if(void 0!==this.explicitFrom)this.position=this.startPosition=this.explicitFrom,this.value.o(this.explicitFrom),this.velocity=0,this.startTime=Date.now();else{const t=this.value.getAnimationController();t instanceof rt?(this.position=t.position,this.velocity=t.velocity,this.startTime=t.startTime):(this.position=this.startPosition=this.value.current,this.velocity=0,this.startTime=Date.now())}null===(n=(t=this.hooks).onStart)||void 0===n||n.call(t),this.value.setAnimationController(this),this.isPaused=!1,this.isCancelled=!1,this.frameId=requestAnimationFrame(this.animate)}pause(){var t,n;this.isCancelled||this.isPaused||(this.isPaused=!0,cancelAnimationFrame(this.frameId),null===(n=(t=this.hooks).onPause)||void 0===n||n.call(t))}resume(){var t,n;!this.isCancelled&&this.isPaused&&(this.isPaused=!1,null===(n=(t=this.hooks).onResume)||void 0===n||n.call(t),this.frameId=requestAnimationFrame(this.animate))}cancel(){this.isCancelled=!0,cancelAnimationFrame(this.frameId)}reset(){this.cancel(),this.isPaused=!1,this.velocity=0,this.position=this.startPosition,this.startTime=Date.now()}setOnComplete(t){this.hooks.onComplete=t}}function at(t,n,i={}){return function(t,n,i,s){if("number"==typeof t.current&&"number"==typeof n)return s(t,n,i);if("string"==typeof t.current&&"string"==typeof n)try{const e=new P(0),o=e.to([0,1],[t.current,n]),r=s(e,1,i);return t.setAnimationController(r),e.subscribe((()=>{t.getAnimationController()===r&&t.o(o.current)})),r}catch(e){throw Error(`[spring] Cannot animate from "${t.current}" to "${n}": ${e.message}`)}throw Error(`[spring] Unsupported type: ${typeof t.current} → ${typeof n}`)}(t,n,i,((t,n,i)=>{const{stiffness:s=170,damping:e=14,mass:o=1,from:r}=i,a=p(i,["stiffness","damping","mass","from"]);return new rt(t,n,s,e,o,Object.assign(Object.assign({},a),{from:r}))}))}var ct=.1,lt="function"==typeof Float32Array;function ut(t,n){return 1-3*n+3*t}function ht(t,n){return 3*n-6*t}function ft(t){return 3*t}function dt(t,n,i){return((ut(n,i)*t+ht(n,i))*t+ft(n))*t}function pt(t,n,i){return 3*ut(n,i)*t*t+2*ht(n,i)*t+ft(n)}function mt(t){return t}class vt{static step0(t){return t>0?1:0}static step1(t){return 1>t?0:1}static linear(t){return t}static ease(t){return yt(t)}static quad(t){return t*t}static cubic(t){return t*t*t}static poly(t){return n=>Math.pow(n,t)}static sin(t){return 1-Math.cos(t*Math.PI/2)}static circle(t){return 1-Math.sqrt(1-t*t)}static exp(t){return Math.pow(2,10*(t-1))}static elastic(t=1){var n=t*Math.PI;return t=>1-Math.pow(Math.cos(t*Math.PI/2),3)*Math.cos(t*n)}static back(t){return void 0===t&&(t=1.70158),n=>n*n*((t+1)*n-t)}static bounce(t){return 1/2.75>t?7.5625*t*t:2/2.75>t?7.5625*(t-=1.5/2.75)*t+.75:2.5/2.75>t?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375}static bezier(t,n,i,s){return function(t,n,i,s){if(0>t||t>1||0>i||i>1)throw Error("bezier x values must be in [0, 1] range");if(t===n&&i===s)return mt;for(var e=lt?new Float32Array(11):[,,,,,,,,,,,],o=0;11>o;++o)e[o]=dt(o*ct,t,i);return function(o){return 0===o||1===o?o:dt(function(n){for(var s=0,o=1;10!==o&&e[o]<=n;++o)s+=ct;--o;var r=s+(n-e[o])/(e[o+1]-e[o])*ct,a=pt(r,t,i);return.001>a?0===a?r:function(t,n,i,s,e){var o,r,a=0;do{(o=dt(r=n+(i-n)/2,s,e)-t)>0?i=r:n=r}while(Math.abs(o)>1e-7&&10>++a);return r}(n,s,s+ct,t,i):function(t,n,i,s){for(var e=0;4>e;++e){var o=pt(n,i,s);if(0===o)return n;n-=(dt(n,i,s)-t)/o}return n}(n,r,t,i)}(o),n,s)}}(t,n,i,s)}static in(t){return t}static out(t){return n=>1-t(1-n)}static inOut(t){return n=>.5>n?t(2*n)/2:1-t(2*(1-n))/2}}var yt=vt.bezier(.42,0,1,1);class gt{constructor(t,n,i=300,s=vt.linear,e){this.value=t,this.target=n,this.duration=i,this.easing=s,this.hooks=e,this.isPaused=!1,this.isCancelled=!1,this.pausedAt=null,this.elapsedBeforePause=0,this.animate=t=>{var n,i,s,e,o,r;if(this.isCancelled||this.isPaused)return;let a=(this.elapsedBeforePause+(t-this.startTime))/this.duration;Number.isFinite(a)||(a=Infinity===a?1:0);const c=Math.min(1,Math.max(0,a));1>c?(this.position=this.fromValue+(this.target-this.fromValue)*this.easing(c),this.value.o(this.position),null===(i=(n=this.hooks).onChange)||void 0===i||i.call(n,this.position),this.frameId=requestAnimationFrame(this.animate)):(this.position=this.target,this.value.o(this.position),null===(e=(s=this.hooks).onChange)||void 0===e||e.call(s,this.position),null===(r=(o=this.hooks).onComplete)||void 0===r||r.call(o))},this.explicitFrom=e.from}start(){var t,n;if(void 0!==this.explicitFrom)this.fromValue=this.position=this.explicitFrom,this.value.o(this.explicitFrom),this.startTime=performance.now();else{const t=this.value.getAnimationController();t instanceof gt&&!t.isCancelled&&t.target===this.target&&t.startTime?(this.fromValue=t.fromValue,this.startTime=t.startTime):(this.fromValue=this.position=this.value.current,this.startTime=performance.now())}null===(n=(t=this.hooks).onStart)||void 0===n||n.call(t),this.value.setAnimationController(this),this.isPaused=!1,this.isCancelled=!1,this.pausedAt=null,this.elapsedBeforePause=0,this.frameId=requestAnimationFrame(this.animate)}pause(){var t,n;this.isCancelled||this.isPaused||(this.isPaused=!0,this.pausedAt=performance.now(),this.elapsedBeforePause+=this.pausedAt-this.startTime,this.startTime=0,cancelAnimationFrame(this.frameId),null===(n=(t=this.hooks).onPause)||void 0===n||n.call(t))}resume(){var t,n;!this.isCancelled&&this.isPaused&&(this.isPaused=!1,null===(n=(t=this.hooks).onResume)||void 0===n||n.call(t),this.startTime=performance.now(),this.pausedAt=null,this.frameId=requestAnimationFrame(this.animate))}cancel(){this.isCancelled=!0,cancelAnimationFrame(this.frameId)}reset(){this.cancel(),this.isPaused=!1,this.pausedAt=null,this.elapsedBeforePause=0,cancelAnimationFrame(this.frameId)}setOnComplete(t){this.hooks.onComplete=t}}function bt(t,n,i={}){return function(t,n,i,s){if("number"==typeof t.current&&"number"==typeof n)return s(t,n,i);if("string"==typeof t.current&&"string"==typeof n)try{const e=new P(0),o=e.to([0,1],[t.current,n]),r=s(e,1,i);return t.setAnimationController(r),e.subscribe((()=>{t.getAnimationController()===r&&t.o(o.current)})),r}catch(e){throw Error(`[timing] Cannot animate from "${t.current}" to "${n}": ${e.message}`)}throw Error(`[timing] Unsupported type: ${typeof t.current} → ${typeof n}`)}(t,n,i,((t,n,i)=>{const{duration:s=300,easing:e=vt.linear,from:o}=i,r=p(i,["duration","easing","from"]);return new gt(t,n,s,e,Object.assign(Object.assign({},r),{from:o}))}))}class wt{constructor(t,n={}){this.controllers=t,this.hooks=n,this.completedCount=0,this.isPaused=!1,this.isCancelled=!1,this.originalCompletes=t.map((t=>{var n;return null===(n=null==t?void 0:t.hooks)||void 0===n?void 0:n.onComplete}))}start(){var t,n;this.completedCount=0,this.isPaused=!1,this.isCancelled=!1,null===(n=(t=this.hooks).onStart)||void 0===n||n.call(t),this.controllers.forEach(((t,n)=>{var i;const s=this.originalCompletes[n];null===(i=t.setOnComplete)||void 0===i||i.call(t,(()=>{null==s||s(),this.handleComplete()})),t.start()}))}handleComplete(){var t,n,i;this.isCancelled||(this.completedCount+=1,this.completedCount===this.controllers.length&&(this.completedCount=0,null===(n=(t=this.hooks).onComplete)||void 0===n||n.call(t),null===(i=this.onComplete)||void 0===i||i.call(this)))}pause(){var t,n;this.isCancelled||this.isPaused||(this.isPaused=!0,null===(n=(t=this.hooks).onPause)||void 0===n||n.call(t),this.controllers.forEach((t=>t.pause())))}resume(){var t,n;!this.isCancelled&&this.isPaused&&(this.isPaused=!1,null===(n=(t=this.hooks).onResume)||void 0===n||n.call(t),this.controllers.forEach((t=>t.resume())))}cancel(){this.isCancelled||(this.isCancelled=!0,this.controllers.forEach((t=>t.cancel())))}reset(){this.isCancelled=!1,this.isPaused=!1,this.completedCount=0,this.controllers.forEach((t=>{var n;return null===(n=t.reset)||void 0===n?void 0:n.call(t)}))}setOnComplete(t){this.onComplete=t}}class xt{constructor(t,n={}){this.controllers=t,this.hooks=n,this.index=0,this.isPaused=!1,this.isCancelled=!1,this.current=null,this.originalCompletes=t.map((t=>{var n;return null===(n=null==t?void 0:t.hooks)||void 0===n?void 0:n.onComplete}))}runNext(){var t,n,i,s;if(this.isCancelled||this.isPaused)return;const e=this.index++,o=this.controllers[e];if(!o)return null===(t=this.onComplete)||void 0===t||t.call(this),void(null===(i=(n=this.hooks).onComplete)||void 0===i||i.call(n));this.current=o;const r=this.originalCompletes[e];null===(s=o.setOnComplete)||void 0===s||s.call(o,(()=>{null==r||r(),this.runNext()})),o.start()}start(){var t,n;this.index=0,this.isPaused=!1,this.isCancelled=!1,null===(n=(t=this.hooks).onStart)||void 0===n||n.call(t),this.runNext()}pause(){var t,n,i;this.isCancelled||(this.isPaused=!0,null===(t=this.current)||void 0===t||t.pause(),null===(i=(n=this.hooks).onPause)||void 0===i||i.call(n))}resume(){var t,n,i;!this.isCancelled&&this.isPaused&&(this.isPaused=!1,null===(t=this.current)||void 0===t||t.resume(),null===(i=(n=this.hooks).onResume)||void 0===i||i.call(n))}cancel(){var t;this.isCancelled=!0,null===(t=this.current)||void 0===t||t.cancel()}reset(){this.isCancelled=!1,this.isPaused=!1,this.index=0,this.controllers.forEach((t=>{var n;return null===(n=t.reset)||void 0===n?void 0:n.call(t)}))}setOnComplete(t){this.onComplete=t}}class Ot{constructor(t,n,i={}){var s;this.controller=t,this.iterations=n,this.hooks=i,this.count=0,this.isCancelled=!1,this.isPaused=!1,this.handleIterationComplete=()=>{var t,n,i,s,e,o;this.count++,null===(t=this.originalComplete)||void 0===t||t.call(this),this.count<this.iterations?(null===(i=(n=this.controller).reset)||void 0===i||i.call(n),this.runIteration()):(null===(s=this.onComplete)||void 0===s||s.call(this),null===(o=(e=this.hooks).onComplete)||void 0===o||o.call(e))},this.originalComplete=null===(s=null==t?void 0:t.hooks)||void 0===s?void 0:s.onComplete}runIteration(){var t,n;this.isCancelled||this.isPaused||(null===(n=(t=this.controller).setOnComplete)||void 0===n||n.call(t,this.handleIterationComplete),this.controller.start())}start(){var t,n;this.isCancelled=!1,this.isPaused=!1,this.count=0,this.controller.reset(),null===(n=(t=this.hooks).onStart)||void 0===n||n.call(t),this.runIteration()}pause(){var t,n;this.isPaused=!0,this.controller.pause(),null===(n=(t=this.hooks).onPause)||void 0===n||n.call(t)}resume(){var t,n;!this.isCancelled&&this.isPaused&&(this.isPaused=!1,this.controller.resume(),null===(n=(t=this.hooks).onResume)||void 0===n||n.call(t))}cancel(){this.isCancelled=!0,this.controller.cancel()}reset(){var t,n;this.isCancelled=!1,this.isPaused=!1,this.count=0,null===(n=(t=this.controller).reset)||void 0===n||n.call(t)}setOnComplete(t){this.onComplete=t}}class jt{constructor(t){this.duration=t}start(){this.timerId=window.setTimeout((()=>{var t;null===(t=this.onComplete)||void 0===t||t.call(this)}),this.duration)}pause(){}resume(){}cancel(){this.timerId&&clearTimeout(this.timerId)}reset(){this.cancel()}setOnComplete(t){this.onComplete=t}}function Mt(t,n={}){return new wt(t,n)}function $t(t,n={}){return new xt(t,n)}function Ft(t,n,i={}){return new Ot(t,n,i)}function kt(t){return new jt(t)}function St(t={},n){if(n)return t;const{onStart:i,onChange:s,onComplete:e}=t;return p(t,["onStart","onChange","onComplete"])}function At(t){return"object"==typeof t&&null!==t&&"type"in t&&"string"==typeof t.type}function Et(t,{type:n,to:i,options:s={}}){var e,o,r,a,c,l,u;switch(n){case"spring":return at(t,i,s);case"timing":return bt(t,i,s);case"decay":return ot(t,null!==(e=s.velocity)&&void 0!==e?e:0,s);case"delay":return kt(null!==(o=s.delay)&&void 0!==o?o:0);case"sequence":return $t((null!==(r=s.animations)&&void 0!==r?r:[]).map((n=>Et(t,n))),s);case"loop":{const n=s.animation;if(!n)return{start(){},pause(){},resume(){},cancel(){},reset(){}};let i;const e=t.initial;if("sequence"===n.type){const s=(null!==(c=null===(a=n.options)||void 0===a?void 0:a.animations)&&void 0!==c?c:[]).map(((n,i)=>{var s;if(0===i&&("spring"===n.type||"timing"===n.type)){const i=null===(s=n.options)||void 0===s?void 0:s.from;return Et(t,Object.assign(Object.assign({},n),{options:Object.assign(Object.assign({},n.options),{from:null!=i?i:e})}))}return Et(t,n)}));i=$t(s,n.options)}else if("spring"===n.type||"timing"===n.type){const s=null===(l=n.options)||void 0===l?void 0:l.from;i=Et(t,Object.assign(Object.assign({},n),{options:Object.assign(Object.assign({},n.options),{from:null!=s?s:e})}))}else i=Et(t,n);return Ft(i,null!==(u=s.iterations)&&void 0!==u?u:0,s)}default:return{start(){},pause(){},resume(){},cancel(){},reset(){}}}}function Ct(t,n){return Mt(Object.entries(t).filter((([t])=>"decay"===n.type||"delay"===n.type||void 0!==n.to[t])).map((([t,i],s)=>Et(i,{type:n.type,to:"decay"===n.type||"delay"===n.type?n.to:n.to[t],options:St(n.options,0===s)}))))}const It=t(null);function Tt(){const t=l(It);return t?[!t.isExiting,t.onExitComplete]:[!0,()=>{}]}function qt(){const t=l(It);return!t||!t.isExiting}function Lt({children:t,isExiting:n,mode:s}){const[e,o]=i(!1);return r((()=>{n&&!e&&o(!0)}),[n,e]),"popLayout"===s&&n?f("div",{style:{position:"absolute"},children:t}):t}function Yt({children:t,initial:l=!0,onExitComplete:u,mode:h="sync"}){const p=n(!0),[m,v]=i((()=>new Map)),y=n(0),g=s((()=>{const n=[];return e.forEach(t,(t=>{var i;if(o(t)){const s=null!==(i=t.key)&&void 0!==i?i:"default";n.push({key:s,element:t})}})),n}),[t]),b=s((()=>new Set(g.map((t=>t.key)))),[g]);r((()=>{v((t=>{const n=new Map,i=new Map(g.map((({key:t,element:n})=>[t,n])));for(const[s,e]of t)i.has(s)?(e.isExiting&&y.current--,n.set(s,{key:s,element:i.get(s),isExiting:!1})):(e.isExiting||y.current++,n.set(s,Object.assign(Object.assign({},e),{isExiting:!0})));for(const{key:s,element:e}of g)t.has(s)||n.set(s,{key:s,element:e,isExiting:!1});return n}))}),[g,b]),a((()=>{p.current&&(p.current=!1)}),[]);const w=c((t=>{v((n=>{const i=new Map(n);return i.delete(t),i})),y.current--,0===y.current&&u&&u()}),[u]),x=s((()=>{const t=[],n=Array.from(m.values()).some((t=>t.isExiting)),i="wait"===h&&n;for(const[s,e]of m){if(i&&!e.isExiting)continue;const n={isInitialMount:p.current&&l,isExiting:e.isExiting,onExitComplete:()=>w(s)};t.push(f(It.Provider,{value:n,children:f(Lt,{isExiting:e.isExiting,mode:h,children:e.element})},s))}return t}),[m,l,h,w]);return f(d,{children:x})}function zt(t){return"opacity"===t||"scale"===t||"scaleX"===t||"scaleY"===t?1:t.startsWith("translate")||t.startsWith("rotate")?0:["boxShadow","textShadow","background","backgroundImage","backgroundPosition","backgroundSize","border","borderColor","borderImage","borderRadius","color","fill","stroke","filter","backdropFilter","clipPath","mask","maskImage"].includes(t)?"":0}function Ht(t){return/\d+(px|rem|em|ex|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)$/i.test(t)}function Nt(t,n,i,s){const e=function(t){return!!G(t)||"opacity"===t||"zIndex"===t}(t),o=function(t,n){if(!t||!(n in t))return null;const i=t[n];if(null==i)return null;if(i&&"object"==typeof i&&"subscribe"in i)return null;if("number"==typeof i)return i;if("string"==typeof i){if([/\s/,/rgba?\(/,/hsla?\(/,/#[0-9a-fA-F]/,/linear-gradient|radial-gradient|conic-gradient/,/calc\(|var\(/].some((t=>t.test(i))))return i;if(/\d+(px|rem|em|ex|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)$/i.test(i))return i;const t=parseFloat(i);return isNaN(t)?i:t}return null}(n,t);if(null!==o){if(e&&"string"==typeof o){const n=parseFloat(o);return isNaN(n)?zt(t):n}return o}const r=i.style[t];if(r){if(e){const n=parseFloat(r);return isNaN(n)?zt(t):n}if(Ht(r))return r;const n=parseFloat(r);return isNaN(n)?r:n}const a=s.getPropertyValue(t.replace(/([A-Z])/g,"-$1").toLowerCase());if(a){const n=a.trim();if(e){const i=parseFloat(n);return isNaN(i)?zt(t):i}if("none"===n&&["boxShadow","textShadow","background","backgroundImage","backgroundPosition","backgroundSize","border","borderColor","borderImage","borderRadius","color","fill","stroke","filter","backdropFilter","clipPath","mask","maskImage"].includes(t))return"";if(Ht(n))return n;const i=parseFloat(n);return isNaN(i)?n:i}return zt(t)}function Dt(t,n,i){if(!t)return;const{node:s,style:e,computedStyle:o,animateValues:r,initialValues:a,stateControllers:c,cleanup:l}=i;c.forEach((t=>t.cancel())),c.length=0;const u=[];for(const[h,f]of Object.entries(t)){let t=r[h];if(t)if(h in a||(a[h]=n?t.current:Nt(h,e,s,o)),G(h)){const n=K(s,r);u.push(t.subscribe(n))}else u.push(t.subscribe((t=>{const n="number"!=typeof t||["opacity","zIndex","fontWeight","lineHeight"].includes(h)?t+"":t+"px";s.style[h]=n})));else{const n=Nt(h,e,s,o);if(t=new P(n),r[h]=t,a[h]=n,G(h)){const n=K(s,r);u.push(t.subscribe(n)),n()}else{const i=t=>{const n="number"!=typeof t||["opacity","zIndex","fontWeight","lineHeight"].includes(h)?t+"":t+"px";s.style[h]=n};u.push(t.subscribe(i)),i(n)}}const i="number"==typeof f||"string"==typeof f;if(n)if(i){const n=Et(t,{type:"spring",to:f,options:{}});c.push(n),n.start()}else{const n=Et(t,f);c.push(n),n.start()}else{const n=a[h];if(i){const i=Et(t,{type:"spring",to:n,options:{}});c.push(i),i.start()}else{const i=Et(t,{type:"spring",to:n,options:{}});c.push(i),i.start()}}}l.push(...u)}function Xt(...t){return n=>{for(const i of t)i&&("function"==typeof i?i(n):"current"in i&&(i.current=n))}}function Pt(t,n={}){const{root:s,rootMargin:e,threshold:o,once:r=!1}=n,[c,l]=i(!1);return a((()=>{const n=t.current;if(!n)return;const i=new IntersectionObserver((([t])=>{t.isIntersecting?(l(!0),r&&(i.unobserve(t.target),i.disconnect())):r||l(!1)}),{root:null!=s?s:null,rootMargin:e,threshold:o});return i.observe(n),()=>i.disconnect()}),[t,s,e,o,r]),c}function Rt(t){if(!t)return"";const n=t;return Object.keys(n).sort().map((t=>{const i=n[t];if("number"==typeof i||"string"==typeof i)return`${t}:${i}`;if("object"==typeof i&&null!==i&&"type"in i){const n=i;return`${t}:${n.type}:${JSON.stringify(n.to||"")}`}return t+":unknown"})).join("|")}function Ut(t){const i=u(((i,s)=>{const e=n(null),o=n(i),c=n(!1),u=n({}),f=n([]);o.current=i,function(t,i,s,e,o){const a=l(It),c=n([]),u=n([]),h=n(null),f=n(!1);r((()=>{var t;h.current=null!==(t=null==a?void 0:a.onExitComplete)&&void 0!==t?t:null})),r((()=>{var n;const{exit:r,style:l={}}=i.current,d=t.current,p=null!==(n=null==a?void 0:a.isExiting)&&void 0!==n&&n,m=f.current;if(f.current=p,p){if(!s.current&&r&&d)return!m&&p&&(s.current=!0,o.current.forEach((t=>t.cancel())),o.current=[],u.current.forEach((t=>t())),u.current=[],u.current=function(t){const{exitProp:n,animateValues:i,controllers:s,onExitComplete:e,node:o,style:r}=t;let a=0;const c=Object.keys(n).length,l=[],u=()=>{a++,c>a||e()},h=window.getComputedStyle(o);for(const m of Object.keys(n))if(!i[m]){const t=Nt(m,r,o,h);i[m]=new P(t)}const f=Object.assign({},r);for(const m of Object.keys(i))f[m]=i[m];const d={},p={};for(const[m,v]of Object.entries(f))v&&"object"==typeof v&&"subscribe"in v&&((G(m)?p:d)[m]=v);l.push(..._(o,d)),l.push(...J(o,p));for(const[m,v]of Object.entries(n)){const t=i[m];if(!t){u();continue}const n="number"==typeof v||"string"==typeof v?{type:"spring",to:v,options:{}}:v,e=Et(t,Object.assign(Object.assign({},n),{options:Object.assign(Object.assign({},n.options),{onComplete:()=>{var t,i;null===(i=null===(t=n.options)||void 0===t?void 0:t.onComplete)||void 0===i||i.call(t),u()}})}));s.push(e),e.start()}return l}({exitProp:r,animateValues:e.current,controllers:c.current,onExitComplete:()=>{s.current&&h.current&&(u.current.forEach((t=>t())),u.current=[],s.current=!1,h.current())},node:d,style:l})),()=>{s.current&&(c.current.forEach((t=>t.cancel())),c.current=[],u.current.forEach((t=>t())),u.current=[])}}else s.current&&(s.current=!1,c.current.forEach((t=>t.cancel())),c.current=[],u.current.forEach((t=>t())),u.current=[])}),[null==a?void 0:a.isExiting])}(e,o,c,u,f),function(t,i,s,e,o,a){const c=l(It),u=n([]),h=n(!1),f=n(""),d=n(!1);r((()=>{var n;const r=t.current,l=null!==(n=null==c?void 0:c.isExiting)&&void 0!==n&&n,m=d.current&&!l;if(d.current=l,!r||l||e.current)return;const v=i.current,{style:y={}}=v,g=p(v,["style"]),b=Rt(s),w=f.current!==b;if((!h.current||w||m)&&(a.current.forEach((t=>t.cancel())),a.current=[],s)){const t=window.getComputedStyle(r);if(0===Object.keys(o.current).length){const n={};for(const i of Object.keys(s)){const s=Nt(i,y,r,t);n[i]=new P(s)}o.current=n}!function(t,n,i){for(const[s,e]of Object.entries(t)){const t=n[s];if(!t)continue;const o=Et(t,"number"==typeof e||"string"==typeof e?{type:"spring",to:e,options:{}}:e);i.push(o),o.start()}}(s,o.current,a.current)}return u.current.forEach((t=>t())),u.current=function(t,n,i,s){const e=Object.assign({},n);for(const a of Object.keys(i))e[a]=i[a];const o={},r={};for(const[a,c]of Object.entries(e))(G(a)?r:o)[a]=c;return[..._(t,o),...J(t,e),...B(t,s)]}(r,y,o.current,g),f.current=b,h.current=!0,()=>{u.current.forEach((t=>t())),u.current=[]}}),[s,null==c?void 0:c.isExiting])}(e,o,i.animate,c,u,f),function(t,i,s,e,o,r){const c=n([]),l=n({}),u=n({isHovered:!1,isTapped:!1,isFocused:!1}),h=n(s),f=n(e),d=n(o);h.current=s,f.current=e,d.current=o;const p=(n,s)=>{if(!n)return;const e=t.current;if(!e)return;const o=window.getComputedStyle(e),{style:a={}}=i.current;Dt(n,s,{node:e,style:a,computedStyle:o,animateValues:r.current,initialValues:l.current,stateControllers:c.current,cleanup:[]})};a((()=>{const n=t.current;if(!n)return;const i=()=>{u.current.isHovered||(u.current.isHovered=!0,p(h.current,!0))},s=()=>{u.current.isHovered&&(u.current.isHovered=!1,p(h.current,!1))},e=()=>{u.current.isTapped||(u.current.isTapped=!0,p(f.current,!0))},o=()=>{u.current.isTapped&&(u.current.isTapped=!1,p(f.current,!1),u.current.isHovered&&h.current&&p(h.current,!0))},r=()=>{u.current.isTapped&&(u.current.isTapped=!1,p(f.current,!1),u.current.isHovered&&h.current&&p(h.current,!0))},a=()=>{u.current.isFocused||(u.current.isFocused=!0,p(d.current,!0))},l=()=>{u.current.isFocused&&(u.current.isFocused=!1,p(d.current,!1))};return h.current&&(n.addEventListener("mouseenter",i),n.addEventListener("mouseleave",s)),f.current&&(n.addEventListener("mousedown",e),n.addEventListener("mouseup",o),n.addEventListener("mouseleave",r),n.addEventListener("touchstart",e),n.addEventListener("touchend",o),n.addEventListener("touchcancel",r)),d.current&&function(t){return t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement||t instanceof HTMLSelectElement||t instanceof HTMLButtonElement||t instanceof HTMLAnchorElement||null!==t.getAttribute("tabindex")}(n)&&(n.addEventListener("focus",a),n.addEventListener("blur",l)),u.current.isHovered&&h.current&&p(h.current,!0),u.current.isTapped&&f.current&&p(f.current,!0),u.current.isFocused&&d.current&&p(d.current,!0),()=>{h.current&&(n.removeEventListener("mouseenter",i),n.removeEventListener("mouseleave",s)),f.current&&(n.removeEventListener("mousedown",e),n.removeEventListener("mouseup",o),n.removeEventListener("mouseleave",r),n.removeEventListener("touchstart",e),n.removeEventListener("touchend",o),n.removeEventListener("touchcancel",r)),d.current&&(n.removeEventListener("focus",a),n.removeEventListener("blur",l)),c.current.forEach((t=>t.cancel())),c.current=[]}}),[Rt(s),Rt(e),Rt(o)]),a((()=>()=>{c.current.forEach((t=>t.cancel())),c.current=[]}),[])}(e,o,i.hover,i.press,i.focus,u),function(t,i,s,e,o){const c=n([]),l=n({}),u=n([]),h=Pt(t,e||{}),f=n(!1);r((()=>{if(!s)return;const n=t.current;if(!n)return;if(f.current)return;const e=window.getComputedStyle(n),{style:r={}}=i.current;for(const t of Object.keys(s))if(!o.current[t]){const i=Nt(t,r,n,e),s=new P(i);if(o.current[t]=s,l.current[t]=i,G(t))K(n,o.current)();else{const s="number"!=typeof i||["opacity","zIndex","fontWeight","lineHeight"].includes(t)?i+"":i+"px";n.style[t]=s}}f.current=!0}),[s]),a((()=>{if(s)return(n=>{if(!s)return;const e=t.current;if(!e)return;u.current.forEach((t=>t())),u.current=[];const r=window.getComputedStyle(e),{style:a={}}=i.current,h={node:e,style:a,computedStyle:r,animateValues:o.current,initialValues:l.current,stateControllers:c.current,cleanup:u.current};Dt(s,n,h)})(h),()=>{c.current.forEach((t=>t.cancel())),c.current=[],u.current.forEach((t=>t())),u.current=[]}}),[h,s]),a((()=>()=>{c.current.forEach((t=>t.cancel())),c.current=[],u.current.forEach((t=>t())),u.current=[]}),[])}(e,o,i.view,i.viewOptions,u);const{animate:d,exit:m,hover:v,press:y,focus:g,view:b,viewOptions:w,style:x}=i,O=p(i,["animate","exit","hover","press","focus","view","viewOptions","style"]),j={},M=new Set([...Object.keys(u.current),...d?Object.keys(d):[]]);if(x)for(const[t,n]of Object.entries(x))G(t)||M.has(t)||n&&"object"==typeof n&&"subscribe"in n||(j[t]=n);return h(t,Object.assign(Object.assign({},O),{style:j,ref:Xt(e,s)}))}));return i.displayName="Animated."+t,i}const Vt=new Map,Wt=new Proxy({},{get:(t,n)=>(Vt.has(n)||Vt.set(n,Ut(n)),Vt.get(n))});function Zt(t,n){const i=t.map((t=>t.current)),s=new P(n(...i));return t.forEach((i=>i.subscribe((()=>(()=>{const i=t.map((t=>t.current));s.set(n(...i))})())))),s}function Gt(t,n,i,s){return X(n,i,s)(t)}const _t=(t,n)=>{var i,s,e;return{type:"spring",to:t,options:{stiffness:null!==(i=null==n?void 0:n.stiffness)&&void 0!==i?i:158,damping:null!==(s=null==n?void 0:n.damping)&&void 0!==s?s:20,mass:null!==(e=null==n?void 0:n.mass)&&void 0!==e?e:1,from:null==n?void 0:n.from,onStart:null==n?void 0:n.onStart,onChange:null==n?void 0:n.onChange,onComplete:null==n?void 0:n.onComplete}}},Bt=(t,n)=>({type:"timing",to:t,options:{duration:null==n?void 0:n.duration,easing:null==n?void 0:n.easing,from:null==n?void 0:n.from,onStart:null==n?void 0:n.onStart,onChange:null==n?void 0:n.onChange,onComplete:null==n?void 0:n.onComplete}}),Jt=(t,n)=>({type:"decay",options:{velocity:t,clamp:null==n?void 0:n.clamp,elastic:null==n?void 0:n.elastic,onStart:null==n?void 0:n.onStart,onChange:null==n?void 0:n.onChange,onComplete:null==n?void 0:n.onComplete}}),Kt=t=>({type:"delay",options:{delay:t}}),Qt=(t,n)=>({type:"sequence",options:{animations:t,onStart:null==n?void 0:n.onStart,onComplete:null==n?void 0:n.onComplete}}),tn=(t,n=Infinity,i)=>({type:"loop",options:{animation:t,iterations:n,onStart:null==i?void 0:i.onStart,onComplete:null==i?void 0:i.onComplete}});function nn(t){const i=n(null),e=s((()=>Array.isArray(t)?t.map((t=>new P(t))):"object"==typeof t?Object.fromEntries(Object.entries(t).map((([t,n])=>[t,new P(n)]))):new P(t)),[]);return[e,function(n){let s=null;s=Array.isArray(t)?function(t,n){var i,s;if(!At(n))return n.forEach(((n,i)=>{var s;null===(s=t[i])||void 0===s||s.set(n)})),null;const e=n,o=Object.fromEntries(t.map(((t,n)=>[n.toString(),t])));switch(e.type){case"sequence":{const t=e.options.animations.map((t=>{var n,i;return"delay"===t.type?kt(null!==(i=null===(n=t.options)||void 0===n?void 0:n.delay)&&void 0!==i?i:0):Ct(o,Object.assign(Object.assign({},t),{to:Array.isArray(t.to)?Object.fromEntries(t.to.map(((t,n)=>[n.toString(),t]))):t.to}))}));return $t(t,e.options)}case"loop":{const t=e.options.animation;return"sequence"===t.type?Ft($t(t.options.animations.map((t=>Ct(o,Object.assign(Object.assign({},t),{to:Array.isArray(t.to)?Object.fromEntries(t.to.map(((t,n)=>[n.toString(),t]))):t.to})))),St(t.options,!0)),null!==(i=e.options.iterations)&&void 0!==i?i:0,St(e.options,!0)):Ft(Ct(o,t),null!==(s=e.options.iterations)&&void 0!==s?s:0,St(e.options,!0))}default:return Ct(o,e)}}(e,n):"object"==typeof t?function(t,n){var i,s;if(At(n))switch(n.type){case"sequence":{const i=n.options.animations.map((n=>{var i;return"delay"===n.type?kt(null!==(i=n.options.delay)&&void 0!==i?i:0):Ct(t,n)}));return $t(i,n.options)}case"loop":{const e=n.options.animation;return"sequence"===e.type?Ft($t(e.options.animations.map((n=>Ct(t,n))),St(e.options,!0)),null!==(i=n.options.iterations)&&void 0!==i?i:0,St(n.options,!0)):Ft(Ct(t,e),null!==(s=n.options.iterations)&&void 0!==s?s:0,St(n.options,!0))}default:return Ct(t,n)}return Object.entries(n).forEach((([n,i])=>{var s;null===(s=t[n])||void 0===s||s.set(i)})),null}(e,n):function(t,n){var i,s,e,o,r,a,c,l,u;if("number"==typeof n||"string"==typeof n)return t.set(n),null;if("sequence"===n.type)return $t((null!==(s=null===(i=n.options)||void 0===i?void 0:i.animations)&&void 0!==s?s:[]).map((n=>Et(t,n))),n.options);if("loop"===n.type){const i=null===(e=n.options)||void 0===e?void 0:e.animation;return i?"sequence"===i.type?Ft($t((null!==(r=null===(o=i.options)||void 0===o?void 0:o.animations)&&void 0!==r?r:[]).map((n=>Et(t,n)))),null!==(c=null===(a=n.options)||void 0===a?void 0:a.iterations)&&void 0!==c?c:0,n.options):Ft(Et(t,i),null!==(u=null===(l=n.options)||void 0===l?void 0:l.iterations)&&void 0!==u?u:0,n.options):null}return Et(t,n)}(e,n),i.current=s,s&&s.start()},{start:()=>{var t;return null===(t=i.current)||void 0===t?void 0:t.start()},pause:()=>{var t;return null===(t=i.current)||void 0===t?void 0:t.pause()},resume:()=>{var t;return null===(t=i.current)||void 0===t?void 0:t.resume()},cancel:()=>{var t;return null===(t=i.current)||void 0===t?void 0:t.cancel()},reset:()=>{var t;return null===(t=i.current)||void 0===t?void 0:t.reset()}}]}const sn={opacity:_t(1,{stiffness:100,damping:15})},en={opacity:_t(0,{stiffness:100,damping:15})},on={opacity:_t(1,{stiffness:100,damping:15}),translateY:_t(0,{stiffness:100,damping:15})},rn={opacity:_t(1,{stiffness:100,damping:15}),translateY:_t(0,{stiffness:100,damping:15})},an={opacity:_t(1,{stiffness:100,damping:15}),translateX:_t(0,{stiffness:100,damping:15})},cn={opacity:_t(1,{stiffness:100,damping:15}),translateX:_t(0,{stiffness:100,damping:15})},ln={translateY:_t(0,{stiffness:100,damping:15})},un={translateY:_t(0,{stiffness:100,damping:15})},hn={translateX:_t(0,{stiffness:100,damping:15})},fn={translateX:_t(0,{stiffness:100,damping:15})},dn={translateY:_t(-100,{stiffness:100,damping:15})},pn={translateY:_t(100,{stiffness:100,damping:15})},mn={translateX:_t(-100,{stiffness:100,damping:15})},vn={translateX:_t(100,{stiffness:100,damping:15})},yn={scale:_t(1,{stiffness:200,damping:20}),opacity:_t(1,{stiffness:100,damping:15})},gn={scale:_t(0,{stiffness:200,damping:20}),opacity:_t(0,{stiffness:100,damping:15})},bn={scale:_t(1.1,{stiffness:150,damping:15})},wn={scale:_t(.9,{stiffness:150,damping:15})},xn={scale:_t(1,{stiffness:300,damping:10}),opacity:_t(1,{stiffness:100,damping:15})},On={scale:_t(0,{stiffness:300,damping:10}),opacity:_t(0,{stiffness:100,damping:15})},jn={rotate:_t(0,{stiffness:100,damping:15}),opacity:_t(1,{stiffness:100,damping:15})},Mn={rotate:_t(180,{stiffness:100,damping:15}),opacity:_t(0,{stiffness:100,damping:15})},$n={rotate:_t(360,{stiffness:50,damping:10})},Fn={scale:_t(1,{stiffness:200,damping:20}),opacity:_t(1,{stiffness:100,damping:15})},kn={scale:_t(0,{stiffness:200,damping:20}),opacity:_t(0,{stiffness:100,damping:15})},Sn={rotateX:_t(0,{stiffness:150,damping:15})},An={rotateY:_t(0,{stiffness:150,damping:15})},En={opacity:_t(1,{stiffness:100,damping:15}),translateY:_t(0,{stiffness:100,damping:15})},Cn={opacity:_t(0,{stiffness:100,damping:15}),translateY:_t(20,{stiffness:100,damping:15})},In={scale:_t(1,{stiffness:200,damping:20}),opacity:_t(1,{stiffness:100,damping:15})},Tn={scale:_t(.8,{stiffness:200,damping:20}),opacity:_t(0,{stiffness:100,damping:15})},qn={scale:_t(1.05,{stiffness:300,damping:20})},Ln={translateY:_t(-5,{stiffness:300,damping:20}),scale:_t(1.02,{stiffness:300,damping:20})},Yn={scale:_t(1.05,{stiffness:300,damping:20}),opacity:_t(.9,{stiffness:300,damping:20})},zn={scale:_t(.95,{stiffness:400,damping:25})},Hn={translateY:_t(2,{stiffness:400,damping:25}),scale:_t(.98,{stiffness:400,damping:25})},Nn={opacity:_t(0,{stiffness:100,damping:15})},Dn={opacity:_t(0,{stiffness:100,damping:15}),translateY:_t(-20,{stiffness:100,damping:15})},Xn={opacity:_t(0,{stiffness:100,damping:15}),translateY:_t(20,{stiffness:100,damping:15})},Pn={scale:_t(.8,{stiffness:200,damping:20}),opacity:_t(0,{stiffness:100,damping:15})},Rn={fadeIn:sn,fadeOut:en,fadeInUp:on,fadeInDown:rn,fadeInLeft:an,fadeInRight:cn,slideInUp:ln,slideInDown:un,slideInLeft:hn,slideInRight:fn,slideOutUp:dn,slideOutDown:pn,slideOutLeft:mn,slideOutRight:vn,scaleIn:yn,scaleOut:gn,scaleUp:bn,scaleDown:wn,bounceIn:xn,bounceOut:On,rotateIn:jn,rotateOut:Mn,spin:$n,zoomIn:Fn,zoomOut:kn,flipX:Sn,flipY:An,slideFadeIn:En,slideFadeOut:Cn,scaleFadeIn:In,scaleFadeOut:Tn,hoverScale:qn,hoverLift:Ln,hoverGlow:Yn,pressScale:zn,pressDown:Hn,exitFade:Nn,exitSlideUp:Dn,exitSlideDown:Xn,exitScale:Pn};function Un(t,i,s=[]){const e=n(i);a((()=>{e.current=i}),[i,...s]),a((()=>{function n(n){const i=t.current,s=n.target;i&&s&&s.isConnected&&i.isConnected&&(i.contains(s)||e.current(n))}return document.addEventListener("mousedown",n),document.addEventListener("touchstart",n),()=>{document.removeEventListener("mousedown",n),document.removeEventListener("touchstart",n)}}),[t])}class Vn{constructor(){this.changeListeners=new Set,this.endListeners=new Set}onChange(t){return this.changeListeners.add(t),this}onEnd(t){return this.endListeners.add(t),this}emitChange(t){this.changeListeners.forEach((n=>n(t)))}emitEnd(t){this.endListeners.forEach((n=>n(t)))}}Vn.VELOCITY_LIMIT=20;class Wn extends Vn{constructor(t={}){super(),this.prev={x:0,y:0},this.lastTime=0,this.movement={x:0,y:0},this.velocity={x:0,y:0},this.start={x:0,y:0},this.offset={x:0,y:0},this.pointerCaptured=!1,this.activePointerId=null,this.attachedEls=new Set,this.activeEl=null,this.pointerDownPos={x:0,y:0},this.thresholdPassed=!1,this.config=t}attach(t){if(t===window)return()=>{};const n=Array.isArray(t)?t:[t],i=this.onDown.bind(this),s=this.onMove.bind(this),e=this.onUp.bind(this);return n.forEach((t=>{this.attachedEls.add(t),t.addEventListener("pointerdown",i,{passive:!1})})),window.addEventListener("pointermove",s,{passive:!1}),window.addEventListener("pointerup",e),window.addEventListener("pointercancel",e),()=>{n.forEach((t=>{t.removeEventListener("pointerdown",i),this.attachedEls.delete(t)})),window.removeEventListener("pointermove",s),window.removeEventListener("pointerup",e),window.removeEventListener("pointercancel",e)}}onDown(t){var n,i,s;if(0!==t.button)return;const e=t.currentTarget;this.attachedEls.has(e)&&(this.activeEl=e,this.activePointerId=t.pointerId,this.pointerCaptured=!1,this.start=!1===this.thresholdPassed&&0===this.start.x&&0===this.start.y?null!==(s=null===(i=(n=this.config).initial)||void 0===i?void 0:i.call(n))&&void 0!==s?s:{x:0,y:0}:Object.assign({},this.offset),this.offset=Object.assign({},this.start),this.movement={x:0,y:0},this.velocity={x:0,y:0},this.pointerDownPos={x:t.clientX,y:t.clientY},this.thresholdPassed=!1,this.prev={x:t.clientX,y:t.clientY},this.lastTime=t.timeStamp,this.emitChange({down:!0,movement:{x:0,y:0},offset:Object.assign({},this.offset),velocity:{x:0,y:0},event:t,cancel:this.cancel.bind(this)}))}onMove(t){var n;if(this.activePointerId!==t.pointerId||!this.activeEl)return;const i=null!==(n=this.config.threshold)&&void 0!==n?n:0;if(!this.thresholdPassed){const n=t.clientX-this.pointerDownPos.x,s=t.clientY-this.pointerDownPos.y;if(i>Math.hypot(n,s))return;this.thresholdPassed=!0,this.activeEl.setPointerCapture(t.pointerId),this.pointerCaptured=!0}this.pointerCaptured&&t.preventDefault();const s=Math.max((t.timeStamp-this.lastTime)/1e3,1e-6);this.lastTime=t.timeStamp;const e=(t.clientX-this.prev.x)/s/1e3,o=(t.clientY-this.prev.y)/s/1e3;this.velocity={x:Q(e,-Vn.VELOCITY_LIMIT,Vn.VELOCITY_LIMIT),y:Q(o,-Vn.VELOCITY_LIMIT,Vn.VELOCITY_LIMIT)};const r={x:t.clientX-this.pointerDownPos.x,y:t.clientY-this.pointerDownPos.y};this.movement={x:"y"===this.config.axis?0:r.x,y:"x"===this.config.axis?0:r.y},this.offset={x:this.start.x+this.movement.x,y:this.start.y+this.movement.y},this.prev={x:t.clientX,y:t.clientY},this.emitChange({down:!0,movement:Object.assign({},this.movement),offset:Object.assign({},this.offset),velocity:Object.assign({},this.velocity),event:t,cancel:this.cancel.bind(this)})}onUp(t){this.activePointerId===t.pointerId&&this.activeEl&&(this.activeEl.releasePointerCapture(t.pointerId),this.emitEnd({down:!1,movement:Object.assign({},this.movement),offset:Object.assign({},this.offset),velocity:Object.assign({},this.velocity),event:t,cancel:this.cancel.bind(this)}),this.activePointerId=null,this.pointerCaptured=!1)}cancel(){this.activeEl&&null!==this.activePointerId&&(this.activeEl.releasePointerCapture(this.activePointerId),this.activePointerId=null,this.activeEl=null)}}function Zn(t,i,s,e){const o=Gn(s),r=Gn(e);if(i===window){const s=n();if(!s.current){const n=new t(r.current),i=t=>o.current(Object.assign(Object.assign({},t),{index:0}));n.onChange(i).onEnd(i),s.current=n}return void a((()=>{const t=s.current.attach(window);return()=>{t()}}),[i])}const c=Array.isArray(i)?i:[i],l=n([]),u=n([]),h=n(c.length);l.current.length!==c.length&&(l.current=c.map(((n,i)=>{const s=new t(r.current),e=t=>o.current(Object.assign(Object.assign({},t),{index:i}));return s.onChange(e).onEnd(e),s})),h.current=c.length),a((()=>{u.current.forEach((t=>t())),u.current=[];const t=[];return c.forEach(((n,i)=>{const s=n.current;if(s&&l.current[i]){const n=l.current[i].attach(s);t.push(n)}})),u.current=t,()=>{t.forEach((t=>t())),u.current=[]}}),[c])}function Gn(t){const i=n(t);return a((()=>{i.current=t}),[t]),i}function _n(t,n,i){return Zn(Wn,t,n,i)}class Bn extends Vn{constructor(){super(...arguments),this.attachedEls=new Set,this.prev={x:0,y:0},this.lastTime=0,this.movement={x:0,y:0},this.offset={x:0,y:0},this.velocity={x:0,y:0},this.startPos=null}attach(t){const n=Array.isArray(t)?t:[t],i=this.onMove.bind(this),s=this.onLeave.bind(this);return n.forEach((t=>{this.attachedEls.add(t),t.addEventListener("pointermove",i,{passive:!1}),t.addEventListener("pointerleave",s)})),()=>{n.forEach((t=>{t.removeEventListener("pointermove",i),t.removeEventListener("pointerleave",s),this.attachedEls.delete(t)}))}}cancel(){}onMove(t){const n=t.timeStamp;null===this.startPos&&(this.startPos={x:t.clientX,y:t.clientY},this.prev={x:t.clientX,y:t.clientY},this.lastTime=n);const i=Math.max((n-this.lastTime)/1e3,1e-6);this.lastTime=n;const s=t.clientX-this.prev.x,e=t.clientY-this.prev.y;this.prev={x:t.clientX,y:t.clientY},this.movement={x:t.clientX-this.startPos.x,y:t.clientY-this.startPos.y};const o=t.currentTarget,r=o instanceof HTMLElement?o.getBoundingClientRect():{left:0,top:0};this.offset={x:t.clientX-r.left,y:t.clientY-r.top};const a=s/i/1e3,c=e/i/1e3;this.velocity={x:Q(a,-Vn.VELOCITY_LIMIT,Vn.VELOCITY_LIMIT),y:Q(c,-Vn.VELOCITY_LIMIT,Vn.VELOCITY_LIMIT)},this.emitChange({movement:Object.assign({},this.movement),offset:Object.assign({},this.offset),velocity:Object.assign({},this.velocity),event:t,cancel:()=>this.onLeave(t)})}onLeave(t){this.emitEnd({movement:Object.assign({},this.movement),offset:Object.assign({},this.offset),velocity:Object.assign({},this.velocity),event:t,cancel:()=>{}})}}function Jn(t,n){return Zn(Bn,t,n)}class Kn extends Vn{constructor(){super(...arguments),this.attachedEls=new Set,this.movement={x:0,y:0},this.offset={x:0,y:0},this.velocity={x:0,y:0},this.prevScroll={x:0,y:0},this.lastTime=0}attach(t){const n=Array.isArray(t)?t:[t],i=this.onScroll.bind(this);return n.forEach((t=>{this.attachedEls.add(t),t.addEventListener("scroll",i,{passive:!0})})),()=>{n.forEach((t=>{t.removeEventListener("scroll",i),this.attachedEls.delete(t)})),null!=this.endTimeout&&(clearTimeout(this.endTimeout),this.endTimeout=void 0)}}cancel(){}onScroll(t){const n=Date.now(),i=Math.max((n-this.lastTime)/1e3,1e-6);this.lastTime=n;const s=t.currentTarget,e=s instanceof HTMLElement?s.scrollLeft:window.scrollX,o=s instanceof HTMLElement?s.scrollTop:window.scrollY,r=e-this.prevScroll.x,a=o-this.prevScroll.y;this.prevScroll={x:e,y:o},this.movement={x:r,y:a},this.offset={x:e,y:o};const c=r/i/1e3,l=a/i/1e3;this.velocity={x:Q(c,-Vn.VELOCITY_LIMIT,Vn.VELOCITY_LIMIT),y:Q(l,-Vn.VELOCITY_LIMIT,Vn.VELOCITY_LIMIT)},this.emitChange({movement:Object.assign({},this.movement),offset:Object.assign({},this.offset),velocity:Object.assign({},this.velocity),event:t,cancel:()=>{null!=this.endTimeout&&clearTimeout(this.endTimeout)}}),null!=this.endTimeout&&clearTimeout(this.endTimeout),this.endTimeout=window.setTimeout((()=>{this.emitEnd({movement:Object.assign({},this.movement),offset:Object.assign({},this.offset),velocity:Object.assign({},this.velocity),event:t,cancel:()=>{}})}),150)}}function Qn(t,{target:i,axis:s="y",offset:e=["start start","end end"],animate:o=!0,toDescriptor:r=(t=>_t(t))}={}){const[c,l]=nn(0),[u,h]=nn(0),f=n([0,0]);return a((()=>{var n;const o=t instanceof Window?window:t.current,r=null!==(n=null==i?void 0:i.current)&&void 0!==n?n:document.documentElement;f.current=function([t,n],i,s,e){return[ni(t,i,s,e),ni(n,i,s,e)]}(e,r,o,s)}),[t,i,s,e]),Zn(Kn,t,(t=>{const n="y"===s?t.offset.y:t.offset.x,[i,e]=f.current,a=Math.min(Math.max(e===i?i>n?0:1:(n-i)/(e-i),0),1),c=o?r:t=>t;"y"===s?(l(c(a)),h(0)):(h(c(a)),l(0))})),{scrollYProgress:c,scrollXProgress:u}}function ti(t,n){return t instanceof HTMLElement?"y"===n?t.clientHeight:t.clientWidth:"y"===n?window.innerHeight:window.innerWidth}function ni(t,n,i,s){const[e,o=e]=t.trim().split(/\s+/);if(i instanceof HTMLElement){const t=n.getBoundingClientRect(),r=i.getBoundingClientRect(),a=function(t,n){return t instanceof HTMLElement?"y"===n?t.scrollTop:t.scrollLeft:"y"===n?window.scrollY:window.scrollX}(i,s),c=("y"===s?t.top-r.top:t.left-r.left)+a,l="y"===s?t.height:t.width,u=ti(i,s);return ii(e,c,l,u)-ii(o,0,u,u)}return si(e,s,n,!1)-si(o,s,window,!0)}function ii(t,n,i,s){if("start"===t)return n;if("center"===t)return n+i/2;if("end"===t)return n+i;const e=t.match(/^(-?\d+(?:\.\d+)?)(px|%|vw|vh)?$/);if(!e)throw Error(`Invalid edge marker “${t}”`);const o=parseFloat(e[1]);switch(e[2]){case"px":return n+o;case"%":return n+o/100*i;case"vw":case"vh":return n+o/100*s;default:return n+o*i}}function si(t,n,i,s){const e=i;return ii(t,s?0:(()=>{if(!(i instanceof HTMLElement))throw Error("Expected HTMLElement for element-relative edge");const t=i.getBoundingClientRect(),s="y"===n?window.pageYOffset||window.scrollY:window.pageXOffset||window.scrollX;return("y"===n?t.top:t.left)+s})(),s?ti(i,n):(()=>{if(!(i instanceof HTMLElement))throw Error();const t=i.getBoundingClientRect();return"y"===n?t.height:t.width})(),ti(e,n))}function ei(t,n){return"function"==typeof n?Zn(Kn,t,n):Qn(t,n)}class oi extends Vn{constructor(){super(...arguments),this.attachedEls=new Set,this.movement={x:0,y:0},this.offset={x:0,y:0},this.velocity={x:0,y:0},this.lastTime=0}attach(t){const n=Array.isArray(t)?t:[t],i=this.onWheel.bind(this);return n.forEach((t=>{this.attachedEls.add(t),t.addEventListener("wheel",i,{passive:!1})})),()=>{n.forEach((t=>{t.removeEventListener("wheel",i),this.attachedEls.delete(t)})),null!=this.endTimeout&&(clearTimeout(this.endTimeout),this.endTimeout=void 0)}}cancel(){}onWheel(t){t.preventDefault();const n=t.timeStamp,i=Math.max((n-this.lastTime)/1e3,1e-6);this.lastTime=n;const s=t.deltaX,e=t.deltaY;this.movement={x:s,y:e},this.offset.x+=s,this.offset.y+=e;const o=s/i/1e3,r=e/i/1e3;this.velocity={x:Q(o,-Vn.VELOCITY_LIMIT,Vn.VELOCITY_LIMIT),y:Q(r,-Vn.VELOCITY_LIMIT,Vn.VELOCITY_LIMIT)},this.emitChange({movement:Object.assign({},this.movement),offset:Object.assign({},this.offset),velocity:Object.assign({},this.velocity),event:t,cancel:()=>{null!=this.endTimeout&&clearTimeout(this.endTimeout)}}),null!=this.endTimeout&&clearTimeout(this.endTimeout),this.endTimeout=window.setTimeout((()=>{this.emitEnd({movement:Object.assign({},this.movement),offset:Object.assign({},this.offset),velocity:Object.assign({},this.velocity),event:t,cancel:()=>{}})}),150)}}function ri(t,n){return Zn(oi,t,n)}export{P as AnimateValue,vt as Easing,Yt as Presence,It as PresenceContext,Wt as animate,xn as bounceIn,On as bounceOut,Q as clamp,Zt as combine,ot as decay,kt as delay,Nn as exitFade,Pn as exitScale,Xn as exitSlideDown,Dn as exitSlideUp,sn as fadeIn,rn as fadeInDown,an as fadeInLeft,cn as fadeInRight,on as fadeInUp,en as fadeOut,Sn as flipX,An as flipY,Yn as hoverGlow,Ln as hoverLift,qn as hoverScale,R as isAnimateValue,Ft as loop,Ut as makeAnimated,st as move,Mt as parallel,Hn as pressDown,zn as pressScale,Rn as recipes,jn as rotateIn,Mn as rotateOut,nt as rubberClamp,wn as scaleDown,In as scaleFadeIn,Tn as scaleFadeOut,yn as scaleIn,gn as scaleOut,bn as scaleUp,$t as sequence,En as slideFadeIn,Cn as slideFadeOut,un as slideInDown,hn as slideInLeft,fn as slideInRight,ln as slideInUp,pn as slideOutDown,mn as slideOutLeft,vn as slideOutRight,dn as slideOutUp,it as snapTo,$n as spin,at as spring,bt as timing,Gt as to,_n as useDrag,Pt as useInView,qt as useIsPresent,Jn as useMove,Un as useOutsideClick,Tt as usePresence,Zn as useRecognizer,ei as useScroll,Qn as useScrollProgress,nn as useValue,ri as useWheel,Jt as withDecay,Kt as withDelay,tn as withLoop,Qt as withSequence,_t as withSpring,Bt as withTiming,Fn as zoomIn,kn as zoomOut};
|
|
1
|
+
import {createContext,useRef,useState,useMemo,Children,isValidElement,useLayoutEffect,useEffect,useCallback,useContext,forwardRef,createElement}from'react';import {jsx,Fragment}from'react/jsx-runtime';const SWITCH_THRESHOLD = 0.01;
|
|
2
|
+
const OPTIONAL_KEYWORDS = ['inset', 'normal', 'italic', 'bold', 'thin', 'medium', 'thick'];
|
|
3
|
+
const TRANSFORM_KEYWORDS = ['translate', 'rotate', 'scale', 'skew', 'matrix', 'perspective'];
|
|
4
|
+
// Reduced to the most commonly used named colors; anything else should use hex/rgb/hsl.
|
|
5
|
+
const namedColors = {
|
|
6
|
+
transparent: '#00000000',
|
|
7
|
+
black: '#000000ff',
|
|
8
|
+
white: '#ffffffff',
|
|
9
|
+
red: '#ff0000ff',
|
|
10
|
+
green: '#008000ff',
|
|
11
|
+
blue: '#0000ffff',
|
|
12
|
+
yellow: '#ffff00ff',
|
|
13
|
+
cyan: '#00ffffff',
|
|
14
|
+
magenta: '#ff00ffff',
|
|
15
|
+
gray: '#808080ff',
|
|
16
|
+
grey: '#808080ff',
|
|
17
|
+
darkgray: '#a9a9a9ff',
|
|
18
|
+
darkgrey: '#a9a9a9ff',
|
|
19
|
+
lightgray: '#d3d3d3ff',
|
|
20
|
+
lightgrey: '#d3d3d3ff',
|
|
21
|
+
dimgray: '#696969ff',
|
|
22
|
+
dimgrey: '#696969ff',
|
|
23
|
+
slategray: '#708090ff',
|
|
24
|
+
slategrey: '#708090ff',
|
|
25
|
+
darkslategray: '#2f4f4fff',
|
|
26
|
+
darkslategrey: '#2f4f4fff',
|
|
27
|
+
orange: '#ffa500ff',
|
|
28
|
+
purple: '#800080ff',
|
|
29
|
+
pink: '#ffc0cbff',
|
|
30
|
+
brown: '#a52a2aff',
|
|
31
|
+
navy: '#000080ff',
|
|
32
|
+
teal: '#008080ff',
|
|
33
|
+
silver: '#c0c0c0ff',
|
|
34
|
+
gold: '#ffd700ff',
|
|
35
|
+
aqua: '#00ffffff',
|
|
36
|
+
fuchsia: '#ff00ffff',
|
|
37
|
+
lime: '#00ff00ff',
|
|
38
|
+
maroon: '#800000ff',
|
|
39
|
+
olive: '#808000ff',
|
|
40
|
+
rebeccapurple: '#663399ff',
|
|
41
|
+
};
|
|
42
|
+
const numberRE = /-?\d+(\.\d+)?/g;
|
|
43
|
+
const HEX_RE = /^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
44
|
+
const RGB_RE = /^rgba?\(\s*-?\d+(\.\d+)?%?(?:\s*,\s*-?\d+(\.\d+)?%?){2}(?:\s*,\s*(0|1|0?\.\d+))?\s*\)$/i;
|
|
45
|
+
const HSL_RE = /^hsl\(\s*\d+(\.\d+)?(?:\s*,\s*\d+(\.\d+)?%){2}\s*\)$/i;
|
|
46
|
+
const HSLA_RE = /^hsla\(\s*\d+(\.\d+)?(?:\s*,\s*\d+(\.\d+)?%){2}\s*,\s*(0|1|0?\.\d+)\s*\)$/i;
|
|
47
|
+
const GRADIENT_RE = /^\s*(linear|radial|conic)-gradient\s*\(/i;
|
|
48
|
+
const SIMPLE_FUNC_RE = /^([a-zA-Z$_][\w$]*)\((-?\d*\.?\d+)([a-zA-Z%]*)\)$/;
|
|
49
|
+
const NUM_UNIT_RE = /^(-?\d+(\.\d+)?)([a-zA-Z%]*)$/;
|
|
50
|
+
const TOKEN_RE = /(\s+|[(),])/g;
|
|
51
|
+
function isCssColorLiteral(s) {
|
|
52
|
+
const c = s.trim().toLowerCase();
|
|
53
|
+
if (HEX_RE.test(c) || namedColors[c] !== undefined) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (RGB_RE.test(c)) {
|
|
57
|
+
const percentage = c.includes('%');
|
|
58
|
+
const nums = [...c.matchAll(numberRE)].map((m) => +m[0]);
|
|
59
|
+
const [r, g, b, a = 1] = nums;
|
|
60
|
+
if (percentage) {
|
|
61
|
+
return r >= 0 && r <= 100 && g >= 0 && g <= 100 && b >= 0 && b <= 100 && a >= 0 && a <= 1;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
return r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255 && a >= 0 && a <= 1;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (HSL_RE.test(c)) {
|
|
68
|
+
const nums = [...c.matchAll(numberRE)].map((m) => +m[0]);
|
|
69
|
+
const [h, s, l] = nums;
|
|
70
|
+
return h >= 0 && h <= 360 && s >= 0 && s <= 100 && l >= 0 && l <= 100;
|
|
71
|
+
}
|
|
72
|
+
if (HSLA_RE.test(c)) {
|
|
73
|
+
const nums = [...c.matchAll(numberRE)].map((m) => +m[0]);
|
|
74
|
+
const [h, s, l, a] = nums;
|
|
75
|
+
return (h >= 0 && h <= 360 && s >= 0 && s <= 100 && l >= 0 && l <= 100 && a >= 0 && a <= 1);
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
function parseCssColor(c) {
|
|
80
|
+
let color = c.trim().toLowerCase();
|
|
81
|
+
if (namedColors[color])
|
|
82
|
+
color = namedColors[color];
|
|
83
|
+
if (HEX_RE.test(color)) {
|
|
84
|
+
let hex = color.slice(1);
|
|
85
|
+
if (hex.length === 3)
|
|
86
|
+
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
|
87
|
+
else if (hex.length === 4)
|
|
88
|
+
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3];
|
|
89
|
+
const hasA = hex.length === 8;
|
|
90
|
+
const v = parseInt(hex, 16);
|
|
91
|
+
const r = (v >> (hasA ? 24 : 16)) & 0xff;
|
|
92
|
+
const g = (v >> (hasA ? 16 : 8)) & 0xff;
|
|
93
|
+
const b = (v >> (hasA ? 8 : 0)) & 0xff;
|
|
94
|
+
const a = hasA ? (v & 0xff) / 255 : 1;
|
|
95
|
+
return [r, g, b, a];
|
|
96
|
+
}
|
|
97
|
+
if (RGB_RE.test(color)) {
|
|
98
|
+
const percentage = color.includes('%');
|
|
99
|
+
const nums = [...color.matchAll(numberRE)].map((m) => +m[0]);
|
|
100
|
+
let [r, g, b, a = 1] = nums;
|
|
101
|
+
if (percentage) {
|
|
102
|
+
r = Math.round((r / 100) * 255);
|
|
103
|
+
g = Math.round((g / 100) * 255);
|
|
104
|
+
b = Math.round((b / 100) * 255);
|
|
105
|
+
}
|
|
106
|
+
return [r, g, b, a];
|
|
107
|
+
}
|
|
108
|
+
const hslaMatch = color.match(/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)%\s*,\s*(\d+(?:\.\d+)?)%\s*(?:,\s*(\d+(?:\.\d+)?))?\s*\)/i);
|
|
109
|
+
if (hslaMatch) {
|
|
110
|
+
const h = parseFloat(hslaMatch[1]) % 360;
|
|
111
|
+
const s = parseFloat(hslaMatch[2]) / 100;
|
|
112
|
+
const l = parseFloat(hslaMatch[3]) / 100;
|
|
113
|
+
const a = hslaMatch[4] !== undefined ? parseFloat(hslaMatch[4]) : 1;
|
|
114
|
+
const [r, g, b] = hslToRgb(h, s, l);
|
|
115
|
+
return [r, g, b, a];
|
|
116
|
+
}
|
|
117
|
+
throw new Error(`Unrecognized CSS color: ${c}`);
|
|
118
|
+
}
|
|
119
|
+
function hslToRgb(h, s, l) {
|
|
120
|
+
const C = (1 - Math.abs(2 * l - 1)) * s;
|
|
121
|
+
const X = C * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
122
|
+
const m = l - C / 2;
|
|
123
|
+
let r = 0, g = 0, b = 0;
|
|
124
|
+
if (h >= 0 && h < 60)
|
|
125
|
+
[r, g, b] = [C, X, 0];
|
|
126
|
+
else if (h >= 60 && h < 120)
|
|
127
|
+
[r, g, b] = [X, C, 0];
|
|
128
|
+
else if (h >= 120 && h < 180)
|
|
129
|
+
[r, g, b] = [0, C, X];
|
|
130
|
+
else if (h >= 180 && h < 240)
|
|
131
|
+
[r, g, b] = [0, X, C];
|
|
132
|
+
else if (h >= 240 && h < 300)
|
|
133
|
+
[r, g, b] = [X, 0, C];
|
|
134
|
+
else if (h >= 300 && h < 360)
|
|
135
|
+
[r, g, b] = [C, 0, X];
|
|
136
|
+
return [
|
|
137
|
+
Math.round((r + m) * 255),
|
|
138
|
+
Math.round((g + m) * 255),
|
|
139
|
+
Math.round((b + m) * 255),
|
|
140
|
+
];
|
|
141
|
+
}
|
|
142
|
+
function replaceCssColorsWithRgba(input) {
|
|
143
|
+
const hex = `#(?:[0-9A-Fa-f]{3,4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})\\b`;
|
|
144
|
+
const rgbFunc = `rgba?\\([^)]*\\)`;
|
|
145
|
+
const hslFunc = `hsla?\\([^)]*\\)`;
|
|
146
|
+
const named = Object.keys(namedColors)
|
|
147
|
+
.map((n) => n.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&'))
|
|
148
|
+
.join('|');
|
|
149
|
+
const colorRe = new RegExp(`(${hex}|${rgbFunc}|${hslFunc}|\\b(?:${named})\\b)`, 'gi');
|
|
150
|
+
return input.replace(colorRe, (match) => {
|
|
151
|
+
try {
|
|
152
|
+
const [r, g, b, a] = parseCssColor(match);
|
|
153
|
+
const R = Math.round(r);
|
|
154
|
+
const G = Math.round(g);
|
|
155
|
+
const B = Math.round(b);
|
|
156
|
+
const A = parseFloat(a.toFixed(3)).toString();
|
|
157
|
+
return `rgba(${R},${G},${B},${A})`;
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return match;
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
function formatNumber(val) {
|
|
165
|
+
let s = val.toFixed(3);
|
|
166
|
+
return s.replace(/\.?0+$/, '');
|
|
167
|
+
}
|
|
168
|
+
function hasListComma(str) {
|
|
169
|
+
if (!str.includes(','))
|
|
170
|
+
return false;
|
|
171
|
+
if (!str.includes('(') && !str.includes(')'))
|
|
172
|
+
return true;
|
|
173
|
+
if (/\)\s*,/.test(str))
|
|
174
|
+
return true;
|
|
175
|
+
let parenDepth = 0;
|
|
176
|
+
for (let i = 0; i < str.length; i++) {
|
|
177
|
+
if (str[i] === '(')
|
|
178
|
+
parenDepth++;
|
|
179
|
+
else if (str[i] === ')')
|
|
180
|
+
parenDepth--;
|
|
181
|
+
else if (str[i] === ',' && parenDepth === 0)
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
function splitByListCommas(str) {
|
|
187
|
+
if (!str.includes(','))
|
|
188
|
+
return [str];
|
|
189
|
+
const result = [];
|
|
190
|
+
let current = '';
|
|
191
|
+
let parenDepth = 0;
|
|
192
|
+
for (let i = 0; i < str.length; i++) {
|
|
193
|
+
const char = str[i];
|
|
194
|
+
if (char === '(') {
|
|
195
|
+
parenDepth++;
|
|
196
|
+
current += char;
|
|
197
|
+
}
|
|
198
|
+
else if (char === ')') {
|
|
199
|
+
parenDepth--;
|
|
200
|
+
current += char;
|
|
201
|
+
}
|
|
202
|
+
else if (char === ',' && parenDepth === 0) {
|
|
203
|
+
result.push(current.trim());
|
|
204
|
+
current = '';
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
current += char;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (current.trim()) {
|
|
211
|
+
result.push(current.trim());
|
|
212
|
+
}
|
|
213
|
+
return result.length > 0 ? result : [str];
|
|
214
|
+
}
|
|
215
|
+
function extractGradientContent(str) {
|
|
216
|
+
const match = str.match(GRADIENT_RE);
|
|
217
|
+
if (!match)
|
|
218
|
+
return null;
|
|
219
|
+
const type = match[1].toLowerCase();
|
|
220
|
+
const startIdx = match[0].length;
|
|
221
|
+
let depth = 1;
|
|
222
|
+
let endIdx = startIdx;
|
|
223
|
+
for (let i = startIdx; i < str.length && depth > 0; i++) {
|
|
224
|
+
if (str[i] === '(')
|
|
225
|
+
depth++;
|
|
226
|
+
else if (str[i] === ')')
|
|
227
|
+
depth--;
|
|
228
|
+
if (depth === 0) {
|
|
229
|
+
endIdx = i;
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (depth !== 0)
|
|
234
|
+
return null;
|
|
235
|
+
return { type, content: str.substring(startIdx, endIdx) };
|
|
236
|
+
}
|
|
237
|
+
function parseGradientParams(content) {
|
|
238
|
+
let parenDepth = 0;
|
|
239
|
+
let firstCommaIdx = -1;
|
|
240
|
+
for (let i = 0; i < content.length; i++) {
|
|
241
|
+
if (content[i] === '(')
|
|
242
|
+
parenDepth++;
|
|
243
|
+
else if (content[i] === ')')
|
|
244
|
+
parenDepth--;
|
|
245
|
+
else if (content[i] === ',' && parenDepth === 0) {
|
|
246
|
+
firstCommaIdx = i;
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (firstCommaIdx === -1) {
|
|
251
|
+
return { firstParam: '', colorStops: content.trim() };
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
firstParam: content.substring(0, firstCommaIdx).trim(),
|
|
255
|
+
colorStops: content.substring(firstCommaIdx + 1).trim(),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function isTransform(str) {
|
|
259
|
+
return TRANSFORM_KEYWORDS.some((keyword) => str.toLowerCase().includes(keyword));
|
|
260
|
+
}
|
|
261
|
+
function hasDifferentTransformFormats(fromStr, toStr) {
|
|
262
|
+
if (!isTransform(fromStr) || !isTransform(toStr))
|
|
263
|
+
return false;
|
|
264
|
+
const fromParts = fromStr.split(TOKEN_RE).filter((s) => s !== '');
|
|
265
|
+
const toParts = toStr.split(TOKEN_RE).filter((s) => s !== '');
|
|
266
|
+
if (fromParts.length !== toParts.length)
|
|
267
|
+
return true;
|
|
268
|
+
const fromHasMatrix = fromStr.toLowerCase().includes('matrix');
|
|
269
|
+
const toHasMatrix = toStr.toLowerCase().includes('matrix');
|
|
270
|
+
const fromHasIndividual = ['translate', 'rotate', 'scale', 'skew'].some((k) => fromStr.toLowerCase().includes(k));
|
|
271
|
+
const toHasIndividual = ['translate', 'rotate', 'scale', 'skew'].some((k) => toStr.toLowerCase().includes(k));
|
|
272
|
+
return (fromHasMatrix && toHasIndividual) || (toHasMatrix && fromHasIndividual);
|
|
273
|
+
}
|
|
274
|
+
function canSwitchFunctionNames(fromStr, toStr) {
|
|
275
|
+
const fromGradient = fromStr.match(GRADIENT_RE);
|
|
276
|
+
const toGradient = toStr.match(GRADIENT_RE);
|
|
277
|
+
if (fromGradient && toGradient)
|
|
278
|
+
return true;
|
|
279
|
+
return hasDifferentTransformFormats(fromStr, toStr);
|
|
280
|
+
}
|
|
281
|
+
function interpolateCommaSeparatedList(fromStr, toStr, p) {
|
|
282
|
+
const fromList = splitByListCommas(fromStr);
|
|
283
|
+
const toList = splitByListCommas(toStr);
|
|
284
|
+
const maxLen = Math.max(fromList.length, toList.length);
|
|
285
|
+
const result = [];
|
|
286
|
+
for (let i = 0; i < maxLen; i++) {
|
|
287
|
+
const fromItem = fromList[i] || fromList[fromList.length - 1] || fromList[0];
|
|
288
|
+
const toItem = toList[i] || toList[toList.length - 1] || toList[0];
|
|
289
|
+
result.push(interpolateString(fromItem, toItem, p));
|
|
290
|
+
}
|
|
291
|
+
return result.join(', ');
|
|
292
|
+
}
|
|
293
|
+
function convertRgbToRgba(str) {
|
|
294
|
+
return str.replace(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/g, 'rgba($1,$2,$3,1.000)');
|
|
295
|
+
}
|
|
296
|
+
function interpolateGradients(fromGradient, toGradient, p) {
|
|
297
|
+
const gradientType = p < SWITCH_THRESHOLD ? fromGradient.type : toGradient.type;
|
|
298
|
+
const fromParams = parseGradientParams(fromGradient.content);
|
|
299
|
+
const toParams = parseGradientParams(toGradient.content);
|
|
300
|
+
let firstParam;
|
|
301
|
+
if (fromGradient.type === toGradient.type) {
|
|
302
|
+
if (fromParams.firstParam && toParams.firstParam) {
|
|
303
|
+
try {
|
|
304
|
+
firstParam = interpolateString(fromParams.firstParam, toParams.firstParam, p);
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
firstParam = p < SWITCH_THRESHOLD ? fromParams.firstParam : toParams.firstParam;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
firstParam = p < SWITCH_THRESHOLD ? fromParams.firstParam : toParams.firstParam;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
firstParam = p < SWITCH_THRESHOLD ? fromParams.firstParam : toParams.firstParam;
|
|
316
|
+
}
|
|
317
|
+
const colorStops = interpolateString(fromParams.colorStops, toParams.colorStops, p);
|
|
318
|
+
const normalizedColorStops = convertRgbToRgba(colorStops);
|
|
319
|
+
const content = firstParam ? `${firstParam}, ${normalizedColorStops}` : normalizedColorStops;
|
|
320
|
+
return `${gradientType}-gradient(${content})`;
|
|
321
|
+
}
|
|
322
|
+
function interpolateWithFunctionNameSwitch(fromStr, toStr, p) {
|
|
323
|
+
const fromGradient = extractGradientContent(fromStr);
|
|
324
|
+
const toGradient = extractGradientContent(toStr);
|
|
325
|
+
if (fromGradient && toGradient) {
|
|
326
|
+
return interpolateGradients(fromGradient, toGradient, p);
|
|
327
|
+
}
|
|
328
|
+
if (fromGradient || toGradient) {
|
|
329
|
+
return p < SWITCH_THRESHOLD ? fromStr : toStr;
|
|
330
|
+
}
|
|
331
|
+
if (hasDifferentTransformFormats(fromStr, toStr)) {
|
|
332
|
+
return p < SWITCH_THRESHOLD ? fromStr : toStr;
|
|
333
|
+
}
|
|
334
|
+
return p < SWITCH_THRESHOLD ? fromStr : toStr;
|
|
335
|
+
}
|
|
336
|
+
// box-shadow/text-shadow pattern: [keyword]? offsetX offsetY blur [spread]? color
|
|
337
|
+
// (both the 3-value and 4-value forms).
|
|
338
|
+
function parseBoxShadow(shadow) {
|
|
339
|
+
const shadowMatch = shadow.match(/^(\S+\s+)?(-?\d+(?:\.\d+)?)(px|rem|em|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)?\s+(-?\d+(?:\.\d+)?)(px|rem|em|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)?\s+(-?\d+(?:\.\d+)?)(px|rem|em|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)?(?:\s+(-?\d+(?:\.\d+)?)(px|rem|em|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)?)?\s+(.+)$/);
|
|
340
|
+
if (shadowMatch) {
|
|
341
|
+
const keyword = shadowMatch[1] || '';
|
|
342
|
+
const values = [];
|
|
343
|
+
if (shadowMatch[2]) {
|
|
344
|
+
values.push({ num: shadowMatch[2], unit: shadowMatch[3] || '' }); // offsetX
|
|
345
|
+
}
|
|
346
|
+
if (shadowMatch[4]) {
|
|
347
|
+
values.push({ num: shadowMatch[4], unit: shadowMatch[5] || '' }); // offsetY
|
|
348
|
+
}
|
|
349
|
+
if (shadowMatch[6]) {
|
|
350
|
+
values.push({ num: shadowMatch[6], unit: shadowMatch[7] || '' }); // blur
|
|
351
|
+
}
|
|
352
|
+
if (shadowMatch[8]) {
|
|
353
|
+
values.push({ num: shadowMatch[8], unit: shadowMatch[9] || '' }); // spread (optional)
|
|
354
|
+
}
|
|
355
|
+
const color = shadowMatch[10];
|
|
356
|
+
return { keyword, values, color };
|
|
357
|
+
}
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
// e.g. "0 4px 12px rgba(0,0,0,0.3)" -> "0 0px 0px rgba(0,0,0,0)", preserving
|
|
361
|
+
// each value's exact unit (including "no unit" for the first value).
|
|
362
|
+
function createZeroValueFromTarget(target) {
|
|
363
|
+
const parsed = parseBoxShadow(target);
|
|
364
|
+
if (parsed) {
|
|
365
|
+
const { keyword, values, color } = parsed;
|
|
366
|
+
const zeroValues = values.map(v => (v.unit ? `0${v.unit}` : '0'));
|
|
367
|
+
const zeroColor = color.replace(/rgba?\([^)]+\)/gi, (match) => {
|
|
368
|
+
if (match.toLowerCase().includes('rgba')) {
|
|
369
|
+
return 'rgba(0,0,0,0)';
|
|
370
|
+
}
|
|
371
|
+
else if (match.toLowerCase().includes('rgb')) {
|
|
372
|
+
return 'rgba(0,0,0,0)';
|
|
373
|
+
}
|
|
374
|
+
return match;
|
|
375
|
+
});
|
|
376
|
+
return `${keyword}${zeroValues.join(' ')} ${zeroColor}`;
|
|
377
|
+
}
|
|
378
|
+
return '';
|
|
379
|
+
}
|
|
380
|
+
function interpolateString(fromStr, toStr, p) {
|
|
381
|
+
if (!fromStr || fromStr.trim() === '') {
|
|
382
|
+
const zeroFrom = createZeroValueFromTarget(toStr);
|
|
383
|
+
if (zeroFrom) {
|
|
384
|
+
return interpolateString(zeroFrom, toStr, p);
|
|
385
|
+
}
|
|
386
|
+
if (p >= SWITCH_THRESHOLD) {
|
|
387
|
+
return toStr;
|
|
388
|
+
}
|
|
389
|
+
return fromStr || '';
|
|
390
|
+
}
|
|
391
|
+
// Animating to an empty string fades through a zero-value version first,
|
|
392
|
+
// then fades that to nothing, instead of just switching at the threshold —
|
|
393
|
+
// so e.g. a box-shadow shrinks to zero-size before disappearing.
|
|
394
|
+
if (!toStr || toStr.trim() === '') {
|
|
395
|
+
const zeroTo = createZeroValueFromTarget(fromStr);
|
|
396
|
+
if (zeroTo) {
|
|
397
|
+
const zeroResult = interpolateString(fromStr, zeroTo, Math.min(p * 2, 1));
|
|
398
|
+
if (p >= 0.5) {
|
|
399
|
+
const emptyProgress = (p - 0.5) * 2; // 0 to 1 from 0.5 to 1.0
|
|
400
|
+
// Fade the zero version to empty by checking if values are close to zero
|
|
401
|
+
const zeroMatch = zeroResult.match(/^(\S+\s+)?(-?\d+(?:\.\d+)?)(px|rem|em|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)?\s+(-?\d+(?:\.\d+)?)(px|rem|em|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)?\s+(-?\d+(?:\.\d+)?)(px|rem|em|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)?\s+(.+)$/);
|
|
402
|
+
if (zeroMatch) {
|
|
403
|
+
const x = Math.abs(parseFloat(zeroMatch[2]));
|
|
404
|
+
const y = Math.abs(parseFloat(zeroMatch[4]));
|
|
405
|
+
const z = Math.abs(parseFloat(zeroMatch[6]));
|
|
406
|
+
const colorMatch = zeroMatch[8].match(/rgba?\((\d+),\s*(\d+),\s*(\d+),\s*([-\d.]+)\)/);
|
|
407
|
+
const alpha = colorMatch ? Math.abs(parseFloat(colorMatch[4])) : 0;
|
|
408
|
+
// If all values are close to zero and progress is high, return empty
|
|
409
|
+
if (x < 0.01 && y < 0.01 && z < 0.01 && alpha < 0.01 && emptyProgress > 0.9) {
|
|
410
|
+
return '';
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return zeroResult;
|
|
414
|
+
}
|
|
415
|
+
return zeroResult;
|
|
416
|
+
}
|
|
417
|
+
if (p < SWITCH_THRESHOLD) {
|
|
418
|
+
return fromStr;
|
|
419
|
+
}
|
|
420
|
+
return toStr || '';
|
|
421
|
+
}
|
|
422
|
+
const m1 = fromStr.match(SIMPLE_FUNC_RE);
|
|
423
|
+
const m2 = toStr.match(SIMPLE_FUNC_RE);
|
|
424
|
+
if (m1 && m2 && m1[1] === m2[1] && m1[3] === m2[3]) {
|
|
425
|
+
const val = parseFloat(m1[2]) + (parseFloat(m2[2]) - parseFloat(m1[2])) * p;
|
|
426
|
+
return `${m1[1]}(${formatNumber(val)}${m1[3]})`;
|
|
427
|
+
}
|
|
428
|
+
if (isCssColorLiteral(fromStr) && isCssColorLiteral(toStr)) {
|
|
429
|
+
const [r1, g1, b1, a1] = parseCssColor(fromStr);
|
|
430
|
+
const [r2, g2, b2, a2] = parseCssColor(toStr);
|
|
431
|
+
const R = Math.round(r1 + (r2 - r1) * p);
|
|
432
|
+
const G = Math.round(g1 + (g2 - g1) * p);
|
|
433
|
+
const B = Math.round(b1 + (b2 - b1) * p);
|
|
434
|
+
const A = a1 + (a2 - a1) * p;
|
|
435
|
+
const formattedAlpha = formatNumber(A);
|
|
436
|
+
return A < 1 ? `rgba(${R},${G},${B},${formattedAlpha})` : `rgb(${R},${G},${B})`;
|
|
437
|
+
}
|
|
438
|
+
const fromGradient = fromStr.trim().match(GRADIENT_RE);
|
|
439
|
+
const toGradient = toStr.trim().match(GRADIENT_RE);
|
|
440
|
+
if (fromGradient || toGradient) {
|
|
441
|
+
return interpolateWithFunctionNameSwitch(fromStr.trim(), toStr.trim(), p);
|
|
442
|
+
}
|
|
443
|
+
if (hasDifferentTransformFormats(fromStr, toStr)) {
|
|
444
|
+
return interpolateWithFunctionNameSwitch(fromStr, toStr, p);
|
|
445
|
+
}
|
|
446
|
+
if ((fromStr.includes(',') || toStr.includes(',')) && (hasListComma(fromStr) || hasListComma(toStr))) {
|
|
447
|
+
return interpolateCommaSeparatedList(fromStr, toStr, p);
|
|
448
|
+
}
|
|
449
|
+
// Optional leading keywords (e.g., "inset" in box-shadow)
|
|
450
|
+
const fromWords = fromStr.trim().split(/\s+/);
|
|
451
|
+
const toWords = toStr.trim().split(/\s+/);
|
|
452
|
+
const fromFirstWord = fromWords[0]?.toLowerCase();
|
|
453
|
+
const toFirstWord = toWords[0]?.toLowerCase();
|
|
454
|
+
const fromHasKeyword = fromFirstWord && OPTIONAL_KEYWORDS.includes(fromFirstWord);
|
|
455
|
+
const toHasKeyword = toFirstWord && OPTIONAL_KEYWORDS.includes(toFirstWord);
|
|
456
|
+
if (fromHasKeyword || toHasKeyword) {
|
|
457
|
+
const keyword = p < SWITCH_THRESHOLD
|
|
458
|
+
? (fromHasKeyword ? fromFirstWord : '')
|
|
459
|
+
: (toHasKeyword ? toFirstWord : '');
|
|
460
|
+
const fromRest = fromHasKeyword ? fromWords.slice(1).join(' ') : fromStr;
|
|
461
|
+
const toRest = toHasKeyword ? toWords.slice(1).join(' ') : toStr;
|
|
462
|
+
const rest = interpolateString(fromRest, toRest, p);
|
|
463
|
+
return keyword ? `${keyword} ${rest}` : rest;
|
|
464
|
+
}
|
|
465
|
+
// box-shadow/text-shadow can be 3-value (offsetX offsetY blur color) or
|
|
466
|
+
// 4-value (...+ spread) — normalize both sides to the max value count
|
|
467
|
+
// (padding a missing spread with 0) before interpolating value-by-value.
|
|
468
|
+
const fromShadow = parseBoxShadow(fromStr);
|
|
469
|
+
const toShadow = parseBoxShadow(toStr);
|
|
470
|
+
if (fromShadow && toShadow) {
|
|
471
|
+
const maxValues = Math.max(fromShadow.values.length, toShadow.values.length);
|
|
472
|
+
const normalizeShadow = (shadow, targetLength) => {
|
|
473
|
+
if (!shadow)
|
|
474
|
+
return null;
|
|
475
|
+
const { keyword, values, color } = shadow;
|
|
476
|
+
if (values.length === 3 && targetLength === 4) {
|
|
477
|
+
const blurUnit = values[2].unit || 'px';
|
|
478
|
+
return { keyword, values: [...values, { num: '0', unit: blurUnit }], color };
|
|
479
|
+
}
|
|
480
|
+
return { keyword, values, color };
|
|
481
|
+
};
|
|
482
|
+
const normalizedFrom = normalizeShadow(fromShadow, maxValues);
|
|
483
|
+
const normalizedTo = normalizeShadow(toShadow, maxValues);
|
|
484
|
+
if (normalizedFrom && normalizedTo) {
|
|
485
|
+
const interpolatedValues = normalizedFrom.values.map((fromVal, i) => {
|
|
486
|
+
const toVal = normalizedTo.values[i];
|
|
487
|
+
if (!toVal)
|
|
488
|
+
return fromVal;
|
|
489
|
+
if (fromVal.unit === toVal.unit) {
|
|
490
|
+
const fromNum = parseFloat(fromVal.num);
|
|
491
|
+
const toNum = parseFloat(toVal.num);
|
|
492
|
+
const interpolated = fromNum + (toNum - fromNum) * p;
|
|
493
|
+
return { num: formatNumber(interpolated), unit: fromVal.unit };
|
|
494
|
+
}
|
|
495
|
+
return p < SWITCH_THRESHOLD ? fromVal : toVal;
|
|
496
|
+
});
|
|
497
|
+
let interpolatedColor = normalizedFrom.color;
|
|
498
|
+
if (normalizedFrom.color !== normalizedTo.color) {
|
|
499
|
+
try {
|
|
500
|
+
interpolatedColor = interpolateString(normalizedFrom.color, normalizedTo.color, p);
|
|
501
|
+
}
|
|
502
|
+
catch {
|
|
503
|
+
interpolatedColor = p < SWITCH_THRESHOLD ? normalizedFrom.color : normalizedTo.color;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
const keyword = p < SWITCH_THRESHOLD
|
|
507
|
+
? (normalizedFrom.keyword || normalizedTo.keyword)
|
|
508
|
+
: (normalizedTo.keyword || normalizedFrom.keyword);
|
|
509
|
+
// Only 4-value output if either original side actually had a spread —
|
|
510
|
+
// don't leak the padding added above back into a 3-value result.
|
|
511
|
+
const shouldIncludeSpread = maxValues === 4;
|
|
512
|
+
const valuesToOutput = shouldIncludeSpread
|
|
513
|
+
? interpolatedValues
|
|
514
|
+
: interpolatedValues.slice(0, 3);
|
|
515
|
+
const valuesStr = valuesToOutput.map(v => v.unit ? `${v.num}${v.unit}` : v.num).join(' ');
|
|
516
|
+
return `${keyword}${valuesStr} ${interpolatedColor}`;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
const fromParts = fromStr.split(TOKEN_RE).filter((s) => s !== '');
|
|
520
|
+
const toParts = toStr.split(TOKEN_RE).filter((s) => s !== '');
|
|
521
|
+
if (fromParts.length !== toParts.length) {
|
|
522
|
+
if (canSwitchFunctionNames(fromStr, toStr)) {
|
|
523
|
+
return interpolateWithFunctionNameSwitch(fromStr, toStr, p);
|
|
524
|
+
}
|
|
525
|
+
throw new Error(`interpolate: template mismatch:\n "${fromStr}"\n vs "${toStr}"`);
|
|
526
|
+
}
|
|
527
|
+
const mappers = fromParts.map((fp, i) => {
|
|
528
|
+
const tp = toParts[i];
|
|
529
|
+
if (fp === tp && /\s+/.test(fp))
|
|
530
|
+
return () => fp;
|
|
531
|
+
const n1 = fp.match(NUM_UNIT_RE);
|
|
532
|
+
const n2 = tp.match(NUM_UNIT_RE);
|
|
533
|
+
if (n1 && n2 && n1[3] === n2[3]) {
|
|
534
|
+
const val = parseFloat(n1[1]) + (parseFloat(n2[1]) - parseFloat(n1[1])) * p;
|
|
535
|
+
return () => `${formatNumber(val)}${n1[3]}`;
|
|
536
|
+
}
|
|
537
|
+
if (isCssColorLiteral(fp) && isCssColorLiteral(tp)) {
|
|
538
|
+
return () => interpolateString(fp, tp, p);
|
|
539
|
+
}
|
|
540
|
+
if (fp === tp)
|
|
541
|
+
return () => fp;
|
|
542
|
+
// CSS keyword switching (e.g., "solid" vs "dashed")
|
|
543
|
+
const isSimpleWord = /^[a-zA-Z-]+$/.test(fp) && /^[a-zA-Z-]+$/.test(tp);
|
|
544
|
+
if (isSimpleWord) {
|
|
545
|
+
const isFunctionName = (i + 1 < fromParts.length && fromParts[i + 1] === '(') ||
|
|
546
|
+
(i + 1 < toParts.length && toParts[i + 1] === '(');
|
|
547
|
+
if (!isFunctionName) {
|
|
548
|
+
return () => (p < SWITCH_THRESHOLD ? fp : tp);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
throw new Error(`interpolate: cannot interpolate tokens "${fp}" vs "${tp}"`);
|
|
552
|
+
});
|
|
553
|
+
return mappers.map((fn) => fn()).join('');
|
|
554
|
+
}
|
|
555
|
+
function to$1(inRange, outRange, config) {
|
|
556
|
+
const len = inRange.length;
|
|
557
|
+
if (len < 2 || outRange.length !== len) {
|
|
558
|
+
throw new Error('interpolate: inRange and outRange must be arrays of the same length >= 2');
|
|
559
|
+
}
|
|
560
|
+
const extrapolateLeft = config?.extrapolateLeft ?? config?.extrapolate ?? 'extend';
|
|
561
|
+
const extrapolateRight = config?.extrapolateRight ?? config?.extrapolate ?? 'extend';
|
|
562
|
+
const sanitizedOut = outRange.map((v) => typeof v === 'string' ? replaceCssColorsWithRgba(v) : v);
|
|
563
|
+
return (tRaw) => {
|
|
564
|
+
let t = tRaw;
|
|
565
|
+
if (tRaw < inRange[0] && extrapolateLeft === 'clamp') {
|
|
566
|
+
t = inRange[0];
|
|
567
|
+
}
|
|
568
|
+
else if (tRaw > inRange[len - 1] && extrapolateRight === 'clamp') {
|
|
569
|
+
t = inRange[len - 1];
|
|
570
|
+
}
|
|
571
|
+
let i = 0;
|
|
572
|
+
if (t <= inRange[0]) {
|
|
573
|
+
i = 0;
|
|
574
|
+
}
|
|
575
|
+
else if (t >= inRange[len - 1]) {
|
|
576
|
+
i = len - 2;
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
for (let j = 0; j < len - 1; j++) {
|
|
580
|
+
if (t >= inRange[j] && t <= inRange[j + 1]) {
|
|
581
|
+
i = j;
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
const t0 = inRange[i];
|
|
587
|
+
const t1 = inRange[i + 1];
|
|
588
|
+
let p = (t - t0) / (t1 - t0);
|
|
589
|
+
if (config?.easing)
|
|
590
|
+
p = config.easing(p);
|
|
591
|
+
const fromOut = sanitizedOut[i];
|
|
592
|
+
const toOut = sanitizedOut[i + 1];
|
|
593
|
+
if (typeof fromOut === 'number' && typeof toOut === 'number') {
|
|
594
|
+
return fromOut + (toOut - fromOut) * p;
|
|
595
|
+
}
|
|
596
|
+
return interpolateString(String(fromOut), String(toOut), p);
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
// combine function moved to utils/combine.ts to avoid circular dependency
|
|
600
|
+
class AnimateValue {
|
|
601
|
+
constructor(initial) {
|
|
602
|
+
this.subscribers = new Set();
|
|
603
|
+
this._current = initial;
|
|
604
|
+
this._initial = initial;
|
|
605
|
+
}
|
|
606
|
+
get current() {
|
|
607
|
+
return this._current;
|
|
608
|
+
}
|
|
609
|
+
get initial() {
|
|
610
|
+
return this._initial;
|
|
611
|
+
}
|
|
612
|
+
set(value) {
|
|
613
|
+
this.controller?.cancel();
|
|
614
|
+
this.controller = undefined;
|
|
615
|
+
this._internalSet(value);
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* @internal only use internally
|
|
619
|
+
*/
|
|
620
|
+
_internalSet(value) {
|
|
621
|
+
if (value === this._current)
|
|
622
|
+
return;
|
|
623
|
+
this._current = value;
|
|
624
|
+
for (const sub of this.subscribers)
|
|
625
|
+
sub(value);
|
|
626
|
+
}
|
|
627
|
+
subscribe(fn) {
|
|
628
|
+
this.subscribers.add(fn);
|
|
629
|
+
fn(this._current);
|
|
630
|
+
return () => {
|
|
631
|
+
this.subscribers.delete(fn);
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
reset() {
|
|
635
|
+
this.set(this._initial);
|
|
636
|
+
}
|
|
637
|
+
destroy() {
|
|
638
|
+
this.subscribers.clear();
|
|
639
|
+
this.controller?.cancel();
|
|
640
|
+
this.controller = undefined;
|
|
641
|
+
}
|
|
642
|
+
to(arg1, arg2, arg3) {
|
|
643
|
+
if (typeof arg1 === 'function') {
|
|
644
|
+
const mapFn = arg1;
|
|
645
|
+
const output = new AnimateValue(mapFn(this._current));
|
|
646
|
+
this.subscribe((value) => output.set(mapFn(value)));
|
|
647
|
+
return output;
|
|
648
|
+
}
|
|
649
|
+
const inRange = arg1;
|
|
650
|
+
const outRange = arg2;
|
|
651
|
+
const config = arg3;
|
|
652
|
+
const mapValue = to$1(inRange, outRange, config);
|
|
653
|
+
const output = new AnimateValue(mapValue(this.current));
|
|
654
|
+
this.subscribe((value) => output.set(mapValue(value)));
|
|
655
|
+
return output;
|
|
656
|
+
}
|
|
657
|
+
setAnimationController(controller) {
|
|
658
|
+
this.controller?.cancel();
|
|
659
|
+
this.controller = controller;
|
|
660
|
+
}
|
|
661
|
+
getAnimationController() {
|
|
662
|
+
return this.controller;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
function isAnimateValue(value) {
|
|
666
|
+
return value instanceof AnimateValue;
|
|
667
|
+
}const UNIT_LESS = new Set([
|
|
668
|
+
'borderImageOutset',
|
|
669
|
+
'borderImageSlice',
|
|
670
|
+
'borderImageWidth',
|
|
671
|
+
'fontWeight',
|
|
672
|
+
'lineHeight',
|
|
673
|
+
'opacity',
|
|
674
|
+
'orphans',
|
|
675
|
+
'tabSize',
|
|
676
|
+
'widows',
|
|
677
|
+
'zIndex',
|
|
678
|
+
'zoom',
|
|
679
|
+
'fillOpacity',
|
|
680
|
+
'floodOpacity',
|
|
681
|
+
'stopOpacity',
|
|
682
|
+
'strokeDasharray',
|
|
683
|
+
'strokeDashoffset',
|
|
684
|
+
'strokeMiterlimit',
|
|
685
|
+
'strokeOpacity',
|
|
686
|
+
'strokeWidth',
|
|
687
|
+
'animationIterationCount',
|
|
688
|
+
'boxFlex',
|
|
689
|
+
'boxFlexGroup',
|
|
690
|
+
'boxOrdinalGroup',
|
|
691
|
+
'columnCount',
|
|
692
|
+
'flex',
|
|
693
|
+
'flexGrow',
|
|
694
|
+
'flexPositive',
|
|
695
|
+
'flexShrink',
|
|
696
|
+
'flexNegative',
|
|
697
|
+
'flexOrder',
|
|
698
|
+
'gridRow',
|
|
699
|
+
'gridColumn',
|
|
700
|
+
'order',
|
|
701
|
+
'lineClamp',
|
|
702
|
+
]);
|
|
703
|
+
const transformKeys = [
|
|
704
|
+
'translateX',
|
|
705
|
+
'translateY',
|
|
706
|
+
'translateZ',
|
|
707
|
+
'rotate',
|
|
708
|
+
'rotateX',
|
|
709
|
+
'rotateY',
|
|
710
|
+
'rotateZ',
|
|
711
|
+
'scale',
|
|
712
|
+
'scaleX',
|
|
713
|
+
'scaleY',
|
|
714
|
+
'skewX',
|
|
715
|
+
'skewY',
|
|
716
|
+
'perspective',
|
|
717
|
+
];
|
|
718
|
+
// Internal-only pseudo transform keys reserved for the `layout` prop's own
|
|
719
|
+
// translate/scale contribution. They're stored under these distinct object
|
|
720
|
+
// keys (instead of e.g. "translateX") specifically so they never overwrite a
|
|
721
|
+
// same-named transform the consumer is animating via `animate`/`hover`/
|
|
722
|
+
// `press`/`view`/`style` on the same element — both contributions compose as
|
|
723
|
+
// separate CSS transform functions in the final string instead.
|
|
724
|
+
const LAYOUT_TRANSFORM_KEY_TO_CSS_FUNCTION = {
|
|
725
|
+
__layoutTranslateX: 'translateX',
|
|
726
|
+
__layoutTranslateY: 'translateY',
|
|
727
|
+
__layoutScaleX: 'scaleX',
|
|
728
|
+
__layoutScaleY: 'scaleY',
|
|
729
|
+
// Separate namespace for the `layoutId` shared transition's own FLIP
|
|
730
|
+
// contribution, so it can never collide with the `layout` prop's pseudo
|
|
731
|
+
// keys above if both happen to be set on the same element.
|
|
732
|
+
__layoutIdTranslateX: 'translateX',
|
|
733
|
+
__layoutIdTranslateY: 'translateY',
|
|
734
|
+
__layoutIdScaleX: 'scaleX',
|
|
735
|
+
__layoutIdScaleY: 'scaleY',
|
|
736
|
+
};
|
|
737
|
+
// Internal function - exported for testing only (not re-exported from main index)
|
|
738
|
+
function applyStyleProp(el, key, v) {
|
|
739
|
+
const css = typeof v === 'number' && !UNIT_LESS.has(key) ? `${v}px` : String(v);
|
|
740
|
+
el.style[key] = css;
|
|
741
|
+
}
|
|
742
|
+
function splitCSSValueAndUnit(raw) {
|
|
743
|
+
const numMatch = raw.match(/-?\d+(\.\d+)?/)?.[0] ?? '0';
|
|
744
|
+
const unitMatch = raw.match(/px|rem|em|ex|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax/)?.[0] ?? '';
|
|
745
|
+
return { value: Number(numMatch), unit: unitMatch };
|
|
746
|
+
}
|
|
747
|
+
function defaultUnit(key) {
|
|
748
|
+
if (key === 'perspective' || key.startsWith('translate'))
|
|
749
|
+
return 'px';
|
|
750
|
+
if (key.startsWith('rotate') || key.startsWith('skew'))
|
|
751
|
+
return 'deg';
|
|
752
|
+
return '';
|
|
753
|
+
}
|
|
754
|
+
function formatTransformFunction(key, raw) {
|
|
755
|
+
const cssFunction = LAYOUT_TRANSFORM_KEY_TO_CSS_FUNCTION[key] ??
|
|
756
|
+
key;
|
|
757
|
+
const cur = raw && typeof raw.subscribe === 'function'
|
|
758
|
+
? raw.current
|
|
759
|
+
: raw;
|
|
760
|
+
if (Array.isArray(cur)) {
|
|
761
|
+
return `${cssFunction}(${cur.join(',')})`;
|
|
762
|
+
}
|
|
763
|
+
const str = String(cur);
|
|
764
|
+
const { value, unit: parsedUnit } = splitCSSValueAndUnit(str);
|
|
765
|
+
const unit = parsedUnit || defaultUnit(cssFunction);
|
|
766
|
+
return `${cssFunction}(${value}${unit})`;
|
|
767
|
+
}
|
|
768
|
+
// Internal function - exported for internal use only
|
|
769
|
+
function isTransformKey(key) {
|
|
770
|
+
return (transformKeys.includes(key) ||
|
|
771
|
+
key in LAYOUT_TRANSFORM_KEY_TO_CSS_FUNCTION);
|
|
772
|
+
}
|
|
773
|
+
// Formats an already-merged style/animateValues object (values may be raw
|
|
774
|
+
// primitives or AnimateValue instances) into a single CSS transform string.
|
|
775
|
+
// Exported so systems that manage their own transform rendering (e.g. the
|
|
776
|
+
// `layout` prop) can compose with whatever else is contributing transforms
|
|
777
|
+
// to the same element.
|
|
778
|
+
function formatTransformString(txProps) {
|
|
779
|
+
const transformKeyList = Object.keys(txProps).filter(isTransformKey);
|
|
780
|
+
if (transformKeyList.length > 0) {
|
|
781
|
+
return transformKeyList
|
|
782
|
+
.map((key) => formatTransformFunction(key, txProps[key]))
|
|
783
|
+
.join(' ');
|
|
784
|
+
}
|
|
785
|
+
if (typeof txProps.transform === 'string')
|
|
786
|
+
return txProps.transform;
|
|
787
|
+
return '';
|
|
788
|
+
}
|
|
789
|
+
// Internal function - exported for testing only (not re-exported from main index)
|
|
790
|
+
function applyTransformsStyle(node, txProps) {
|
|
791
|
+
const transformKeyList = Object.keys(txProps).filter(isTransformKey);
|
|
792
|
+
const hasTransformKeys = transformKeyList.length > 0;
|
|
793
|
+
const render = () => {
|
|
794
|
+
if (hasTransformKeys) {
|
|
795
|
+
const parts = transformKeyList.map((key) => formatTransformFunction(key, txProps[key]));
|
|
796
|
+
node.style.transform = parts.join(' ');
|
|
797
|
+
}
|
|
798
|
+
else if (typeof txProps.transform === 'string') {
|
|
799
|
+
node.style.transform = txProps.transform;
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
render();
|
|
803
|
+
const unsubs = [];
|
|
804
|
+
if (hasTransformKeys) {
|
|
805
|
+
for (const key of transformKeyList) {
|
|
806
|
+
const value = txProps[key];
|
|
807
|
+
if (value &&
|
|
808
|
+
typeof value.subscribe === 'function') {
|
|
809
|
+
unsubs.push(value.subscribe(render));
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
return unsubs;
|
|
814
|
+
}
|
|
815
|
+
function applyStyles(node, style) {
|
|
816
|
+
const subscriptions = [];
|
|
817
|
+
for (const [key, value] of Object.entries(style)) {
|
|
818
|
+
if (isAnimateValue(value)) {
|
|
819
|
+
subscriptions.push(value.subscribe((v) => applyStyleProp(node, key, v)));
|
|
820
|
+
}
|
|
821
|
+
else {
|
|
822
|
+
applyStyleProp(node, key, value);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return subscriptions;
|
|
826
|
+
}
|
|
827
|
+
function applyAttrs(node, props) {
|
|
828
|
+
const subscriptions = [];
|
|
829
|
+
for (const [key, value] of Object.entries(props)) {
|
|
830
|
+
const setBool = (v) => {
|
|
831
|
+
if (v)
|
|
832
|
+
node.setAttribute(key, '');
|
|
833
|
+
else
|
|
834
|
+
node.removeAttribute(key);
|
|
835
|
+
};
|
|
836
|
+
const setOther = (v) => {
|
|
837
|
+
node.setAttribute(key, String(v));
|
|
838
|
+
};
|
|
839
|
+
if (isAnimateValue(value)) {
|
|
840
|
+
subscriptions.push(value.subscribe((v) => {
|
|
841
|
+
if (typeof v === 'boolean')
|
|
842
|
+
setBool(v);
|
|
843
|
+
else if (typeof v === 'string')
|
|
844
|
+
setOther(v);
|
|
845
|
+
else if (typeof v === 'number')
|
|
846
|
+
setOther(v);
|
|
847
|
+
else
|
|
848
|
+
node.removeAttribute(key);
|
|
849
|
+
}));
|
|
850
|
+
}
|
|
851
|
+
else {
|
|
852
|
+
if (typeof value === 'boolean')
|
|
853
|
+
setBool(value);
|
|
854
|
+
else if (typeof value === 'string')
|
|
855
|
+
setOther(value);
|
|
856
|
+
else if (typeof value === 'number')
|
|
857
|
+
setOther(value);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
return subscriptions;
|
|
861
|
+
}
|
|
862
|
+
function applyTransforms(elRef, txProps) {
|
|
863
|
+
return applyTransformsStyle(elRef, txProps);
|
|
864
|
+
}
|
|
865
|
+
function createTransformRenderer(node, animateValues) {
|
|
866
|
+
return () => {
|
|
867
|
+
const transformKeyList = Object.keys(animateValues).filter(isTransformKey);
|
|
868
|
+
if (transformKeyList.length > 0) {
|
|
869
|
+
const parts = transformKeyList.map((key) => formatTransformFunction(key, animateValues[key]));
|
|
870
|
+
node.style.transform = parts.join(' ');
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
}function clamp(value, lowerbound, upperbound) {
|
|
874
|
+
return Math.min(Math.max(value, lowerbound), upperbound);
|
|
875
|
+
}
|
|
876
|
+
function rubber2(distanceFromEdge, constant) {
|
|
877
|
+
return Math.pow(distanceFromEdge, constant * 5);
|
|
878
|
+
}
|
|
879
|
+
function rubber(distanceFromEdge, dimension, constant) {
|
|
880
|
+
if (dimension === 0 || Math.abs(dimension) === Infinity)
|
|
881
|
+
return rubber2(distanceFromEdge, constant);
|
|
882
|
+
return ((distanceFromEdge * dimension * constant) /
|
|
883
|
+
(dimension + constant * distanceFromEdge));
|
|
884
|
+
}
|
|
885
|
+
function rubberClamp(value, lowerbound, upperbound, constant = 0.15) {
|
|
886
|
+
if (constant === 0)
|
|
887
|
+
return clamp(value, lowerbound, upperbound);
|
|
888
|
+
if (value < lowerbound) {
|
|
889
|
+
return (-rubber(lowerbound - value, upperbound - lowerbound, constant) +
|
|
890
|
+
lowerbound);
|
|
891
|
+
}
|
|
892
|
+
if (value > upperbound) {
|
|
893
|
+
return (+rubber(value - upperbound, upperbound - lowerbound, constant) +
|
|
894
|
+
upperbound);
|
|
895
|
+
}
|
|
896
|
+
return value;
|
|
897
|
+
}
|
|
898
|
+
function snapTo(value, velocity, snapPoints) {
|
|
899
|
+
const finalValue = value + velocity * 0.2;
|
|
900
|
+
const getDiff = (point) => Math.abs(point - finalValue);
|
|
901
|
+
const deltas = snapPoints.map(getDiff);
|
|
902
|
+
const minDelta = Math.min(...deltas);
|
|
903
|
+
return snapPoints.reduce(function (acc, point) {
|
|
904
|
+
if (getDiff(point) === minDelta) {
|
|
905
|
+
return point;
|
|
906
|
+
}
|
|
907
|
+
else {
|
|
908
|
+
return acc;
|
|
909
|
+
}
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
function move(array, moveIndex, toIndex) {
|
|
913
|
+
const item = array[moveIndex];
|
|
914
|
+
const length = array.length;
|
|
915
|
+
const diff = moveIndex - toIndex;
|
|
916
|
+
if (diff > 0) {
|
|
917
|
+
return [
|
|
918
|
+
...array.slice(0, toIndex),
|
|
919
|
+
item,
|
|
920
|
+
...array.slice(toIndex, moveIndex),
|
|
921
|
+
...array.slice(moveIndex + 1, length),
|
|
922
|
+
];
|
|
923
|
+
}
|
|
924
|
+
else if (diff < 0) {
|
|
925
|
+
const targetIndex = toIndex + 1;
|
|
926
|
+
return [
|
|
927
|
+
...array.slice(0, moveIndex),
|
|
928
|
+
...array.slice(moveIndex + 1, targetIndex),
|
|
929
|
+
item,
|
|
930
|
+
...array.slice(targetIndex, length),
|
|
931
|
+
];
|
|
932
|
+
}
|
|
933
|
+
return array;
|
|
934
|
+
}class DecayController {
|
|
935
|
+
constructor(value, velocity, deceleration, hooks) {
|
|
936
|
+
this.value = value;
|
|
937
|
+
this.velocity = velocity;
|
|
938
|
+
this.deceleration = deceleration;
|
|
939
|
+
this.hooks = hooks;
|
|
940
|
+
this.restSpeed = 0.01;
|
|
941
|
+
this.isPaused = false;
|
|
942
|
+
this.isCancelled = false;
|
|
943
|
+
this.pausedAt = 0;
|
|
944
|
+
this.animate = (now) => {
|
|
945
|
+
if (this.isCancelled || this.isPaused)
|
|
946
|
+
return;
|
|
947
|
+
const elapsed = now - this.startTime;
|
|
948
|
+
const k = 1 - this.deceleration;
|
|
949
|
+
const currentVelocity = this.velocity * Math.exp(-k * elapsed);
|
|
950
|
+
this.position =
|
|
951
|
+
this.from + (this.velocity / k) * (1 - Math.exp(-k * elapsed));
|
|
952
|
+
if (this.clampBounds) {
|
|
953
|
+
const [min, max] = this.clampBounds;
|
|
954
|
+
if (this.elasticConstant !== undefined) {
|
|
955
|
+
this.position = rubberClamp(this.position, min, max, this.elasticConstant);
|
|
956
|
+
}
|
|
957
|
+
else {
|
|
958
|
+
if (this.position < min) {
|
|
959
|
+
this.position = min;
|
|
960
|
+
}
|
|
961
|
+
else if (this.position > max) {
|
|
962
|
+
this.position = max;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
this.value._internalSet(this.position);
|
|
967
|
+
this.hooks.onChange?.(this.position);
|
|
968
|
+
if (Math.abs(currentVelocity) > this.restSpeed) {
|
|
969
|
+
this.frameId = requestAnimationFrame(this.animate);
|
|
970
|
+
}
|
|
971
|
+
else {
|
|
972
|
+
cancelAnimationFrame(this.frameId);
|
|
973
|
+
this.hooks.onChange?.(this.position);
|
|
974
|
+
this.hooks.onComplete?.();
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
this.clampBounds = hooks.clamp;
|
|
978
|
+
if (hooks.elastic === true) {
|
|
979
|
+
this.elasticConstant = 0.15;
|
|
980
|
+
}
|
|
981
|
+
else if (typeof hooks.elastic === 'number') {
|
|
982
|
+
this.elasticConstant = hooks.elastic;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
start() {
|
|
986
|
+
const previous = this.value.getAnimationController();
|
|
987
|
+
if (previous instanceof DecayController) {
|
|
988
|
+
this.velocity = previous.velocity;
|
|
989
|
+
this.deceleration = previous.deceleration;
|
|
990
|
+
}
|
|
991
|
+
this.hooks.onStart?.();
|
|
992
|
+
this.value.setAnimationController(this);
|
|
993
|
+
this.isPaused = false;
|
|
994
|
+
this.isCancelled = false;
|
|
995
|
+
this.from = this.position = this.value.current;
|
|
996
|
+
this.startTime = performance.now();
|
|
997
|
+
this.frameId = requestAnimationFrame(this.animate);
|
|
998
|
+
}
|
|
999
|
+
pause() {
|
|
1000
|
+
if (this.isCancelled || this.isPaused)
|
|
1001
|
+
return;
|
|
1002
|
+
this.isPaused = true;
|
|
1003
|
+
this.pausedAt = performance.now();
|
|
1004
|
+
cancelAnimationFrame(this.frameId);
|
|
1005
|
+
this.hooks.onPause?.();
|
|
1006
|
+
}
|
|
1007
|
+
resume() {
|
|
1008
|
+
if (this.isCancelled || !this.isPaused)
|
|
1009
|
+
return;
|
|
1010
|
+
const now = performance.now();
|
|
1011
|
+
this.startTime += now - this.pausedAt;
|
|
1012
|
+
this.isPaused = false;
|
|
1013
|
+
this.hooks.onResume?.();
|
|
1014
|
+
this.frameId = requestAnimationFrame(this.animate);
|
|
1015
|
+
}
|
|
1016
|
+
cancel() {
|
|
1017
|
+
this.isCancelled = true;
|
|
1018
|
+
cancelAnimationFrame(this.frameId);
|
|
1019
|
+
}
|
|
1020
|
+
reset() {
|
|
1021
|
+
this.cancel();
|
|
1022
|
+
this.isPaused = false;
|
|
1023
|
+
this.pausedAt = 0;
|
|
1024
|
+
this.startTime = 0;
|
|
1025
|
+
}
|
|
1026
|
+
setOnComplete(fn) {
|
|
1027
|
+
this.hooks.onComplete = fn;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
function decay(value, velocity, options = {}) {
|
|
1031
|
+
const { decay = 0.998, ...hooks } = options;
|
|
1032
|
+
return new DecayController(value, velocity, decay, hooks);
|
|
1033
|
+
}function withInterpolation$1(value, target, options, factory) {
|
|
1034
|
+
if (typeof value.current === 'number' && typeof target === 'number') {
|
|
1035
|
+
return factory(value, target, options);
|
|
1036
|
+
}
|
|
1037
|
+
if (typeof value.current === 'string' && typeof target === 'string') {
|
|
1038
|
+
try {
|
|
1039
|
+
const progress = new AnimateValue(0);
|
|
1040
|
+
const interpolated = progress.to([0, 1], [value.current, target]);
|
|
1041
|
+
const controller = factory(progress, 1, options);
|
|
1042
|
+
value.setAnimationController(controller);
|
|
1043
|
+
progress.subscribe(() => {
|
|
1044
|
+
if (value.getAnimationController() === controller) {
|
|
1045
|
+
value._internalSet(interpolated.current);
|
|
1046
|
+
}
|
|
1047
|
+
});
|
|
1048
|
+
return controller;
|
|
1049
|
+
}
|
|
1050
|
+
catch (err) {
|
|
1051
|
+
throw new Error(`[spring] Cannot animate from "${value.current}" to "${target}": ${err.message}`);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
throw new Error(`[spring] Unsupported type: ${typeof value.current} → ${typeof target}`);
|
|
1055
|
+
}
|
|
1056
|
+
class SpringController {
|
|
1057
|
+
constructor(value, target, stiffness, damping, mass, hooks) {
|
|
1058
|
+
this.value = value;
|
|
1059
|
+
this.target = target;
|
|
1060
|
+
this.stiffness = stiffness;
|
|
1061
|
+
this.damping = damping;
|
|
1062
|
+
this.mass = mass;
|
|
1063
|
+
this.hooks = hooks;
|
|
1064
|
+
this.velocity = 0;
|
|
1065
|
+
this.restDisplacement = 0.001;
|
|
1066
|
+
this.restSpeed = 0.001;
|
|
1067
|
+
this.isPaused = false;
|
|
1068
|
+
this.isCancelled = false;
|
|
1069
|
+
this.animate = () => {
|
|
1070
|
+
if (this.isCancelled || this.isPaused)
|
|
1071
|
+
return;
|
|
1072
|
+
const now = Date.now();
|
|
1073
|
+
const deltaTime = Math.min(now - this.startTime, 64);
|
|
1074
|
+
this.startTime = now;
|
|
1075
|
+
const c = this.damping;
|
|
1076
|
+
const m = this.mass;
|
|
1077
|
+
const k = this.stiffness;
|
|
1078
|
+
const v0 = -this.velocity;
|
|
1079
|
+
const x0 = this.target - this.position;
|
|
1080
|
+
const zeta = c / (2 * Math.sqrt(k * m));
|
|
1081
|
+
const omega0 = Math.sqrt(k / m);
|
|
1082
|
+
const omega1 = omega0 * Math.sqrt(1 - zeta ** 2);
|
|
1083
|
+
const t = deltaTime / 1000;
|
|
1084
|
+
const sin1 = Math.sin(omega1 * t);
|
|
1085
|
+
const cos1 = Math.cos(omega1 * t);
|
|
1086
|
+
const underDampedEnvelope = Math.exp(-zeta * omega0 * t);
|
|
1087
|
+
const underDampedFrag1 = underDampedEnvelope *
|
|
1088
|
+
(sin1 * ((v0 + zeta * omega0 * x0) / omega1) + x0 * cos1);
|
|
1089
|
+
const underDampedPosition = this.target - underDampedFrag1;
|
|
1090
|
+
const underDampedVelocity = zeta * omega0 * underDampedFrag1 -
|
|
1091
|
+
underDampedEnvelope *
|
|
1092
|
+
(cos1 * (v0 + zeta * omega0 * x0) - omega1 * x0 * sin1);
|
|
1093
|
+
const criticallyDampedEnvelope = Math.exp(-omega0 * t);
|
|
1094
|
+
const criticallyDampedPosition = this.target - criticallyDampedEnvelope * (x0 + (v0 + omega0 * x0) * t);
|
|
1095
|
+
const criticallyDampedVelocity = criticallyDampedEnvelope *
|
|
1096
|
+
(v0 * (t * omega0 - 1) + t * x0 * omega0 * omega0);
|
|
1097
|
+
this.value._internalSet(this.position);
|
|
1098
|
+
this.hooks.onChange?.(this.position);
|
|
1099
|
+
const isVelocity = Math.abs(this.velocity) < this.restSpeed;
|
|
1100
|
+
const isDisplacement = this.stiffness === 0 ||
|
|
1101
|
+
Math.abs(this.target - this.position) < this.restDisplacement;
|
|
1102
|
+
if (zeta < 1) {
|
|
1103
|
+
this.position = underDampedPosition;
|
|
1104
|
+
this.velocity = underDampedVelocity;
|
|
1105
|
+
}
|
|
1106
|
+
else {
|
|
1107
|
+
this.position = criticallyDampedPosition;
|
|
1108
|
+
this.velocity = criticallyDampedVelocity;
|
|
1109
|
+
}
|
|
1110
|
+
if (isVelocity && isDisplacement) {
|
|
1111
|
+
this.velocity = 0;
|
|
1112
|
+
this.position = this.target;
|
|
1113
|
+
this.value._internalSet(this.position);
|
|
1114
|
+
this.hooks.onChange?.(this.position);
|
|
1115
|
+
this.hooks.onComplete?.();
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
this.frameId = requestAnimationFrame(this.animate);
|
|
1119
|
+
};
|
|
1120
|
+
this.explicitFrom = hooks.from;
|
|
1121
|
+
}
|
|
1122
|
+
start() {
|
|
1123
|
+
// If explicit 'from' is provided, always use it (for loops, sequences, etc.)
|
|
1124
|
+
if (this.explicitFrom !== undefined) {
|
|
1125
|
+
this.position = this.startPosition = this.explicitFrom;
|
|
1126
|
+
this.value._internalSet(this.explicitFrom);
|
|
1127
|
+
this.velocity = 0;
|
|
1128
|
+
this.startTime = Date.now();
|
|
1129
|
+
}
|
|
1130
|
+
else {
|
|
1131
|
+
// Otherwise, try to inherit from previous controller for smooth chaining
|
|
1132
|
+
const previous = this.value.getAnimationController();
|
|
1133
|
+
if (previous instanceof SpringController) {
|
|
1134
|
+
this.position = previous.position;
|
|
1135
|
+
this.velocity = previous.velocity;
|
|
1136
|
+
this.startTime = previous.startTime;
|
|
1137
|
+
}
|
|
1138
|
+
else {
|
|
1139
|
+
this.position = this.startPosition = this.value.current;
|
|
1140
|
+
this.velocity = 0;
|
|
1141
|
+
this.startTime = Date.now();
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
this.hooks.onStart?.();
|
|
1145
|
+
this.value.setAnimationController(this);
|
|
1146
|
+
this.isPaused = false;
|
|
1147
|
+
this.isCancelled = false;
|
|
1148
|
+
this.frameId = requestAnimationFrame(this.animate);
|
|
1149
|
+
}
|
|
1150
|
+
pause() {
|
|
1151
|
+
if (this.isCancelled || this.isPaused)
|
|
1152
|
+
return;
|
|
1153
|
+
this.isPaused = true;
|
|
1154
|
+
cancelAnimationFrame(this.frameId);
|
|
1155
|
+
this.hooks.onPause?.();
|
|
1156
|
+
}
|
|
1157
|
+
resume() {
|
|
1158
|
+
if (this.isCancelled || !this.isPaused)
|
|
1159
|
+
return;
|
|
1160
|
+
this.isPaused = false;
|
|
1161
|
+
this.hooks.onResume?.();
|
|
1162
|
+
this.frameId = requestAnimationFrame(this.animate);
|
|
1163
|
+
}
|
|
1164
|
+
cancel() {
|
|
1165
|
+
this.isCancelled = true;
|
|
1166
|
+
cancelAnimationFrame(this.frameId);
|
|
1167
|
+
}
|
|
1168
|
+
reset() {
|
|
1169
|
+
this.cancel();
|
|
1170
|
+
this.isPaused = false;
|
|
1171
|
+
this.velocity = 0;
|
|
1172
|
+
this.position = this.startPosition;
|
|
1173
|
+
this.startTime = Date.now();
|
|
1174
|
+
}
|
|
1175
|
+
setOnComplete(fn) {
|
|
1176
|
+
this.hooks.onComplete = fn;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
function spring(value, target, options = {}) {
|
|
1180
|
+
return withInterpolation$1(value, target, options, (v, t, opts) => {
|
|
1181
|
+
const { stiffness = 170, damping = 14, mass = 1, from, ...hooks } = opts;
|
|
1182
|
+
return new SpringController(v, t, stiffness, damping, mass, { ...hooks, from });
|
|
1183
|
+
});
|
|
1184
|
+
}/**
|
|
1185
|
+
* Copyright (c) 2015-present, Facebook, Inc.
|
|
1186
|
+
* All rights reserved.
|
|
1187
|
+
*
|
|
1188
|
+
* This source code is licensed under the MIT license found in the
|
|
1189
|
+
* LICENSE file in the root directory of this source tree.
|
|
1190
|
+
*
|
|
1191
|
+
*/
|
|
1192
|
+
/**
|
|
1193
|
+
* https://github.com/gre/bezier-easing
|
|
1194
|
+
* BezierEasing - use bezier curve for transition easing function
|
|
1195
|
+
* by Gaëtan Renaudeau 2014 - 2015 – MIT License
|
|
1196
|
+
*/
|
|
1197
|
+
// These values are established by empiricism with tests (tradeoff: performance VS precision)
|
|
1198
|
+
var NEWTON_ITERATIONS = 4;
|
|
1199
|
+
var NEWTON_MIN_SLOPE = 0.001;
|
|
1200
|
+
var SUBDIVISION_PRECISION = 0.0000001;
|
|
1201
|
+
var SUBDIVISION_MAX_ITERATIONS = 10;
|
|
1202
|
+
var kSplineTableSize = 11;
|
|
1203
|
+
var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);
|
|
1204
|
+
var float32ArraySupported = typeof Float32Array === "function";
|
|
1205
|
+
function A(aA1, aA2) {
|
|
1206
|
+
return 1.0 - 3.0 * aA2 + 3.0 * aA1;
|
|
1207
|
+
}
|
|
1208
|
+
function B(aA1, aA2) {
|
|
1209
|
+
return 3.0 * aA2 - 6.0 * aA1;
|
|
1210
|
+
}
|
|
1211
|
+
function C(aA1) {
|
|
1212
|
+
return 3.0 * aA1;
|
|
1213
|
+
}
|
|
1214
|
+
function calcBezier(aT, aA1, aA2) {
|
|
1215
|
+
return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
|
|
1216
|
+
}
|
|
1217
|
+
function getSlope(aT, aA1, aA2) {
|
|
1218
|
+
return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
|
|
1219
|
+
}
|
|
1220
|
+
function binarySubdivide(aX, aA, aB, mX1, mX2) {
|
|
1221
|
+
var currentX, currentT, i = 0;
|
|
1222
|
+
do {
|
|
1223
|
+
currentT = aA + (aB - aA) / 2.0;
|
|
1224
|
+
currentX = calcBezier(currentT, mX1, mX2) - aX;
|
|
1225
|
+
if (currentX > 0.0) {
|
|
1226
|
+
aB = currentT;
|
|
1227
|
+
}
|
|
1228
|
+
else {
|
|
1229
|
+
aA = currentT;
|
|
1230
|
+
}
|
|
1231
|
+
} while (Math.abs(currentX) > SUBDIVISION_PRECISION &&
|
|
1232
|
+
++i < SUBDIVISION_MAX_ITERATIONS);
|
|
1233
|
+
return currentT;
|
|
1234
|
+
}
|
|
1235
|
+
function newtonRaphsonIterate(aX, aGuessT, mX1, mX2) {
|
|
1236
|
+
for (var i = 0; i < NEWTON_ITERATIONS; ++i) {
|
|
1237
|
+
var currentSlope = getSlope(aGuessT, mX1, mX2);
|
|
1238
|
+
if (currentSlope === 0.0) {
|
|
1239
|
+
return aGuessT;
|
|
1240
|
+
}
|
|
1241
|
+
var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
|
|
1242
|
+
aGuessT -= currentX / currentSlope;
|
|
1243
|
+
}
|
|
1244
|
+
return aGuessT;
|
|
1245
|
+
}
|
|
1246
|
+
function LinearEasing(x) {
|
|
1247
|
+
return x;
|
|
1248
|
+
}
|
|
1249
|
+
function bezier(mX1, mY1, mX2, mY2) {
|
|
1250
|
+
if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) {
|
|
1251
|
+
throw new Error("bezier x values must be in [0, 1] range");
|
|
1252
|
+
}
|
|
1253
|
+
if (mX1 === mY1 && mX2 === mY2) {
|
|
1254
|
+
return LinearEasing;
|
|
1255
|
+
}
|
|
1256
|
+
var sampleValues = float32ArraySupported
|
|
1257
|
+
? new Float32Array(kSplineTableSize)
|
|
1258
|
+
: new Array(kSplineTableSize);
|
|
1259
|
+
for (var i = 0; i < kSplineTableSize; ++i) {
|
|
1260
|
+
sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
|
|
1261
|
+
}
|
|
1262
|
+
function getTForX(aX) {
|
|
1263
|
+
var intervalStart = 0.0;
|
|
1264
|
+
var currentSample = 1;
|
|
1265
|
+
var lastSample = kSplineTableSize - 1;
|
|
1266
|
+
for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
|
|
1267
|
+
intervalStart += kSampleStepSize;
|
|
1268
|
+
}
|
|
1269
|
+
--currentSample;
|
|
1270
|
+
var dist = (aX - sampleValues[currentSample]) /
|
|
1271
|
+
(sampleValues[currentSample + 1] - sampleValues[currentSample]);
|
|
1272
|
+
var guessForT = intervalStart + dist * kSampleStepSize;
|
|
1273
|
+
var initialSlope = getSlope(guessForT, mX1, mX2);
|
|
1274
|
+
if (initialSlope >= NEWTON_MIN_SLOPE) {
|
|
1275
|
+
return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
|
|
1276
|
+
}
|
|
1277
|
+
else if (initialSlope === 0.0) {
|
|
1278
|
+
return guessForT;
|
|
1279
|
+
}
|
|
1280
|
+
else {
|
|
1281
|
+
return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
return function BezierEasing(x) {
|
|
1285
|
+
// Because JavaScript number are imprecise, we should guarantee the extremes are right.
|
|
1286
|
+
if (x === 0 || x === 1) {
|
|
1287
|
+
return x;
|
|
1288
|
+
}
|
|
1289
|
+
return calcBezier(getTForX(x), mY1, mY2);
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
/**
|
|
1293
|
+
* This class implements common easing functions. The math is pretty obscure,
|
|
1294
|
+
* but this cool website has nice visual illustrations of what they represent:
|
|
1295
|
+
* http://xaedes.de/dev/transitions/
|
|
1296
|
+
*/
|
|
1297
|
+
class Easing {
|
|
1298
|
+
static step0(n) {
|
|
1299
|
+
return n > 0 ? 1 : 0;
|
|
1300
|
+
}
|
|
1301
|
+
static step1(n) {
|
|
1302
|
+
return n >= 1 ? 1 : 0;
|
|
1303
|
+
}
|
|
1304
|
+
static linear(t) {
|
|
1305
|
+
return t;
|
|
1306
|
+
}
|
|
1307
|
+
static ease(t) {
|
|
1308
|
+
return ease(t);
|
|
1309
|
+
}
|
|
1310
|
+
static quad(t) {
|
|
1311
|
+
return t * t;
|
|
1312
|
+
}
|
|
1313
|
+
static cubic(t) {
|
|
1314
|
+
return t * t * t;
|
|
1315
|
+
}
|
|
1316
|
+
static poly(n) {
|
|
1317
|
+
return (t) => Math.pow(t, n);
|
|
1318
|
+
}
|
|
1319
|
+
static sin(t) {
|
|
1320
|
+
return 1 - Math.cos((t * Math.PI) / 2);
|
|
1321
|
+
}
|
|
1322
|
+
static circle(t) {
|
|
1323
|
+
return 1 - Math.sqrt(1 - t * t);
|
|
1324
|
+
}
|
|
1325
|
+
static exp(t) {
|
|
1326
|
+
return Math.pow(2, 10 * (t - 1));
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* A simple elastic interaction, similar to a spring. Default bounciness
|
|
1330
|
+
* is 1, which overshoots a little bit once. 0 bounciness doesn't overshoot
|
|
1331
|
+
* at all, and bounciness of N > 1 will overshoot about N times.
|
|
1332
|
+
*
|
|
1333
|
+
* Wolfram Plots:
|
|
1334
|
+
*
|
|
1335
|
+
* http://tiny.cc/elastic_b_1 (default bounciness = 1)
|
|
1336
|
+
* http://tiny.cc/elastic_b_3 (bounciness = 3)
|
|
1337
|
+
*/
|
|
1338
|
+
static elastic(bounciness = 1) {
|
|
1339
|
+
var p = bounciness * Math.PI;
|
|
1340
|
+
return (t) => 1 - Math.pow(Math.cos((t * Math.PI) / 2), 3) * Math.cos(t * p);
|
|
1341
|
+
}
|
|
1342
|
+
static back(s) {
|
|
1343
|
+
if (s === undefined) {
|
|
1344
|
+
s = 1.70158;
|
|
1345
|
+
}
|
|
1346
|
+
return (t) => t * t * ((s + 1) * t - s);
|
|
1347
|
+
}
|
|
1348
|
+
static bounce(t) {
|
|
1349
|
+
if (t < 1 / 2.75) {
|
|
1350
|
+
return 7.5625 * t * t;
|
|
1351
|
+
}
|
|
1352
|
+
if (t < 2 / 2.75) {
|
|
1353
|
+
t -= 1.5 / 2.75;
|
|
1354
|
+
return 7.5625 * t * t + 0.75;
|
|
1355
|
+
}
|
|
1356
|
+
if (t < 2.5 / 2.75) {
|
|
1357
|
+
t -= 2.25 / 2.75;
|
|
1358
|
+
return 7.5625 * t * t + 0.9375;
|
|
1359
|
+
}
|
|
1360
|
+
t -= 2.625 / 2.75;
|
|
1361
|
+
return 7.5625 * t * t + 0.984375;
|
|
1362
|
+
}
|
|
1363
|
+
static bezier(x1, y1, x2, y2) {
|
|
1364
|
+
return bezier(x1, y1, x2, y2);
|
|
1365
|
+
}
|
|
1366
|
+
static in(easing) {
|
|
1367
|
+
return easing;
|
|
1368
|
+
}
|
|
1369
|
+
/**
|
|
1370
|
+
* Runs an easing function backwards.
|
|
1371
|
+
*/
|
|
1372
|
+
static out(easing) {
|
|
1373
|
+
return (t) => 1 - easing(1 - t);
|
|
1374
|
+
}
|
|
1375
|
+
/**
|
|
1376
|
+
* Makes any easing function symmetrical.
|
|
1377
|
+
*/
|
|
1378
|
+
static inOut(easing) {
|
|
1379
|
+
return (t) => {
|
|
1380
|
+
if (t < 0.5) {
|
|
1381
|
+
return easing(t * 2) / 2;
|
|
1382
|
+
}
|
|
1383
|
+
return 1 - easing((1 - t) * 2) / 2;
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
var ease = /*#__PURE__*/ Easing.bezier(0.42, 0, 1, 1);function withInterpolation(value, target, options, factory) {
|
|
1388
|
+
if (typeof value.current === 'number' && typeof target === 'number') {
|
|
1389
|
+
return factory(value, target, options);
|
|
1390
|
+
}
|
|
1391
|
+
if (typeof value.current === 'string' && typeof target === 'string') {
|
|
1392
|
+
try {
|
|
1393
|
+
const progress = new AnimateValue(0);
|
|
1394
|
+
const interpolated = progress.to([0, 1], [value.current, target]);
|
|
1395
|
+
const controller = factory(progress, 1, options);
|
|
1396
|
+
value.setAnimationController(controller);
|
|
1397
|
+
progress.subscribe(() => {
|
|
1398
|
+
if (value.getAnimationController() === controller) {
|
|
1399
|
+
value._internalSet(interpolated.current);
|
|
1400
|
+
}
|
|
1401
|
+
});
|
|
1402
|
+
return controller;
|
|
1403
|
+
}
|
|
1404
|
+
catch (err) {
|
|
1405
|
+
throw new Error(`[timing] Cannot animate from "${value.current}" to "${target}": ${err.message}`);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
throw new Error(`[timing] Unsupported type: ${typeof value.current} → ${typeof target}`);
|
|
1409
|
+
}
|
|
1410
|
+
class TimingController {
|
|
1411
|
+
constructor(value, target, duration = 300, easing = Easing.linear, hooks) {
|
|
1412
|
+
this.value = value;
|
|
1413
|
+
this.target = target;
|
|
1414
|
+
this.duration = duration;
|
|
1415
|
+
this.easing = easing;
|
|
1416
|
+
this.hooks = hooks;
|
|
1417
|
+
this.isPaused = false;
|
|
1418
|
+
this.isCancelled = false;
|
|
1419
|
+
this.pausedAt = null;
|
|
1420
|
+
this.elapsedBeforePause = 0;
|
|
1421
|
+
this.animate = (timestamp) => {
|
|
1422
|
+
if (this.isCancelled || this.isPaused)
|
|
1423
|
+
return;
|
|
1424
|
+
const elapsed = this.elapsedBeforePause + (timestamp - this.startTime);
|
|
1425
|
+
let progress = elapsed / this.duration;
|
|
1426
|
+
if (!Number.isFinite(progress))
|
|
1427
|
+
progress = progress === Infinity ? 1 : 0;
|
|
1428
|
+
const t = Math.min(1, Math.max(0, progress));
|
|
1429
|
+
if (t < 1) {
|
|
1430
|
+
this.position = this.fromValue + (this.target - this.fromValue) * this.easing(t);
|
|
1431
|
+
if (Math.abs(this.target - this.position) < 0.01) {
|
|
1432
|
+
this.position = this.target;
|
|
1433
|
+
this.value._internalSet(this.position);
|
|
1434
|
+
this.hooks.onChange?.(this.position);
|
|
1435
|
+
this.hooks.onComplete?.();
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
this.value._internalSet(this.position);
|
|
1439
|
+
this.hooks.onChange?.(this.position);
|
|
1440
|
+
this.frameId = requestAnimationFrame(this.animate);
|
|
1441
|
+
}
|
|
1442
|
+
else {
|
|
1443
|
+
this.position = this.target;
|
|
1444
|
+
this.value._internalSet(this.position);
|
|
1445
|
+
this.hooks.onChange?.(this.position);
|
|
1446
|
+
this.hooks.onComplete?.();
|
|
1447
|
+
}
|
|
1448
|
+
};
|
|
1449
|
+
this.explicitFrom = hooks.from;
|
|
1450
|
+
}
|
|
1451
|
+
start() {
|
|
1452
|
+
// If explicit 'from' is provided, always use it (for loops, sequences, etc.)
|
|
1453
|
+
if (this.explicitFrom !== undefined) {
|
|
1454
|
+
this.fromValue = this.position = this.explicitFrom;
|
|
1455
|
+
this.value._internalSet(this.explicitFrom);
|
|
1456
|
+
this.startTime = performance.now();
|
|
1457
|
+
}
|
|
1458
|
+
else {
|
|
1459
|
+
// Otherwise, try to inherit from previous controller for smooth chaining
|
|
1460
|
+
const previous = this.value.getAnimationController();
|
|
1461
|
+
if (previous instanceof TimingController &&
|
|
1462
|
+
!previous.isCancelled &&
|
|
1463
|
+
previous.target === this.target &&
|
|
1464
|
+
previous.startTime) {
|
|
1465
|
+
this.fromValue = previous.fromValue;
|
|
1466
|
+
this.startTime = previous.startTime;
|
|
1467
|
+
}
|
|
1468
|
+
else {
|
|
1469
|
+
this.fromValue = this.position = this.value.current;
|
|
1470
|
+
this.startTime = performance.now();
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
this.hooks.onStart?.();
|
|
1474
|
+
this.value.setAnimationController(this);
|
|
1475
|
+
this.isPaused = false;
|
|
1476
|
+
this.isCancelled = false;
|
|
1477
|
+
this.pausedAt = null;
|
|
1478
|
+
this.elapsedBeforePause = 0;
|
|
1479
|
+
this.frameId = requestAnimationFrame(this.animate);
|
|
1480
|
+
}
|
|
1481
|
+
pause() {
|
|
1482
|
+
if (this.isCancelled || this.isPaused)
|
|
1483
|
+
return;
|
|
1484
|
+
this.isPaused = true;
|
|
1485
|
+
this.pausedAt = performance.now();
|
|
1486
|
+
this.elapsedBeforePause += this.pausedAt - this.startTime;
|
|
1487
|
+
this.startTime = 0;
|
|
1488
|
+
cancelAnimationFrame(this.frameId);
|
|
1489
|
+
this.hooks.onPause?.();
|
|
1490
|
+
}
|
|
1491
|
+
resume() {
|
|
1492
|
+
if (this.isCancelled || !this.isPaused)
|
|
1493
|
+
return;
|
|
1494
|
+
this.isPaused = false;
|
|
1495
|
+
this.hooks.onResume?.();
|
|
1496
|
+
this.startTime = performance.now();
|
|
1497
|
+
this.pausedAt = null;
|
|
1498
|
+
this.frameId = requestAnimationFrame(this.animate);
|
|
1499
|
+
}
|
|
1500
|
+
cancel() {
|
|
1501
|
+
this.isCancelled = true;
|
|
1502
|
+
cancelAnimationFrame(this.frameId);
|
|
1503
|
+
}
|
|
1504
|
+
reset() {
|
|
1505
|
+
this.cancel();
|
|
1506
|
+
this.isPaused = false;
|
|
1507
|
+
this.pausedAt = null;
|
|
1508
|
+
this.elapsedBeforePause = 0;
|
|
1509
|
+
cancelAnimationFrame(this.frameId);
|
|
1510
|
+
}
|
|
1511
|
+
setOnComplete(fn) {
|
|
1512
|
+
this.hooks.onComplete = fn;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
function timing(value, target, options = {}) {
|
|
1516
|
+
return withInterpolation(value, target, options, (v, t, opts) => {
|
|
1517
|
+
const { duration = 300, easing = Easing.linear, from, ...hooks } = opts;
|
|
1518
|
+
return new TimingController(v, t, duration, easing, { ...hooks, from });
|
|
1519
|
+
});
|
|
1520
|
+
}class ParallelController {
|
|
1521
|
+
constructor(controllers, hooks = {}) {
|
|
1522
|
+
this.controllers = controllers;
|
|
1523
|
+
this.hooks = hooks;
|
|
1524
|
+
this.completedCount = 0;
|
|
1525
|
+
this.isPaused = false;
|
|
1526
|
+
this.isCancelled = false;
|
|
1527
|
+
this.originalCompletes = controllers.map((ctrl) => ctrl?.hooks?.onComplete);
|
|
1528
|
+
}
|
|
1529
|
+
start() {
|
|
1530
|
+
this.completedCount = 0;
|
|
1531
|
+
this.isPaused = false;
|
|
1532
|
+
this.isCancelled = false;
|
|
1533
|
+
this.hooks.onStart?.();
|
|
1534
|
+
this.controllers.forEach((controller, index) => {
|
|
1535
|
+
const original = this.originalCompletes[index];
|
|
1536
|
+
controller.setOnComplete?.(() => {
|
|
1537
|
+
original?.();
|
|
1538
|
+
this.handleComplete();
|
|
1539
|
+
});
|
|
1540
|
+
controller.start();
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
handleComplete() {
|
|
1544
|
+
if (this.isCancelled)
|
|
1545
|
+
return;
|
|
1546
|
+
this.completedCount += 1;
|
|
1547
|
+
if (this.completedCount === this.controllers.length) {
|
|
1548
|
+
this.completedCount = 0;
|
|
1549
|
+
this.hooks.onComplete?.();
|
|
1550
|
+
this.onComplete?.();
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
pause() {
|
|
1554
|
+
if (this.isCancelled || this.isPaused)
|
|
1555
|
+
return;
|
|
1556
|
+
this.isPaused = true;
|
|
1557
|
+
this.hooks.onPause?.();
|
|
1558
|
+
this.controllers.forEach((controller) => controller.pause());
|
|
1559
|
+
}
|
|
1560
|
+
resume() {
|
|
1561
|
+
if (this.isCancelled || !this.isPaused)
|
|
1562
|
+
return;
|
|
1563
|
+
this.isPaused = false;
|
|
1564
|
+
this.hooks.onResume?.();
|
|
1565
|
+
this.controllers.forEach((controller) => controller.resume());
|
|
1566
|
+
}
|
|
1567
|
+
cancel() {
|
|
1568
|
+
if (this.isCancelled)
|
|
1569
|
+
return;
|
|
1570
|
+
this.isCancelled = true;
|
|
1571
|
+
this.controllers.forEach((controller) => controller.cancel());
|
|
1572
|
+
}
|
|
1573
|
+
reset() {
|
|
1574
|
+
this.isCancelled = false;
|
|
1575
|
+
this.isPaused = false;
|
|
1576
|
+
this.completedCount = 0;
|
|
1577
|
+
this.controllers.forEach((controller) => controller.reset?.());
|
|
1578
|
+
}
|
|
1579
|
+
setOnComplete(fn) {
|
|
1580
|
+
this.onComplete = fn;
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
class SequenceController {
|
|
1584
|
+
constructor(controllers, hooks = {}) {
|
|
1585
|
+
this.controllers = controllers;
|
|
1586
|
+
this.hooks = hooks;
|
|
1587
|
+
this.index = 0;
|
|
1588
|
+
this.isPaused = false;
|
|
1589
|
+
this.isCancelled = false;
|
|
1590
|
+
this.current = null;
|
|
1591
|
+
this.originalCompletes = controllers.map((ctrl) => ctrl?.hooks?.onComplete);
|
|
1592
|
+
}
|
|
1593
|
+
runNext() {
|
|
1594
|
+
if (this.isCancelled || this.isPaused)
|
|
1595
|
+
return;
|
|
1596
|
+
const index = this.index++;
|
|
1597
|
+
const controller = this.controllers[index];
|
|
1598
|
+
if (!controller) {
|
|
1599
|
+
this.onComplete?.();
|
|
1600
|
+
this.hooks.onComplete?.();
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
this.current = controller;
|
|
1604
|
+
const original = this.originalCompletes[index];
|
|
1605
|
+
controller.setOnComplete?.(() => {
|
|
1606
|
+
original?.();
|
|
1607
|
+
this.runNext();
|
|
1608
|
+
});
|
|
1609
|
+
controller.start();
|
|
1610
|
+
}
|
|
1611
|
+
start() {
|
|
1612
|
+
this.index = 0;
|
|
1613
|
+
this.isPaused = false;
|
|
1614
|
+
this.isCancelled = false;
|
|
1615
|
+
this.hooks.onStart?.();
|
|
1616
|
+
this.runNext();
|
|
1617
|
+
}
|
|
1618
|
+
pause() {
|
|
1619
|
+
if (this.isCancelled)
|
|
1620
|
+
return;
|
|
1621
|
+
this.isPaused = true;
|
|
1622
|
+
this.current?.pause();
|
|
1623
|
+
this.hooks.onPause?.();
|
|
1624
|
+
}
|
|
1625
|
+
resume() {
|
|
1626
|
+
if (this.isCancelled || !this.isPaused)
|
|
1627
|
+
return;
|
|
1628
|
+
this.isPaused = false;
|
|
1629
|
+
this.current?.resume();
|
|
1630
|
+
this.hooks.onResume?.();
|
|
1631
|
+
}
|
|
1632
|
+
cancel() {
|
|
1633
|
+
this.isCancelled = true;
|
|
1634
|
+
this.current?.cancel();
|
|
1635
|
+
}
|
|
1636
|
+
reset() {
|
|
1637
|
+
this.isCancelled = false;
|
|
1638
|
+
this.isPaused = false;
|
|
1639
|
+
this.index = 0;
|
|
1640
|
+
this.controllers.forEach((controller) => controller.reset?.());
|
|
1641
|
+
}
|
|
1642
|
+
setOnComplete(fn) {
|
|
1643
|
+
this.onComplete = fn;
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
class LoopController {
|
|
1647
|
+
constructor(controller, iterations, hooks = {}) {
|
|
1648
|
+
this.controller = controller;
|
|
1649
|
+
this.iterations = iterations;
|
|
1650
|
+
this.hooks = hooks;
|
|
1651
|
+
this.count = 0;
|
|
1652
|
+
this.isCancelled = false;
|
|
1653
|
+
this.isPaused = false;
|
|
1654
|
+
this.handleIterationComplete = () => {
|
|
1655
|
+
this.count++;
|
|
1656
|
+
this.originalComplete?.();
|
|
1657
|
+
if (this.count < this.iterations) {
|
|
1658
|
+
this.controller.reset?.();
|
|
1659
|
+
this.runIteration();
|
|
1660
|
+
}
|
|
1661
|
+
else {
|
|
1662
|
+
this.onComplete?.();
|
|
1663
|
+
this.hooks.onComplete?.();
|
|
1664
|
+
}
|
|
1665
|
+
};
|
|
1666
|
+
this.originalComplete = controller?.hooks?.onComplete;
|
|
1667
|
+
}
|
|
1668
|
+
runIteration() {
|
|
1669
|
+
if (this.isCancelled || this.isPaused)
|
|
1670
|
+
return;
|
|
1671
|
+
this.controller.setOnComplete?.(this.handleIterationComplete);
|
|
1672
|
+
this.controller.start();
|
|
1673
|
+
}
|
|
1674
|
+
start() {
|
|
1675
|
+
this.isCancelled = false;
|
|
1676
|
+
this.isPaused = false;
|
|
1677
|
+
this.count = 0;
|
|
1678
|
+
this.controller.reset();
|
|
1679
|
+
this.hooks.onStart?.();
|
|
1680
|
+
this.runIteration();
|
|
1681
|
+
}
|
|
1682
|
+
pause() {
|
|
1683
|
+
this.isPaused = true;
|
|
1684
|
+
this.controller.pause();
|
|
1685
|
+
this.hooks.onPause?.();
|
|
1686
|
+
}
|
|
1687
|
+
resume() {
|
|
1688
|
+
if (this.isCancelled || !this.isPaused)
|
|
1689
|
+
return;
|
|
1690
|
+
this.isPaused = false;
|
|
1691
|
+
this.controller.resume();
|
|
1692
|
+
this.hooks.onResume?.();
|
|
1693
|
+
}
|
|
1694
|
+
cancel() {
|
|
1695
|
+
this.isCancelled = true;
|
|
1696
|
+
this.controller.cancel();
|
|
1697
|
+
}
|
|
1698
|
+
reset() {
|
|
1699
|
+
this.isCancelled = false;
|
|
1700
|
+
this.isPaused = false;
|
|
1701
|
+
this.count = 0;
|
|
1702
|
+
this.controller.reset?.();
|
|
1703
|
+
}
|
|
1704
|
+
setOnComplete(fn) {
|
|
1705
|
+
this.onComplete = fn;
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
class DelayController {
|
|
1709
|
+
constructor(duration) {
|
|
1710
|
+
this.duration = duration;
|
|
1711
|
+
}
|
|
1712
|
+
start() {
|
|
1713
|
+
this.timerId = window.setTimeout(() => {
|
|
1714
|
+
this.onComplete?.();
|
|
1715
|
+
}, this.duration);
|
|
1716
|
+
}
|
|
1717
|
+
pause() { }
|
|
1718
|
+
resume() { }
|
|
1719
|
+
cancel() {
|
|
1720
|
+
if (this.timerId)
|
|
1721
|
+
clearTimeout(this.timerId);
|
|
1722
|
+
}
|
|
1723
|
+
reset() {
|
|
1724
|
+
this.cancel();
|
|
1725
|
+
}
|
|
1726
|
+
setOnComplete(fn) {
|
|
1727
|
+
this.onComplete = fn;
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
function parallel(controllers, hooks = {}) {
|
|
1731
|
+
return new ParallelController(controllers, hooks);
|
|
1732
|
+
}
|
|
1733
|
+
function sequence(controllers, hooks = {}) {
|
|
1734
|
+
return new SequenceController(controllers, hooks);
|
|
1735
|
+
}
|
|
1736
|
+
function loop(controller, iterations, hooks = {}) {
|
|
1737
|
+
return new LoopController(controller, iterations, hooks);
|
|
1738
|
+
}
|
|
1739
|
+
function delay(duration) {
|
|
1740
|
+
return new DelayController(duration);
|
|
1741
|
+
}function filterCallbackOptions(options = {}, attach) {
|
|
1742
|
+
if (attach)
|
|
1743
|
+
return options;
|
|
1744
|
+
const { onStart, onChange, onComplete, ...rest } = options;
|
|
1745
|
+
return rest;
|
|
1746
|
+
}
|
|
1747
|
+
function isDescriptor(x) {
|
|
1748
|
+
return (typeof x === 'object' &&
|
|
1749
|
+
x !== null &&
|
|
1750
|
+
'type' in x &&
|
|
1751
|
+
typeof x.type === 'string');
|
|
1752
|
+
}function buildAnimation(value, { type, to, options = {} }) {
|
|
1753
|
+
switch (type) {
|
|
1754
|
+
case 'spring':
|
|
1755
|
+
return spring(value, to, options);
|
|
1756
|
+
case 'timing':
|
|
1757
|
+
return timing(value, to, options);
|
|
1758
|
+
case 'decay':
|
|
1759
|
+
return decay(value, options.velocity ?? 0, options);
|
|
1760
|
+
case 'delay':
|
|
1761
|
+
return delay(options.delay ?? 0);
|
|
1762
|
+
case 'sequence': {
|
|
1763
|
+
const animations = options.animations ?? [];
|
|
1764
|
+
const controllers = animations.map((step) => buildAnimation(value, step));
|
|
1765
|
+
return sequence(controllers, options);
|
|
1766
|
+
}
|
|
1767
|
+
case 'loop': {
|
|
1768
|
+
const innerDesc = options.animation;
|
|
1769
|
+
if (!innerDesc) {
|
|
1770
|
+
console.warn('[buildAnimation] loop missing `animation` descriptor');
|
|
1771
|
+
return { start() { }, pause() { }, resume() { }, cancel() { }, reset() { } };
|
|
1772
|
+
}
|
|
1773
|
+
let innerController;
|
|
1774
|
+
// Each loop iteration restarts from the AnimateValue's initial value
|
|
1775
|
+
// (not its current value) unless a step already sets an explicit `from`.
|
|
1776
|
+
const loopFromValue = value.initial;
|
|
1777
|
+
if (innerDesc.type === 'sequence') {
|
|
1778
|
+
const animations = innerDesc.options?.animations ?? [];
|
|
1779
|
+
const controllers = animations.map((step, index) => {
|
|
1780
|
+
if (index === 0 && (step.type === 'spring' || step.type === 'timing')) {
|
|
1781
|
+
const explicitFrom = step.options?.from;
|
|
1782
|
+
return buildAnimation(value, {
|
|
1783
|
+
...step,
|
|
1784
|
+
options: { ...step.options, from: explicitFrom ?? loopFromValue },
|
|
1785
|
+
});
|
|
1786
|
+
}
|
|
1787
|
+
return buildAnimation(value, step);
|
|
1788
|
+
});
|
|
1789
|
+
innerController = sequence(controllers, innerDesc.options);
|
|
1790
|
+
}
|
|
1791
|
+
else if (innerDesc.type === 'spring' || innerDesc.type === 'timing') {
|
|
1792
|
+
const explicitFrom = innerDesc.options?.from;
|
|
1793
|
+
innerController = buildAnimation(value, {
|
|
1794
|
+
...innerDesc,
|
|
1795
|
+
options: { ...innerDesc.options, from: explicitFrom ?? loopFromValue },
|
|
1796
|
+
});
|
|
1797
|
+
}
|
|
1798
|
+
else {
|
|
1799
|
+
innerController = buildAnimation(value, innerDesc);
|
|
1800
|
+
}
|
|
1801
|
+
return loop(innerController, options.iterations ?? 0, options);
|
|
1802
|
+
}
|
|
1803
|
+
default:
|
|
1804
|
+
console.warn(`Unsupported animation type: ${type}`);
|
|
1805
|
+
return { start() { }, pause() { }, resume() { }, cancel() { }, reset() { } };
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
function buildParallel(valueMap, step) {
|
|
1809
|
+
const entries = Object.entries(valueMap).filter(([key]) => {
|
|
1810
|
+
return (step.type === 'decay' ||
|
|
1811
|
+
step.type === 'delay' ||
|
|
1812
|
+
step.to[key] !== undefined);
|
|
1813
|
+
});
|
|
1814
|
+
const controllers = entries.map(([key, value], idx) => buildAnimation(value, {
|
|
1815
|
+
type: step.type,
|
|
1816
|
+
to: step.type === 'decay' || step.type === 'delay'
|
|
1817
|
+
? step.to
|
|
1818
|
+
: step.to[key],
|
|
1819
|
+
options: filterCallbackOptions(step.options, idx === 0),
|
|
1820
|
+
}));
|
|
1821
|
+
return parallel(controllers);
|
|
1822
|
+
}const PresenceContext = createContext(null);
|
|
1823
|
+
/**
|
|
1824
|
+
* Hook to access presence state from within an animated component.
|
|
1825
|
+
*/
|
|
1826
|
+
function usePresence() {
|
|
1827
|
+
const context = useContext(PresenceContext);
|
|
1828
|
+
if (!context)
|
|
1829
|
+
return [true, () => { }];
|
|
1830
|
+
return [!context.isExiting, context.onExitComplete];
|
|
1831
|
+
}
|
|
1832
|
+
/**
|
|
1833
|
+
* Hook to check if this is the initial mount (for skipping initial animations).
|
|
1834
|
+
*/
|
|
1835
|
+
function useIsPresent() {
|
|
1836
|
+
const context = useContext(PresenceContext);
|
|
1837
|
+
return context ? !context.isExiting : true;
|
|
1838
|
+
}
|
|
1839
|
+
// `exitingState` forces a re-render once `isExiting` flips, so this subtree
|
|
1840
|
+
// re-reads the (now exiting) PresenceContext instead of staying stale.
|
|
1841
|
+
function PresenceChild({ children, isExiting, mode, }) {
|
|
1842
|
+
const [exitingState, setExitingState] = useState(false);
|
|
1843
|
+
useLayoutEffect(() => {
|
|
1844
|
+
if (isExiting && !exitingState) {
|
|
1845
|
+
setExitingState(true);
|
|
1846
|
+
}
|
|
1847
|
+
}, [isExiting, exitingState]);
|
|
1848
|
+
if (mode === 'popLayout' && isExiting) {
|
|
1849
|
+
return jsx("div", { style: { position: 'absolute' }, children: children });
|
|
1850
|
+
}
|
|
1851
|
+
return children;
|
|
1852
|
+
}
|
|
1853
|
+
/**
|
|
1854
|
+
* Presence enables exit animations when children are removed from the tree.
|
|
1855
|
+
*
|
|
1856
|
+
* @example
|
|
1857
|
+
* ```tsx
|
|
1858
|
+
* <Presence>
|
|
1859
|
+
* {isVisible && (
|
|
1860
|
+
* <animate.div
|
|
1861
|
+
* key="modal"
|
|
1862
|
+
* style={{ opacity: 0 }}
|
|
1863
|
+
* animate={{ opacity: withTiming(1) }}
|
|
1864
|
+
* exit={{ opacity: withTiming(0) }}
|
|
1865
|
+
* />
|
|
1866
|
+
* )}
|
|
1867
|
+
* </Presence>
|
|
1868
|
+
* ```
|
|
1869
|
+
*/
|
|
1870
|
+
function Presence({ children, initial = true, onExitComplete, mode = 'sync', }) {
|
|
1871
|
+
const isInitialMount = useRef(true);
|
|
1872
|
+
const [childStates, setChildStates] = useState(() => new Map());
|
|
1873
|
+
const exitingCount = useRef(0);
|
|
1874
|
+
const currentChildren = useMemo(() => {
|
|
1875
|
+
const result = [];
|
|
1876
|
+
Children.forEach(children, (child) => {
|
|
1877
|
+
if (isValidElement(child)) {
|
|
1878
|
+
const key = child.key ?? 'default';
|
|
1879
|
+
result.push({ key, element: child });
|
|
1880
|
+
}
|
|
1881
|
+
});
|
|
1882
|
+
return result;
|
|
1883
|
+
}, [children]);
|
|
1884
|
+
const currentKeys = useMemo(() => new Set(currentChildren.map((c) => c.key)), [currentChildren]);
|
|
1885
|
+
useLayoutEffect(() => {
|
|
1886
|
+
setChildStates((prev) => {
|
|
1887
|
+
const next = new Map();
|
|
1888
|
+
const currentChildrenMap = new Map(currentChildren.map(({ key, element }) => [key, element]));
|
|
1889
|
+
// Two passes: first carry forward everything from `prev` (updating
|
|
1890
|
+
// elements that still exist, marking the rest as exiting), then append
|
|
1891
|
+
// any brand-new keys — so existing children keep their render order
|
|
1892
|
+
// and new ones land at the end.
|
|
1893
|
+
for (const [key, state] of prev) {
|
|
1894
|
+
if (currentChildrenMap.has(key)) {
|
|
1895
|
+
// Was exiting and came back before its exit finished re-entering.
|
|
1896
|
+
if (state.isExiting) {
|
|
1897
|
+
exitingCount.current--;
|
|
1898
|
+
}
|
|
1899
|
+
next.set(key, {
|
|
1900
|
+
key,
|
|
1901
|
+
element: currentChildrenMap.get(key),
|
|
1902
|
+
isExiting: false,
|
|
1903
|
+
});
|
|
1904
|
+
}
|
|
1905
|
+
else {
|
|
1906
|
+
if (!state.isExiting) {
|
|
1907
|
+
exitingCount.current++;
|
|
1908
|
+
}
|
|
1909
|
+
next.set(key, {
|
|
1910
|
+
...state,
|
|
1911
|
+
isExiting: true,
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
for (const { key, element } of currentChildren) {
|
|
1916
|
+
if (!prev.has(key)) {
|
|
1917
|
+
next.set(key, {
|
|
1918
|
+
key,
|
|
1919
|
+
element,
|
|
1920
|
+
isExiting: false,
|
|
1921
|
+
});
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
return next;
|
|
1925
|
+
});
|
|
1926
|
+
}, [currentChildren, currentKeys]);
|
|
1927
|
+
useEffect(() => {
|
|
1928
|
+
if (isInitialMount.current) {
|
|
1929
|
+
isInitialMount.current = false;
|
|
1930
|
+
}
|
|
1931
|
+
}, []);
|
|
1932
|
+
const handleExitComplete = useCallback((key) => {
|
|
1933
|
+
setChildStates((prev) => {
|
|
1934
|
+
const next = new Map(prev);
|
|
1935
|
+
next.delete(key);
|
|
1936
|
+
return next;
|
|
1937
|
+
});
|
|
1938
|
+
exitingCount.current--;
|
|
1939
|
+
if (exitingCount.current === 0 && onExitComplete) {
|
|
1940
|
+
onExitComplete();
|
|
1941
|
+
}
|
|
1942
|
+
}, [onExitComplete]);
|
|
1943
|
+
const renderedChildren = useMemo(() => {
|
|
1944
|
+
const result = [];
|
|
1945
|
+
// In 'wait' mode, entering children stay unrendered until nothing is exiting.
|
|
1946
|
+
const hasExiting = Array.from(childStates.values()).some((s) => s.isExiting);
|
|
1947
|
+
const shouldWait = mode === 'wait' && hasExiting;
|
|
1948
|
+
for (const [key, state] of childStates) {
|
|
1949
|
+
if (shouldWait && !state.isExiting) {
|
|
1950
|
+
continue;
|
|
1951
|
+
}
|
|
1952
|
+
const contextValue = {
|
|
1953
|
+
isInitialMount: isInitialMount.current && initial,
|
|
1954
|
+
isExiting: state.isExiting,
|
|
1955
|
+
onExitComplete: () => handleExitComplete(key),
|
|
1956
|
+
};
|
|
1957
|
+
result.push(jsx(PresenceContext.Provider, { value: contextValue, children: jsx(PresenceChild, { isExiting: state.isExiting, mode: mode, children: state.element }) }, key));
|
|
1958
|
+
}
|
|
1959
|
+
return result;
|
|
1960
|
+
}, [childStates, initial, mode, handleExitComplete]);
|
|
1961
|
+
return jsx(Fragment, { children: renderedChildren });
|
|
1962
|
+
}function getStaticStyleValue(style, key) {
|
|
1963
|
+
if (!style || !(key in style))
|
|
1964
|
+
return null;
|
|
1965
|
+
const value = style[key];
|
|
1966
|
+
if (value === undefined || value === null)
|
|
1967
|
+
return null;
|
|
1968
|
+
// Skip AnimateValues - we want static initial values
|
|
1969
|
+
if (value && typeof value === 'object' && 'subscribe' in value)
|
|
1970
|
+
return null;
|
|
1971
|
+
if (typeof value === 'number')
|
|
1972
|
+
return value;
|
|
1973
|
+
if (typeof value === 'string') {
|
|
1974
|
+
// Must check for complex CSS values (spaces, functions, colors, gradients)
|
|
1975
|
+
// before attempting to parse as a number, or e.g. "0 0 0 rgba(0,0,0,0)"
|
|
1976
|
+
// would get mangled.
|
|
1977
|
+
const complexValuePatterns = [
|
|
1978
|
+
/\s/,
|
|
1979
|
+
/rgba?\(/,
|
|
1980
|
+
/hsla?\(/,
|
|
1981
|
+
/#[0-9a-fA-F]/,
|
|
1982
|
+
/linear-gradient|radial-gradient|conic-gradient/,
|
|
1983
|
+
/calc\(|var\(/,
|
|
1984
|
+
];
|
|
1985
|
+
if (complexValuePatterns.some(pattern => pattern.test(value))) {
|
|
1986
|
+
return value;
|
|
1987
|
+
}
|
|
1988
|
+
if (/\d+(px|rem|em|ex|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)$/i.test(value))
|
|
1989
|
+
return value;
|
|
1990
|
+
const num = parseFloat(value);
|
|
1991
|
+
return isNaN(num) ? value : num;
|
|
1992
|
+
}
|
|
1993
|
+
return null;
|
|
1994
|
+
}
|
|
1995
|
+
function getDefaultInitialValue(key) {
|
|
1996
|
+
if (key === 'opacity')
|
|
1997
|
+
return 1;
|
|
1998
|
+
if (key === 'scale' || key === 'scaleX' || key === 'scaleY')
|
|
1999
|
+
return 1;
|
|
2000
|
+
if (key.startsWith('translate'))
|
|
2001
|
+
return 0;
|
|
2002
|
+
if (key.startsWith('rotate'))
|
|
2003
|
+
return 0;
|
|
2004
|
+
const stringProperties = [
|
|
2005
|
+
'boxShadow', 'textShadow', 'background', 'backgroundImage',
|
|
2006
|
+
'backgroundPosition', 'backgroundSize', 'border', 'borderColor',
|
|
2007
|
+
'borderImage', 'borderRadius', 'color', 'fill', 'stroke',
|
|
2008
|
+
'filter', 'backdropFilter', 'clipPath', 'mask', 'maskImage'
|
|
2009
|
+
];
|
|
2010
|
+
if (stringProperties.includes(key)) {
|
|
2011
|
+
return '';
|
|
2012
|
+
}
|
|
2013
|
+
return 0;
|
|
2014
|
+
}
|
|
2015
|
+
function hasUnit(value) {
|
|
2016
|
+
return /\d+(px|rem|em|ex|%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)$/i.test(value);
|
|
2017
|
+
}
|
|
2018
|
+
function isNumericProperty(key) {
|
|
2019
|
+
if (isTransformKey(key))
|
|
2020
|
+
return true;
|
|
2021
|
+
if (key === 'opacity')
|
|
2022
|
+
return true;
|
|
2023
|
+
if (key === 'zIndex')
|
|
2024
|
+
return true;
|
|
2025
|
+
return false;
|
|
2026
|
+
}
|
|
2027
|
+
// Resolves in priority order: explicit style prop -> inline style -> computed
|
|
2028
|
+
// style -> a sensible per-property default.
|
|
2029
|
+
function getInitialValue(key, style, node, computedStyle) {
|
|
2030
|
+
const shouldBeNumeric = isNumericProperty(key);
|
|
2031
|
+
const staticValue = getStaticStyleValue(style, key);
|
|
2032
|
+
if (staticValue !== null) {
|
|
2033
|
+
if (shouldBeNumeric && typeof staticValue === 'string') {
|
|
2034
|
+
const num = parseFloat(staticValue);
|
|
2035
|
+
return isNaN(num) ? getDefaultInitialValue(key) : num;
|
|
2036
|
+
}
|
|
2037
|
+
return staticValue;
|
|
2038
|
+
}
|
|
2039
|
+
const inlineValue = node.style[key];
|
|
2040
|
+
if (inlineValue) {
|
|
2041
|
+
if (shouldBeNumeric) {
|
|
2042
|
+
const num = parseFloat(inlineValue);
|
|
2043
|
+
return isNaN(num) ? getDefaultInitialValue(key) : num;
|
|
2044
|
+
}
|
|
2045
|
+
if (hasUnit(inlineValue))
|
|
2046
|
+
return inlineValue;
|
|
2047
|
+
const num = parseFloat(inlineValue);
|
|
2048
|
+
return isNaN(num) ? inlineValue : num;
|
|
2049
|
+
}
|
|
2050
|
+
const computedValue = computedStyle.getPropertyValue(key.replace(/([A-Z])/g, '-$1').toLowerCase());
|
|
2051
|
+
if (computedValue) {
|
|
2052
|
+
const trimmed = computedValue.trim();
|
|
2053
|
+
if (shouldBeNumeric) {
|
|
2054
|
+
const num = parseFloat(trimmed);
|
|
2055
|
+
return isNaN(num) ? getDefaultInitialValue(key) : num;
|
|
2056
|
+
}
|
|
2057
|
+
// 'none' -> '' so a property can animate from "no value" to a value.
|
|
2058
|
+
if (trimmed === 'none') {
|
|
2059
|
+
const stringProperties = [
|
|
2060
|
+
'boxShadow', 'textShadow', 'background', 'backgroundImage',
|
|
2061
|
+
'backgroundPosition', 'backgroundSize', 'border', 'borderColor',
|
|
2062
|
+
'borderImage', 'borderRadius', 'color', 'fill', 'stroke',
|
|
2063
|
+
'filter', 'backdropFilter', 'clipPath', 'mask', 'maskImage'
|
|
2064
|
+
];
|
|
2065
|
+
if (stringProperties.includes(key)) {
|
|
2066
|
+
return '';
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
if (hasUnit(trimmed))
|
|
2070
|
+
return trimmed;
|
|
2071
|
+
const num = parseFloat(trimmed);
|
|
2072
|
+
return isNaN(num) ? trimmed : num;
|
|
2073
|
+
}
|
|
2074
|
+
return getDefaultInitialValue(key);
|
|
2075
|
+
}// Unwraps a descriptor (including `withSequence`/`withDelay` chains) to find
|
|
2076
|
+
// the final numeric/string destination it's headed towards. Returns
|
|
2077
|
+
// `undefined` for open-ended animations (e.g. `withLoop`) that have no
|
|
2078
|
+
// settled target to speak of.
|
|
2079
|
+
function extractRestingTarget(descriptor) {
|
|
2080
|
+
if (descriptor === undefined)
|
|
2081
|
+
return undefined;
|
|
2082
|
+
if (typeof descriptor === 'number' || typeof descriptor === 'string') {
|
|
2083
|
+
return descriptor;
|
|
2084
|
+
}
|
|
2085
|
+
if (descriptor.type === 'sequence') {
|
|
2086
|
+
const animations = descriptor.options?.animations ?? [];
|
|
2087
|
+
for (let i = animations.length - 1; i >= 0; i--) {
|
|
2088
|
+
const target = extractRestingTarget(animations[i]);
|
|
2089
|
+
if (target !== undefined)
|
|
2090
|
+
return target;
|
|
2091
|
+
}
|
|
2092
|
+
return undefined;
|
|
2093
|
+
}
|
|
2094
|
+
if (descriptor.type === 'loop' || descriptor.type === 'decay') {
|
|
2095
|
+
return undefined;
|
|
2096
|
+
}
|
|
2097
|
+
if (typeof descriptor.to === 'number' || typeof descriptor.to === 'string') {
|
|
2098
|
+
return descriptor.to;
|
|
2099
|
+
}
|
|
2100
|
+
return undefined;
|
|
2101
|
+
}
|
|
2102
|
+
function applyStateAnimation(stateProp, isActive, context) {
|
|
2103
|
+
if (!stateProp)
|
|
2104
|
+
return;
|
|
2105
|
+
const { node, style, computedStyle, animateValues, initialValues, stateControllers, cleanup, restingTargets, } = context;
|
|
2106
|
+
stateControllers.forEach((ctrl) => ctrl.cancel());
|
|
2107
|
+
stateControllers.length = 0;
|
|
2108
|
+
const newSubscriptions = [];
|
|
2109
|
+
for (const [key, valueOrDescriptor] of Object.entries(stateProp)) {
|
|
2110
|
+
let value = animateValues[key];
|
|
2111
|
+
if (!value) {
|
|
2112
|
+
const initial = getInitialValue(key, style, node, computedStyle);
|
|
2113
|
+
value = new AnimateValue(initial);
|
|
2114
|
+
animateValues[key] = value;
|
|
2115
|
+
initialValues[key] = initial;
|
|
2116
|
+
if (isTransformKey(key)) {
|
|
2117
|
+
const render = createTransformRenderer(node, animateValues);
|
|
2118
|
+
newSubscriptions.push(value.subscribe(render));
|
|
2119
|
+
render();
|
|
2120
|
+
}
|
|
2121
|
+
else {
|
|
2122
|
+
const updateStyle = (v) => {
|
|
2123
|
+
const css = typeof v === 'number' &&
|
|
2124
|
+
!['opacity', 'zIndex', 'fontWeight', 'lineHeight'].includes(key)
|
|
2125
|
+
? `${v}px`
|
|
2126
|
+
: String(v);
|
|
2127
|
+
node.style[key] = css;
|
|
2128
|
+
};
|
|
2129
|
+
newSubscriptions.push(value.subscribe(updateStyle));
|
|
2130
|
+
updateStyle(initial);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
else {
|
|
2134
|
+
// If value already exists (e.g. pre-initialized by `animate`/`view`),
|
|
2135
|
+
// capture the revert target fresh on every activation rather than only
|
|
2136
|
+
// once. The value right before a hover/press/focus starts is always
|
|
2137
|
+
// the correct "resting" baseline to snap back to — a *view* (or
|
|
2138
|
+
// `animate`) transition can move that baseline well after mount (e.g.
|
|
2139
|
+
// translateY settling from 40 -> 0), and caching the target only once
|
|
2140
|
+
// would permanently lock in whatever the value happened to be during
|
|
2141
|
+
// the very first activation, even after the real baseline has moved on.
|
|
2142
|
+
if (isActive) {
|
|
2143
|
+
initialValues[key] = value.current;
|
|
2144
|
+
}
|
|
2145
|
+
else if (!(key in initialValues)) {
|
|
2146
|
+
// Deactivating without ever having activated (shouldn't normally
|
|
2147
|
+
// happen) — fall back to resolving from static style.
|
|
2148
|
+
initialValues[key] = getInitialValue(key, style, node, computedStyle);
|
|
2149
|
+
}
|
|
2150
|
+
// AnimateValues created in useLayoutEffect (e.g. by the `view` prop)
|
|
2151
|
+
// need their subscriptions (re)established here too, or they'd never
|
|
2152
|
+
// reach the DOM.
|
|
2153
|
+
if (isTransformKey(key)) {
|
|
2154
|
+
const render = createTransformRenderer(node, animateValues);
|
|
2155
|
+
newSubscriptions.push(value.subscribe(render));
|
|
2156
|
+
}
|
|
2157
|
+
else {
|
|
2158
|
+
newSubscriptions.push(value.subscribe((v) => {
|
|
2159
|
+
const css = typeof v === 'number' &&
|
|
2160
|
+
!['opacity', 'zIndex', 'fontWeight', 'lineHeight'].includes(key)
|
|
2161
|
+
? `${v}px`
|
|
2162
|
+
: String(v);
|
|
2163
|
+
node.style[key] = css;
|
|
2164
|
+
}));
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
const isPrimitive = typeof valueOrDescriptor === 'number' ||
|
|
2168
|
+
typeof valueOrDescriptor === 'string';
|
|
2169
|
+
if (isActive) {
|
|
2170
|
+
if (isPrimitive) {
|
|
2171
|
+
const springDescriptor = {
|
|
2172
|
+
type: 'spring',
|
|
2173
|
+
to: valueOrDescriptor,
|
|
2174
|
+
options: {},
|
|
2175
|
+
};
|
|
2176
|
+
const controller = buildAnimation(value, springDescriptor);
|
|
2177
|
+
stateControllers.push(controller);
|
|
2178
|
+
controller.start();
|
|
2179
|
+
}
|
|
2180
|
+
else {
|
|
2181
|
+
const controller = buildAnimation(value, valueOrDescriptor);
|
|
2182
|
+
stateControllers.push(controller);
|
|
2183
|
+
controller.start();
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
else {
|
|
2187
|
+
// Revert to the real settled target if `animate`/`view` declares one
|
|
2188
|
+
// for this key (see `restingTargets` doc above); otherwise fall back
|
|
2189
|
+
// to the captured value.
|
|
2190
|
+
const initialValue = restingTargets?.[key] ?? initialValues[key];
|
|
2191
|
+
if (isPrimitive) {
|
|
2192
|
+
const revertDescriptor = {
|
|
2193
|
+
type: 'spring',
|
|
2194
|
+
to: initialValue,
|
|
2195
|
+
options: {},
|
|
2196
|
+
};
|
|
2197
|
+
const controller = buildAnimation(value, revertDescriptor);
|
|
2198
|
+
stateControllers.push(controller);
|
|
2199
|
+
controller.start();
|
|
2200
|
+
}
|
|
2201
|
+
else {
|
|
2202
|
+
const revertDescriptor = {
|
|
2203
|
+
type: 'spring',
|
|
2204
|
+
to: initialValue,
|
|
2205
|
+
options: {},
|
|
2206
|
+
};
|
|
2207
|
+
const controller = buildAnimation(value, revertDescriptor);
|
|
2208
|
+
stateControllers.push(controller);
|
|
2209
|
+
controller.start();
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
cleanup.push(...newSubscriptions);
|
|
2214
|
+
}function setupExitAnimations(context) {
|
|
2215
|
+
const { exitProp, animateValues, controllers, onExitComplete, node, style } = context;
|
|
2216
|
+
let completedCount = 0;
|
|
2217
|
+
const totalAnimations = Object.keys(exitProp).length;
|
|
2218
|
+
const newSubscriptions = [];
|
|
2219
|
+
const checkComplete = () => {
|
|
2220
|
+
completedCount++;
|
|
2221
|
+
if (completedCount >= totalAnimations) {
|
|
2222
|
+
onExitComplete();
|
|
2223
|
+
}
|
|
2224
|
+
};
|
|
2225
|
+
const computedStyle = window.getComputedStyle(node);
|
|
2226
|
+
for (const key of Object.keys(exitProp)) {
|
|
2227
|
+
if (!animateValues[key]) {
|
|
2228
|
+
const initial = getInitialValue(key, style, node, computedStyle);
|
|
2229
|
+
animateValues[key] = new AnimateValue(initial);
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
// Re-apply the full merged style (including the exit-only AnimateValues
|
|
2233
|
+
// just created above) so every value is actually subscribed to the DOM.
|
|
2234
|
+
const mergedStyle = { ...style };
|
|
2235
|
+
for (const key of Object.keys(animateValues)) {
|
|
2236
|
+
mergedStyle[key] = animateValues[key];
|
|
2237
|
+
}
|
|
2238
|
+
const normal = {};
|
|
2239
|
+
const transforms = {};
|
|
2240
|
+
for (const [key, value] of Object.entries(mergedStyle)) {
|
|
2241
|
+
if (value && typeof value === 'object' && 'subscribe' in value) {
|
|
2242
|
+
(isTransformKey(key) ? transforms : normal)[key] = value;
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
newSubscriptions.push(...applyStyles(node, normal));
|
|
2246
|
+
newSubscriptions.push(...applyTransforms(node, transforms));
|
|
2247
|
+
for (const [key, valueOrDescriptor] of Object.entries(exitProp)) {
|
|
2248
|
+
const value = animateValues[key];
|
|
2249
|
+
if (!value) {
|
|
2250
|
+
checkComplete();
|
|
2251
|
+
continue;
|
|
2252
|
+
}
|
|
2253
|
+
const baseDescriptor = typeof valueOrDescriptor === 'number' ||
|
|
2254
|
+
typeof valueOrDescriptor === 'string'
|
|
2255
|
+
? {
|
|
2256
|
+
type: 'spring',
|
|
2257
|
+
to: valueOrDescriptor,
|
|
2258
|
+
options: {},
|
|
2259
|
+
}
|
|
2260
|
+
: valueOrDescriptor;
|
|
2261
|
+
const exitDescriptor = {
|
|
2262
|
+
...baseDescriptor,
|
|
2263
|
+
options: {
|
|
2264
|
+
...baseDescriptor.options,
|
|
2265
|
+
onComplete: () => {
|
|
2266
|
+
baseDescriptor.options?.onComplete?.();
|
|
2267
|
+
checkComplete();
|
|
2268
|
+
},
|
|
2269
|
+
},
|
|
2270
|
+
};
|
|
2271
|
+
const controller = buildAnimation(value, exitDescriptor);
|
|
2272
|
+
controllers.push(controller);
|
|
2273
|
+
controller.start();
|
|
2274
|
+
}
|
|
2275
|
+
return newSubscriptions;
|
|
2276
|
+
}const LAYOUT_FLIP_KEYS = {
|
|
2277
|
+
tx: '__layoutTranslateX',
|
|
2278
|
+
ty: '__layoutTranslateY',
|
|
2279
|
+
sx: '__layoutScaleX',
|
|
2280
|
+
sy: '__layoutScaleY',
|
|
2281
|
+
};
|
|
2282
|
+
const LAYOUT_ID_FLIP_KEYS = {
|
|
2283
|
+
tx: '__layoutIdTranslateX',
|
|
2284
|
+
ty: '__layoutIdTranslateY',
|
|
2285
|
+
sx: '__layoutIdScaleX',
|
|
2286
|
+
sy: '__layoutIdScaleY',
|
|
2287
|
+
};
|
|
2288
|
+
const DEFAULT_LAYOUT_SPRING = {
|
|
2289
|
+
stiffness: 500,
|
|
2290
|
+
damping: 40,
|
|
2291
|
+
mass: 1,
|
|
2292
|
+
};
|
|
2293
|
+
// Resolves `layoutOptions` into a spring/timing descriptor. Only those two
|
|
2294
|
+
// drivers make sense for a FLIP settle-to-identity; anything else falls back
|
|
2295
|
+
// to the default layout spring.
|
|
2296
|
+
function resolveLayoutTransition(layoutOptions) {
|
|
2297
|
+
if (!layoutOptions) {
|
|
2298
|
+
return { type: 'spring', to: 0, options: { ...DEFAULT_LAYOUT_SPRING } };
|
|
2299
|
+
}
|
|
2300
|
+
if (isDescriptor(layoutOptions)) {
|
|
2301
|
+
if (layoutOptions.type === 'spring' || layoutOptions.type === 'timing') {
|
|
2302
|
+
return {
|
|
2303
|
+
type: layoutOptions.type,
|
|
2304
|
+
to: 0,
|
|
2305
|
+
options: { ...layoutOptions.options },
|
|
2306
|
+
};
|
|
2307
|
+
}
|
|
2308
|
+
return { type: 'spring', to: 0, options: { ...DEFAULT_LAYOUT_SPRING } };
|
|
2309
|
+
}
|
|
2310
|
+
return {
|
|
2311
|
+
type: 'spring',
|
|
2312
|
+
to: 0,
|
|
2313
|
+
options: { ...DEFAULT_LAYOUT_SPRING, ...layoutOptions },
|
|
2314
|
+
};
|
|
2315
|
+
}
|
|
2316
|
+
// Shared FLIP (First, Last, Invert, Play) animation used by both the
|
|
2317
|
+
// `layout` prop (diffing an element's own rect across renders) and the
|
|
2318
|
+
// `layoutId` prop (diffing against a rect recorded by a different element).
|
|
2319
|
+
// Jumps to the inverted delta instantly, then animates back to identity
|
|
2320
|
+
// via the same `withSpring` / `withTiming` drivers used elsewhere.
|
|
2321
|
+
function runFlipAnimation(node, delta, keys, refs, layoutOptions) {
|
|
2322
|
+
const { animateValuesRef, propsRef, controllersRef, unsubsRef, initializedRef, } = refs;
|
|
2323
|
+
const animateValues = animateValuesRef.current;
|
|
2324
|
+
if (!initializedRef.current) {
|
|
2325
|
+
animateValues[keys.tx] = new AnimateValue(0);
|
|
2326
|
+
animateValues[keys.ty] = new AnimateValue(0);
|
|
2327
|
+
animateValues[keys.sx] = new AnimateValue(1);
|
|
2328
|
+
animateValues[keys.sy] = new AnimateValue(1);
|
|
2329
|
+
initializedRef.current = true;
|
|
2330
|
+
}
|
|
2331
|
+
controllersRef.current.forEach((ctrl) => ctrl.cancel());
|
|
2332
|
+
controllersRef.current = [];
|
|
2333
|
+
unsubsRef.current.forEach((unsub) => unsub());
|
|
2334
|
+
unsubsRef.current = [];
|
|
2335
|
+
const tx = animateValues[keys.tx];
|
|
2336
|
+
const ty = animateValues[keys.ty];
|
|
2337
|
+
const sx = animateValues[keys.sx];
|
|
2338
|
+
const sy = animateValues[keys.sy];
|
|
2339
|
+
tx.set(delta.x);
|
|
2340
|
+
ty.set(delta.y);
|
|
2341
|
+
sx.set(delta.scaleX);
|
|
2342
|
+
sy.set(delta.scaleY);
|
|
2343
|
+
node.style.transformOrigin = 'top left';
|
|
2344
|
+
// Compose with whatever else is contributing to this element's transform
|
|
2345
|
+
// (static `style` values, and any `animate`/`hover`/`press`/`view`-driven
|
|
2346
|
+
// AnimateValues already sitting in animateValuesRef) rather than replacing it.
|
|
2347
|
+
const render = () => {
|
|
2348
|
+
const { style } = propsRef.current;
|
|
2349
|
+
node.style.transform = formatTransformString({
|
|
2350
|
+
...style,
|
|
2351
|
+
...animateValuesRef.current,
|
|
2352
|
+
});
|
|
2353
|
+
};
|
|
2354
|
+
render();
|
|
2355
|
+
unsubsRef.current = [
|
|
2356
|
+
tx.subscribe(render),
|
|
2357
|
+
ty.subscribe(render),
|
|
2358
|
+
sx.subscribe(render),
|
|
2359
|
+
sy.subscribe(render),
|
|
2360
|
+
];
|
|
2361
|
+
const transition = resolveLayoutTransition(layoutOptions);
|
|
2362
|
+
// Translate channels settle at 0; scale channels settle at 1. Descriptor
|
|
2363
|
+
// `to` (when present) is ignored so options-only forms like
|
|
2364
|
+
// `withTiming({ duration: 300 })` work as layout transitions.
|
|
2365
|
+
const controllers = [
|
|
2366
|
+
buildAnimation(tx, { ...transition, to: 0 }),
|
|
2367
|
+
buildAnimation(ty, { ...transition, to: 0 }),
|
|
2368
|
+
buildAnimation(sx, { ...transition, to: 1 }),
|
|
2369
|
+
buildAnimation(sy, { ...transition, to: 1 }),
|
|
2370
|
+
];
|
|
2371
|
+
controllersRef.current = controllers;
|
|
2372
|
+
controllers.forEach((ctrl) => ctrl.start());
|
|
2373
|
+
}
|
|
2374
|
+
// Measures `node`'s untransformed layout box. getBoundingClientRect()
|
|
2375
|
+
// reflects whatever CSS transform is currently applied (ours or the
|
|
2376
|
+
// consumer's own), which would corrupt the measurement — e.g. while a
|
|
2377
|
+
// previous layout animation is still in flight (rapid re-triggers), or if a
|
|
2378
|
+
// static/animated transform is set via `style`/`animate`. Briefly
|
|
2379
|
+
// neutralizing the transform happens entirely within useLayoutEffect, before
|
|
2380
|
+
// the browser paints, so it's never visible.
|
|
2381
|
+
function measureUntransformedRect(node) {
|
|
2382
|
+
const previousTransform = node.style.transform;
|
|
2383
|
+
node.style.transform = 'none';
|
|
2384
|
+
const rect = node.getBoundingClientRect();
|
|
2385
|
+
node.style.transform = previousTransform;
|
|
2386
|
+
return rect;
|
|
2387
|
+
}
|
|
2388
|
+
// True if this element's FLIP pseudo-transform is currently displaced away
|
|
2389
|
+
// from identity (0 translate / 1 scale) with no controller actively driving
|
|
2390
|
+
// it back — i.e. a previously started FLIP was interrupted (its controllers
|
|
2391
|
+
// canceled) without a replacement being started. Used to recover from React
|
|
2392
|
+
// StrictMode double-invoking a `layoutId` element's mount effects: the
|
|
2393
|
+
// simulated "unmount" cancels the just-started spring, and without this
|
|
2394
|
+
// check the simulated "remount" would see no rect delta (it's comparing
|
|
2395
|
+
// against the rect it just registered for itself) and never resume it,
|
|
2396
|
+
// leaving the transform stuck at its initial inverted value.
|
|
2397
|
+
function readStrandedDisplacement(animateValues, keys, isAnimating) {
|
|
2398
|
+
if (isAnimating)
|
|
2399
|
+
return null;
|
|
2400
|
+
const tx = animateValues[keys.tx];
|
|
2401
|
+
if (!tx)
|
|
2402
|
+
return null;
|
|
2403
|
+
const x = Number(tx.current) || 0;
|
|
2404
|
+
const y = Number(animateValues[keys.ty]?.current) || 0;
|
|
2405
|
+
const scaleX = Number(animateValues[keys.sx]?.current ?? 1);
|
|
2406
|
+
const scaleY = Number(animateValues[keys.sy]?.current ?? 1);
|
|
2407
|
+
const isDisplaced = Math.abs(x) > 0.5 ||
|
|
2408
|
+
Math.abs(y) > 0.5 ||
|
|
2409
|
+
Math.abs(scaleX - 1) > 0.01 ||
|
|
2410
|
+
Math.abs(scaleY - 1) > 0.01;
|
|
2411
|
+
return isDisplaced ? { x, y, scaleX, scaleY } : null;
|
|
2412
|
+
}
|
|
2413
|
+
function diffRects(prevRect, nextRect) {
|
|
2414
|
+
const deltaX = prevRect.left - nextRect.left;
|
|
2415
|
+
const deltaY = prevRect.top - nextRect.top;
|
|
2416
|
+
const scaleX = prevRect.width / nextRect.width;
|
|
2417
|
+
const scaleY = prevRect.height / nextRect.height;
|
|
2418
|
+
const hasMoved = Math.abs(deltaX) > 0.5 || Math.abs(deltaY) > 0.5;
|
|
2419
|
+
const hasResized = Math.abs(scaleX - 1) > 0.01 || Math.abs(scaleY - 1) > 0.01;
|
|
2420
|
+
if (!hasMoved && !hasResized)
|
|
2421
|
+
return null;
|
|
2422
|
+
return { x: deltaX, y: deltaY, scaleX, scaleY };
|
|
2423
|
+
}/**
|
|
2424
|
+
* Animates an element's own layout changes across renders (`layout` prop).
|
|
2425
|
+
* Measures the untransformed rect each commit; when it differs from the
|
|
2426
|
+
* previous measurement, runs a FLIP spring back to identity.
|
|
2427
|
+
*/
|
|
2428
|
+
function useLayoutAnimations(nodeRef, propsRef, isExitingRef, animateValuesRef) {
|
|
2429
|
+
const prevRectRef = useRef(null);
|
|
2430
|
+
const hasMeasuredRef = useRef(false);
|
|
2431
|
+
const initializedRef = useRef(false);
|
|
2432
|
+
const controllersRef = useRef([]);
|
|
2433
|
+
const unsubsRef = useRef([]);
|
|
2434
|
+
useLayoutEffect(() => {
|
|
2435
|
+
const node = nodeRef.current;
|
|
2436
|
+
const { layout, layoutOptions } = propsRef.current;
|
|
2437
|
+
if (!node || !layout || isExitingRef.current) {
|
|
2438
|
+
hasMeasuredRef.current = false;
|
|
2439
|
+
prevRectRef.current = null;
|
|
2440
|
+
return;
|
|
2441
|
+
}
|
|
2442
|
+
const nextRect = measureUntransformedRect(node);
|
|
2443
|
+
const prevRect = prevRectRef.current;
|
|
2444
|
+
const shouldCompare = hasMeasuredRef.current;
|
|
2445
|
+
hasMeasuredRef.current = true;
|
|
2446
|
+
prevRectRef.current = nextRect;
|
|
2447
|
+
// Skip the first measurement (mount) and degenerate (hidden) rects
|
|
2448
|
+
if (!shouldCompare || !prevRect)
|
|
2449
|
+
return;
|
|
2450
|
+
if (nextRect.width === 0 || nextRect.height === 0)
|
|
2451
|
+
return;
|
|
2452
|
+
const delta = diffRects(prevRect, nextRect);
|
|
2453
|
+
if (!delta)
|
|
2454
|
+
return;
|
|
2455
|
+
runFlipAnimation(node, delta, LAYOUT_FLIP_KEYS, {
|
|
2456
|
+
animateValuesRef,
|
|
2457
|
+
propsRef,
|
|
2458
|
+
controllersRef,
|
|
2459
|
+
unsubsRef,
|
|
2460
|
+
initializedRef,
|
|
2461
|
+
}, layoutOptions);
|
|
2462
|
+
});
|
|
2463
|
+
useEffect(() => {
|
|
2464
|
+
return () => {
|
|
2465
|
+
controllersRef.current.forEach((ctrl) => ctrl.cancel());
|
|
2466
|
+
unsubsRef.current.forEach((unsub) => unsub());
|
|
2467
|
+
};
|
|
2468
|
+
}, []);
|
|
2469
|
+
}// Global registry mapping a `layoutId` to the last known rect of whichever
|
|
2470
|
+
// element most recently claimed it. A "claim" happens on every layout effect
|
|
2471
|
+
// flush of any element carrying that `layoutId`, so the registry naturally
|
|
2472
|
+
// captures the last position/size before an element unmounts (the
|
|
2473
|
+
// registration from its final render stays put) as well as before each
|
|
2474
|
+
// re-render of a persisting element (degrading gracefully to the same
|
|
2475
|
+
// per-node behavior as the `layout` prop). Note this is a single global map,
|
|
2476
|
+
// so `layoutId`s should be unique per active transition group.
|
|
2477
|
+
//
|
|
2478
|
+
// Stashed on `globalThis` (rather than a plain module-level variable) so
|
|
2479
|
+
// every copy of this module shares the SAME registry even if the host
|
|
2480
|
+
// bundler ends up evaluating this file more than once (e.g. duplicate
|
|
2481
|
+
// chunks from a symlinked/monorepo package, or a module re-executed by hot
|
|
2482
|
+
// reloading) — otherwise the "old" element's registration and the "new"
|
|
2483
|
+
// element's lookup could land in two different Maps, silently turning every
|
|
2484
|
+
// transition into a no-op (the new element just renders at its target rect
|
|
2485
|
+
// with no prior entry to diff against, so it appears to jump instead of
|
|
2486
|
+
// animate).
|
|
2487
|
+
const LAYOUT_ID_REGISTRY_KEY = '__REACT_UI_ANIMATE_LAYOUT_ID_REGISTRY__';
|
|
2488
|
+
const layoutIdRegistry = (() => {
|
|
2489
|
+
const g = globalThis;
|
|
2490
|
+
if (!g[LAYOUT_ID_REGISTRY_KEY]) {
|
|
2491
|
+
g[LAYOUT_ID_REGISTRY_KEY] = new Map();
|
|
2492
|
+
}
|
|
2493
|
+
return g[LAYOUT_ID_REGISTRY_KEY];
|
|
2494
|
+
})();/**
|
|
2495
|
+
* Shared-element transitions via `layoutId`. On each layout flush the
|
|
2496
|
+
* element claims its id in the global registry; a newly mounted claimant
|
|
2497
|
+
* FLIPs from the previous owner's last rect. Also recovers StrictMode
|
|
2498
|
+
* stranded displacements (mount → cleanup cancels spring → remount).
|
|
2499
|
+
*/
|
|
2500
|
+
function useLayoutIdAnimations(nodeRef, propsRef, isExitingRef, animateValuesRef) {
|
|
2501
|
+
const initializedRef = useRef(false);
|
|
2502
|
+
const controllersRef = useRef([]);
|
|
2503
|
+
const unsubsRef = useRef([]);
|
|
2504
|
+
// Captures which (layoutId, node) pair this instance last claimed, read
|
|
2505
|
+
// back on unmount — by then nodeRef.current/propsRef.current may already
|
|
2506
|
+
// be cleared/stale, so the claim must be captured while still live.
|
|
2507
|
+
const claimRef = useRef(null);
|
|
2508
|
+
useLayoutEffect(() => {
|
|
2509
|
+
const node = nodeRef.current;
|
|
2510
|
+
const { layoutId, layoutOptions } = propsRef.current;
|
|
2511
|
+
if (!node || !layoutId || isExitingRef.current)
|
|
2512
|
+
return;
|
|
2513
|
+
const nextRect = measureUntransformedRect(node);
|
|
2514
|
+
const prevEntry = layoutIdRegistry.get(layoutId);
|
|
2515
|
+
layoutIdRegistry.set(layoutId, { rect: nextRect, node });
|
|
2516
|
+
claimRef.current = { layoutId, node };
|
|
2517
|
+
// If a FLIP was already in flight but its controllers were canceled
|
|
2518
|
+
// without anything resuming them (see readStrandedDisplacement — this
|
|
2519
|
+
// happens under React StrictMode's mount/cleanup/remount simulation),
|
|
2520
|
+
// resume it toward identity even though this element's own rect hasn't
|
|
2521
|
+
// changed since it last registered.
|
|
2522
|
+
const stranded = readStrandedDisplacement(animateValuesRef.current, LAYOUT_ID_FLIP_KEYS, controllersRef.current.length > 0);
|
|
2523
|
+
if (!prevEntry && !stranded)
|
|
2524
|
+
return;
|
|
2525
|
+
if (nextRect.width === 0 || nextRect.height === 0)
|
|
2526
|
+
return;
|
|
2527
|
+
const delta = prevEntry ? diffRects(prevEntry.rect, nextRect) : null;
|
|
2528
|
+
const flipDelta = delta ?? stranded;
|
|
2529
|
+
if (!flipDelta)
|
|
2530
|
+
return;
|
|
2531
|
+
runFlipAnimation(node, flipDelta, LAYOUT_ID_FLIP_KEYS, {
|
|
2532
|
+
animateValuesRef,
|
|
2533
|
+
propsRef,
|
|
2534
|
+
controllersRef,
|
|
2535
|
+
unsubsRef,
|
|
2536
|
+
initializedRef,
|
|
2537
|
+
}, layoutOptions);
|
|
2538
|
+
});
|
|
2539
|
+
useEffect(() => {
|
|
2540
|
+
return () => {
|
|
2541
|
+
controllersRef.current.forEach((ctrl) => ctrl.cancel());
|
|
2542
|
+
controllersRef.current = [];
|
|
2543
|
+
unsubsRef.current.forEach((unsub) => unsub());
|
|
2544
|
+
unsubsRef.current = [];
|
|
2545
|
+
const claim = claimRef.current;
|
|
2546
|
+
if (!claim)
|
|
2547
|
+
return;
|
|
2548
|
+
// A sibling element mounting with the same layoutId in this same
|
|
2549
|
+
// commit (the normal cross-component transition case) re-registers
|
|
2550
|
+
// synchronously before this runs, so it's safe to check back a tick
|
|
2551
|
+
// later: if nothing has re-claimed the id by then, this was the last
|
|
2552
|
+
// owner and the entry is genuinely abandoned — drop it so a much
|
|
2553
|
+
// later, unrelated remount (a different page/story, or Fast Refresh)
|
|
2554
|
+
// doesn't inherit a stale rect and produce a bogus transition.
|
|
2555
|
+
queueMicrotask(() => {
|
|
2556
|
+
const entry = layoutIdRegistry.get(claim.layoutId);
|
|
2557
|
+
if (entry && entry.node === claim.node) {
|
|
2558
|
+
layoutIdRegistry.delete(claim.layoutId);
|
|
2559
|
+
}
|
|
2560
|
+
});
|
|
2561
|
+
};
|
|
2562
|
+
}, []);
|
|
2563
|
+
}function combineRefs(...refs) {
|
|
2564
|
+
return (element) => {
|
|
2565
|
+
for (const ref of refs) {
|
|
2566
|
+
if (!ref)
|
|
2567
|
+
continue;
|
|
2568
|
+
if (typeof ref === 'function')
|
|
2569
|
+
ref(element);
|
|
2570
|
+
else if ('current' in ref)
|
|
2571
|
+
ref.current = element;
|
|
2572
|
+
}
|
|
2573
|
+
};
|
|
2574
|
+
}function useInView(ref, options = {}) {
|
|
2575
|
+
const { root, rootMargin, threshold, once = false } = options;
|
|
2576
|
+
const [isInView, setIsInView] = useState(false);
|
|
2577
|
+
useEffect(() => {
|
|
2578
|
+
const el = ref.current;
|
|
2579
|
+
if (!el)
|
|
2580
|
+
return;
|
|
2581
|
+
const observer = new IntersectionObserver(([entry]) => {
|
|
2582
|
+
if (entry.isIntersecting) {
|
|
2583
|
+
setIsInView(true);
|
|
2584
|
+
if (once) {
|
|
2585
|
+
observer.unobserve(entry.target);
|
|
2586
|
+
observer.disconnect();
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
else if (!once) {
|
|
2590
|
+
setIsInView(false);
|
|
2591
|
+
}
|
|
2592
|
+
}, { root: root ?? null, rootMargin, threshold });
|
|
2593
|
+
observer.observe(el);
|
|
2594
|
+
return () => observer.disconnect();
|
|
2595
|
+
}, [ref, root, rootMargin, threshold, once]);
|
|
2596
|
+
return isInView;
|
|
2597
|
+
}function serializeAnimateProp(prop) {
|
|
2598
|
+
if (!prop)
|
|
2599
|
+
return '';
|
|
2600
|
+
const propRecord = prop;
|
|
2601
|
+
const keys = Object.keys(propRecord).sort();
|
|
2602
|
+
return keys
|
|
2603
|
+
.map((key) => {
|
|
2604
|
+
const value = propRecord[key];
|
|
2605
|
+
if (typeof value === 'number' || typeof value === 'string') {
|
|
2606
|
+
return `${key}:${value}`;
|
|
2607
|
+
}
|
|
2608
|
+
if (typeof value === 'object' && value !== null && 'type' in value) {
|
|
2609
|
+
const desc = value;
|
|
2610
|
+
return `${key}:${desc.type}:${JSON.stringify(desc.to || '')}`;
|
|
2611
|
+
}
|
|
2612
|
+
return `${key}:unknown`;
|
|
2613
|
+
})
|
|
2614
|
+
.join('|');
|
|
2615
|
+
}
|
|
2616
|
+
function startAnimations(animateProp, animateValues, controllers) {
|
|
2617
|
+
for (const [key, valueOrDescriptor] of Object.entries(animateProp)) {
|
|
2618
|
+
const value = animateValues[key];
|
|
2619
|
+
if (!value)
|
|
2620
|
+
continue;
|
|
2621
|
+
const descriptor = typeof valueOrDescriptor === 'number' ||
|
|
2622
|
+
typeof valueOrDescriptor === 'string'
|
|
2623
|
+
? {
|
|
2624
|
+
type: 'spring',
|
|
2625
|
+
to: valueOrDescriptor,
|
|
2626
|
+
options: {},
|
|
2627
|
+
}
|
|
2628
|
+
: valueOrDescriptor;
|
|
2629
|
+
const controller = buildAnimation(value, descriptor);
|
|
2630
|
+
controllers.push(controller);
|
|
2631
|
+
controller.start();
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
function applyStylesToNode(node, style, animateValues, rest) {
|
|
2635
|
+
const mergedStyle = { ...style };
|
|
2636
|
+
for (const key of Object.keys(animateValues)) {
|
|
2637
|
+
mergedStyle[key] = animateValues[key];
|
|
2638
|
+
}
|
|
2639
|
+
const normal = {};
|
|
2640
|
+
const transforms = {};
|
|
2641
|
+
for (const [key, value] of Object.entries(mergedStyle)) {
|
|
2642
|
+
(isTransformKey(key) ? transforms : normal)[key] = value;
|
|
2643
|
+
}
|
|
2644
|
+
return [
|
|
2645
|
+
...applyStyles(node, normal),
|
|
2646
|
+
...applyTransforms(node, mergedStyle),
|
|
2647
|
+
...applyAttrs(node, rest),
|
|
2648
|
+
];
|
|
2649
|
+
}
|
|
2650
|
+
function isFocusable(node) {
|
|
2651
|
+
return (node instanceof HTMLInputElement ||
|
|
2652
|
+
node instanceof HTMLTextAreaElement ||
|
|
2653
|
+
node instanceof HTMLSelectElement ||
|
|
2654
|
+
node instanceof HTMLButtonElement ||
|
|
2655
|
+
node instanceof HTMLAnchorElement ||
|
|
2656
|
+
node.getAttribute('tabindex') !== null);
|
|
2657
|
+
}
|
|
2658
|
+
function useEnterAnimations(nodeRef, propsRef, animateProp, isExitingRef, animateValuesRef, controllersRef) {
|
|
2659
|
+
const presenceContext = useContext(PresenceContext);
|
|
2660
|
+
const cleanupRef = useRef([]);
|
|
2661
|
+
const hasMountedRef = useRef(false);
|
|
2662
|
+
const prevAnimatePropKeyRef = useRef('');
|
|
2663
|
+
const wasExitingRef = useRef(false);
|
|
2664
|
+
useLayoutEffect(() => {
|
|
2665
|
+
const node = nodeRef.current;
|
|
2666
|
+
const isExiting = presenceContext?.isExiting ?? false;
|
|
2667
|
+
const justReEntered = wasExitingRef.current && !isExiting;
|
|
2668
|
+
wasExitingRef.current = isExiting;
|
|
2669
|
+
if (!node || isExiting || isExitingRef.current)
|
|
2670
|
+
return;
|
|
2671
|
+
const { style: currentStyle = {}, ...rest } = propsRef.current;
|
|
2672
|
+
const currentKey = serializeAnimateProp(animateProp);
|
|
2673
|
+
const valuesChanged = prevAnimatePropKeyRef.current !== currentKey;
|
|
2674
|
+
// isFirstMount only refers to the literal first mount of this component instance
|
|
2675
|
+
const isFirstMount = !hasMountedRef.current;
|
|
2676
|
+
const shouldRestart = isFirstMount || valuesChanged || justReEntered;
|
|
2677
|
+
if (shouldRestart) {
|
|
2678
|
+
controllersRef.current.forEach((ctrl) => ctrl.cancel());
|
|
2679
|
+
controllersRef.current = [];
|
|
2680
|
+
if (animateProp) {
|
|
2681
|
+
const computedStyle = window.getComputedStyle(node);
|
|
2682
|
+
if (Object.keys(animateValuesRef.current).length === 0) {
|
|
2683
|
+
const newAnimateValues = {};
|
|
2684
|
+
for (const key of Object.keys(animateProp)) {
|
|
2685
|
+
const initial = getInitialValue(key, currentStyle, node, computedStyle);
|
|
2686
|
+
newAnimateValues[key] = new AnimateValue(initial);
|
|
2687
|
+
}
|
|
2688
|
+
animateValuesRef.current = newAnimateValues;
|
|
2689
|
+
}
|
|
2690
|
+
startAnimations(animateProp, animateValuesRef.current, controllersRef.current);
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
// Always re-sync subscriptions to handle StrictMode or prop updates
|
|
2694
|
+
// without restarting the animation timeline
|
|
2695
|
+
cleanupRef.current.forEach((cleanup) => cleanup());
|
|
2696
|
+
cleanupRef.current = applyStylesToNode(node, currentStyle, animateValuesRef.current, rest);
|
|
2697
|
+
prevAnimatePropKeyRef.current = currentKey;
|
|
2698
|
+
hasMountedRef.current = true;
|
|
2699
|
+
return () => {
|
|
2700
|
+
cleanupRef.current.forEach((cleanup) => cleanup());
|
|
2701
|
+
cleanupRef.current = [];
|
|
2702
|
+
};
|
|
2703
|
+
// Removed 'style' from dependencies to prevent re-triggering on parent state changes
|
|
2704
|
+
}, [animateProp, presenceContext?.isExiting]);
|
|
2705
|
+
}
|
|
2706
|
+
function useExitAnimations(nodeRef, propsRef, isExitingRef, animateValuesRef, enterControllersRef) {
|
|
2707
|
+
const presenceContext = useContext(PresenceContext);
|
|
2708
|
+
const exitControllersRef = useRef([]);
|
|
2709
|
+
const exitCleanupRef = useRef([]);
|
|
2710
|
+
const onExitCompleteRef = useRef(null);
|
|
2711
|
+
const prevIsExitingRef = useRef(false);
|
|
2712
|
+
useLayoutEffect(() => {
|
|
2713
|
+
onExitCompleteRef.current = presenceContext?.onExitComplete ?? null;
|
|
2714
|
+
});
|
|
2715
|
+
// useLayoutEffect (not useEffect) so this runs in sync with useEnterAnimations.
|
|
2716
|
+
useLayoutEffect(() => {
|
|
2717
|
+
const { exit: exitProp, style: currentStyle = {} } = propsRef.current;
|
|
2718
|
+
const node = nodeRef.current;
|
|
2719
|
+
const isExiting = presenceContext?.isExiting ?? false;
|
|
2720
|
+
const prevIsExiting = prevIsExitingRef.current;
|
|
2721
|
+
prevIsExitingRef.current = isExiting;
|
|
2722
|
+
if (!isExiting) {
|
|
2723
|
+
if (isExitingRef.current) {
|
|
2724
|
+
isExitingRef.current = false;
|
|
2725
|
+
exitControllersRef.current.forEach((ctrl) => ctrl.cancel());
|
|
2726
|
+
exitControllersRef.current = [];
|
|
2727
|
+
exitCleanupRef.current.forEach((cleanup) => cleanup());
|
|
2728
|
+
exitCleanupRef.current = [];
|
|
2729
|
+
}
|
|
2730
|
+
return;
|
|
2731
|
+
}
|
|
2732
|
+
if (isExitingRef.current || !exitProp || !node) {
|
|
2733
|
+
return;
|
|
2734
|
+
}
|
|
2735
|
+
// Gate on the not-exiting -> exiting transition (not just `isExiting`)
|
|
2736
|
+
// so a PresenceContext object identity change alone doesn't re-trigger
|
|
2737
|
+
// (and cancel) an exit animation already in flight.
|
|
2738
|
+
if (!prevIsExiting && isExiting) {
|
|
2739
|
+
isExitingRef.current = true;
|
|
2740
|
+
enterControllersRef.current.forEach((ctrl) => ctrl.cancel());
|
|
2741
|
+
enterControllersRef.current = [];
|
|
2742
|
+
exitCleanupRef.current.forEach((cleanup) => cleanup());
|
|
2743
|
+
exitCleanupRef.current = [];
|
|
2744
|
+
exitCleanupRef.current = setupExitAnimations({
|
|
2745
|
+
exitProp,
|
|
2746
|
+
animateValues: animateValuesRef.current,
|
|
2747
|
+
controllers: exitControllersRef.current,
|
|
2748
|
+
onExitComplete: () => {
|
|
2749
|
+
// Guard against a child that got re-added mid-exit and is no
|
|
2750
|
+
// longer exiting by the time this fires.
|
|
2751
|
+
if (isExitingRef.current && onExitCompleteRef.current) {
|
|
2752
|
+
exitCleanupRef.current.forEach((cleanup) => cleanup());
|
|
2753
|
+
exitCleanupRef.current = [];
|
|
2754
|
+
isExitingRef.current = false;
|
|
2755
|
+
onExitCompleteRef.current();
|
|
2756
|
+
}
|
|
2757
|
+
},
|
|
2758
|
+
node,
|
|
2759
|
+
style: currentStyle,
|
|
2760
|
+
});
|
|
2761
|
+
}
|
|
2762
|
+
return () => {
|
|
2763
|
+
// Only tear down if actually exiting — this also runs on every
|
|
2764
|
+
// non-exiting re-render, where there's nothing to clean up.
|
|
2765
|
+
if (isExitingRef.current) {
|
|
2766
|
+
exitControllersRef.current.forEach((ctrl) => ctrl.cancel());
|
|
2767
|
+
exitControllersRef.current = [];
|
|
2768
|
+
exitCleanupRef.current.forEach((cleanup) => cleanup());
|
|
2769
|
+
exitCleanupRef.current = [];
|
|
2770
|
+
}
|
|
2771
|
+
};
|
|
2772
|
+
}, [presenceContext?.isExiting]);
|
|
2773
|
+
}
|
|
2774
|
+
function useViewAnimations(nodeRef, propsRef, view, viewOptions, animateValuesRef) {
|
|
2775
|
+
const stateControllersRef = useRef([]);
|
|
2776
|
+
const initialValuesRef = useRef({});
|
|
2777
|
+
const cleanupRef = useRef([]);
|
|
2778
|
+
const isInView = useInView(nodeRef, viewOptions || {});
|
|
2779
|
+
const hasInitializedRef = useRef(false);
|
|
2780
|
+
// Applies initial values (e.g. opacity: 0) to the DOM immediately, before
|
|
2781
|
+
// any animation runs, so the element never flashes with default styles.
|
|
2782
|
+
useLayoutEffect(() => {
|
|
2783
|
+
if (!view)
|
|
2784
|
+
return;
|
|
2785
|
+
const node = nodeRef.current;
|
|
2786
|
+
if (!node)
|
|
2787
|
+
return;
|
|
2788
|
+
if (hasInitializedRef.current)
|
|
2789
|
+
return;
|
|
2790
|
+
const computedStyle = window.getComputedStyle(node);
|
|
2791
|
+
const { style = {} } = propsRef.current;
|
|
2792
|
+
for (const key of Object.keys(view)) {
|
|
2793
|
+
if (!animateValuesRef.current[key]) {
|
|
2794
|
+
const initial = getInitialValue(key, style, node, computedStyle);
|
|
2795
|
+
const value = new AnimateValue(initial);
|
|
2796
|
+
animateValuesRef.current[key] = value;
|
|
2797
|
+
initialValuesRef.current[key] = initial;
|
|
2798
|
+
if (isTransformKey(key)) {
|
|
2799
|
+
const render = createTransformRenderer(node, animateValuesRef.current);
|
|
2800
|
+
render();
|
|
2801
|
+
}
|
|
2802
|
+
else {
|
|
2803
|
+
const css = typeof initial === 'number' &&
|
|
2804
|
+
!['opacity', 'zIndex', 'fontWeight', 'lineHeight'].includes(key)
|
|
2805
|
+
? `${initial}px`
|
|
2806
|
+
: String(initial);
|
|
2807
|
+
node.style[key] = css;
|
|
2808
|
+
}
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
hasInitializedRef.current = true;
|
|
2812
|
+
}, [view]);
|
|
2813
|
+
const applyViewAnimationWrapper = (isActive) => {
|
|
2814
|
+
if (!view)
|
|
2815
|
+
return;
|
|
2816
|
+
const node = nodeRef.current;
|
|
2817
|
+
if (!node)
|
|
2818
|
+
return;
|
|
2819
|
+
cleanupRef.current.forEach((cleanup) => cleanup());
|
|
2820
|
+
cleanupRef.current = [];
|
|
2821
|
+
const computedStyle = window.getComputedStyle(node);
|
|
2822
|
+
const { style = {} } = propsRef.current;
|
|
2823
|
+
const context = {
|
|
2824
|
+
node,
|
|
2825
|
+
style,
|
|
2826
|
+
computedStyle,
|
|
2827
|
+
animateValues: animateValuesRef.current,
|
|
2828
|
+
initialValues: initialValuesRef.current,
|
|
2829
|
+
stateControllers: stateControllersRef.current,
|
|
2830
|
+
cleanup: cleanupRef.current,
|
|
2831
|
+
};
|
|
2832
|
+
applyStateAnimation(view, isActive, context);
|
|
2833
|
+
};
|
|
2834
|
+
useEffect(() => {
|
|
2835
|
+
if (!view)
|
|
2836
|
+
return;
|
|
2837
|
+
applyViewAnimationWrapper(isInView);
|
|
2838
|
+
return () => {
|
|
2839
|
+
stateControllersRef.current.forEach((ctrl) => ctrl.cancel());
|
|
2840
|
+
stateControllersRef.current = [];
|
|
2841
|
+
cleanupRef.current.forEach((cleanup) => cleanup());
|
|
2842
|
+
cleanupRef.current = [];
|
|
2843
|
+
};
|
|
2844
|
+
}, [isInView, view]);
|
|
2845
|
+
useEffect(() => {
|
|
2846
|
+
return () => {
|
|
2847
|
+
stateControllersRef.current.forEach((ctrl) => ctrl.cancel());
|
|
2848
|
+
stateControllersRef.current = [];
|
|
2849
|
+
cleanupRef.current.forEach((cleanup) => cleanup());
|
|
2850
|
+
cleanupRef.current = [];
|
|
2851
|
+
};
|
|
2852
|
+
}, []);
|
|
2853
|
+
}
|
|
2854
|
+
function useStateAnimations(nodeRef, propsRef, hover, press, focus, animateValuesRef) {
|
|
2855
|
+
const stateControllersRef = useRef([]);
|
|
2856
|
+
const initialValuesRef = useRef({});
|
|
2857
|
+
const stateRef = useRef({
|
|
2858
|
+
isHovered: false,
|
|
2859
|
+
isTapped: false,
|
|
2860
|
+
isFocused: false,
|
|
2861
|
+
});
|
|
2862
|
+
const hoverRef = useRef(hover);
|
|
2863
|
+
const pressRef = useRef(press);
|
|
2864
|
+
const focusRef = useRef(focus);
|
|
2865
|
+
hoverRef.current = hover;
|
|
2866
|
+
pressRef.current = press;
|
|
2867
|
+
focusRef.current = focus;
|
|
2868
|
+
const applyStateAnimationWrapper = (stateProp, isActive) => {
|
|
2869
|
+
if (!stateProp)
|
|
2870
|
+
return;
|
|
2871
|
+
const node = nodeRef.current;
|
|
2872
|
+
if (!node)
|
|
2873
|
+
return;
|
|
2874
|
+
const computedStyle = window.getComputedStyle(node);
|
|
2875
|
+
const { style = {}, animate: animateProp, view: viewProp } = propsRef.current;
|
|
2876
|
+
// The real destination for each key, as declared by `animate`/`view` —
|
|
2877
|
+
// used to revert to the true target instead of wherever a hover/press/
|
|
2878
|
+
// focus interaction happened to interrupt an in-flight reveal.
|
|
2879
|
+
const restingTargets = {};
|
|
2880
|
+
for (const key of Object.keys(stateProp)) {
|
|
2881
|
+
const viewRecord = viewProp;
|
|
2882
|
+
const animateRecord = animateProp;
|
|
2883
|
+
const target = (viewRecord && key in viewRecord
|
|
2884
|
+
? extractRestingTarget(viewRecord[key])
|
|
2885
|
+
: undefined) ??
|
|
2886
|
+
(animateRecord && key in animateRecord
|
|
2887
|
+
? extractRestingTarget(animateRecord[key])
|
|
2888
|
+
: undefined);
|
|
2889
|
+
if (target !== undefined)
|
|
2890
|
+
restingTargets[key] = target;
|
|
2891
|
+
}
|
|
2892
|
+
const context = {
|
|
2893
|
+
node,
|
|
2894
|
+
style,
|
|
2895
|
+
computedStyle,
|
|
2896
|
+
animateValues: animateValuesRef.current,
|
|
2897
|
+
initialValues: initialValuesRef.current,
|
|
2898
|
+
stateControllers: stateControllersRef.current,
|
|
2899
|
+
cleanup: [],
|
|
2900
|
+
restingTargets,
|
|
2901
|
+
};
|
|
2902
|
+
applyStateAnimation(stateProp, isActive, context);
|
|
2903
|
+
};
|
|
2904
|
+
useEffect(() => {
|
|
2905
|
+
const node = nodeRef.current;
|
|
2906
|
+
if (!node)
|
|
2907
|
+
return;
|
|
2908
|
+
const handleMouseEnter = () => {
|
|
2909
|
+
if (stateRef.current.isHovered)
|
|
2910
|
+
return;
|
|
2911
|
+
stateRef.current.isHovered = true;
|
|
2912
|
+
applyStateAnimationWrapper(hoverRef.current, true);
|
|
2913
|
+
};
|
|
2914
|
+
const handleMouseLeave = () => {
|
|
2915
|
+
if (!stateRef.current.isHovered)
|
|
2916
|
+
return;
|
|
2917
|
+
stateRef.current.isHovered = false;
|
|
2918
|
+
applyStateAnimationWrapper(hoverRef.current, false);
|
|
2919
|
+
};
|
|
2920
|
+
const handleMouseDown = () => {
|
|
2921
|
+
if (stateRef.current.isTapped)
|
|
2922
|
+
return;
|
|
2923
|
+
stateRef.current.isTapped = true;
|
|
2924
|
+
applyStateAnimationWrapper(pressRef.current, true);
|
|
2925
|
+
};
|
|
2926
|
+
const handleMouseUp = () => {
|
|
2927
|
+
if (!stateRef.current.isTapped)
|
|
2928
|
+
return;
|
|
2929
|
+
stateRef.current.isTapped = false;
|
|
2930
|
+
applyStateAnimationWrapper(pressRef.current, false);
|
|
2931
|
+
// Re-apply hover if still active
|
|
2932
|
+
if (stateRef.current.isHovered && hoverRef.current) {
|
|
2933
|
+
applyStateAnimationWrapper(hoverRef.current, true);
|
|
2934
|
+
}
|
|
2935
|
+
};
|
|
2936
|
+
const handleMouseLeaveForPress = () => {
|
|
2937
|
+
if (stateRef.current.isTapped) {
|
|
2938
|
+
stateRef.current.isTapped = false;
|
|
2939
|
+
applyStateAnimationWrapper(pressRef.current, false);
|
|
2940
|
+
// Re-apply hover if still active
|
|
2941
|
+
if (stateRef.current.isHovered && hoverRef.current) {
|
|
2942
|
+
applyStateAnimationWrapper(hoverRef.current, true);
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
};
|
|
2946
|
+
const handleFocus = () => {
|
|
2947
|
+
if (stateRef.current.isFocused)
|
|
2948
|
+
return;
|
|
2949
|
+
stateRef.current.isFocused = true;
|
|
2950
|
+
applyStateAnimationWrapper(focusRef.current, true);
|
|
2951
|
+
};
|
|
2952
|
+
const handleBlur = () => {
|
|
2953
|
+
if (!stateRef.current.isFocused)
|
|
2954
|
+
return;
|
|
2955
|
+
stateRef.current.isFocused = false;
|
|
2956
|
+
applyStateAnimationWrapper(focusRef.current, false);
|
|
2957
|
+
};
|
|
2958
|
+
if (hoverRef.current) {
|
|
2959
|
+
node.addEventListener('mouseenter', handleMouseEnter);
|
|
2960
|
+
node.addEventListener('mouseleave', handleMouseLeave);
|
|
2961
|
+
}
|
|
2962
|
+
if (pressRef.current) {
|
|
2963
|
+
node.addEventListener('mousedown', handleMouseDown);
|
|
2964
|
+
node.addEventListener('mouseup', handleMouseUp);
|
|
2965
|
+
node.addEventListener('mouseleave', handleMouseLeaveForPress);
|
|
2966
|
+
node.addEventListener('touchstart', handleMouseDown);
|
|
2967
|
+
node.addEventListener('touchend', handleMouseUp);
|
|
2968
|
+
node.addEventListener('touchcancel', handleMouseLeaveForPress);
|
|
2969
|
+
}
|
|
2970
|
+
if (focusRef.current && isFocusable(node)) {
|
|
2971
|
+
node.addEventListener('focus', handleFocus);
|
|
2972
|
+
node.addEventListener('blur', handleBlur);
|
|
2973
|
+
}
|
|
2974
|
+
// Re-apply active state animations after effect re-runs (e.g. config change)
|
|
2975
|
+
if (stateRef.current.isHovered && hoverRef.current) {
|
|
2976
|
+
applyStateAnimationWrapper(hoverRef.current, true);
|
|
2977
|
+
}
|
|
2978
|
+
if (stateRef.current.isTapped && pressRef.current) {
|
|
2979
|
+
applyStateAnimationWrapper(pressRef.current, true);
|
|
2980
|
+
}
|
|
2981
|
+
if (stateRef.current.isFocused && focusRef.current) {
|
|
2982
|
+
applyStateAnimationWrapper(focusRef.current, true);
|
|
2983
|
+
}
|
|
2984
|
+
return () => {
|
|
2985
|
+
if (hoverRef.current) {
|
|
2986
|
+
node.removeEventListener('mouseenter', handleMouseEnter);
|
|
2987
|
+
node.removeEventListener('mouseleave', handleMouseLeave);
|
|
2988
|
+
}
|
|
2989
|
+
if (pressRef.current) {
|
|
2990
|
+
node.removeEventListener('mousedown', handleMouseDown);
|
|
2991
|
+
node.removeEventListener('mouseup', handleMouseUp);
|
|
2992
|
+
node.removeEventListener('mouseleave', handleMouseLeaveForPress);
|
|
2993
|
+
node.removeEventListener('touchstart', handleMouseDown);
|
|
2994
|
+
node.removeEventListener('touchend', handleMouseUp);
|
|
2995
|
+
node.removeEventListener('touchcancel', handleMouseLeaveForPress);
|
|
2996
|
+
}
|
|
2997
|
+
if (focusRef.current) {
|
|
2998
|
+
node.removeEventListener('focus', handleFocus);
|
|
2999
|
+
node.removeEventListener('blur', handleBlur);
|
|
3000
|
+
}
|
|
3001
|
+
stateControllersRef.current.forEach((ctrl) => ctrl.cancel());
|
|
3002
|
+
stateControllersRef.current = [];
|
|
3003
|
+
};
|
|
3004
|
+
}, [
|
|
3005
|
+
serializeAnimateProp(hover),
|
|
3006
|
+
serializeAnimateProp(press),
|
|
3007
|
+
serializeAnimateProp(focus),
|
|
3008
|
+
]);
|
|
3009
|
+
useEffect(() => {
|
|
3010
|
+
return () => {
|
|
3011
|
+
stateControllersRef.current.forEach((ctrl) => ctrl.cancel());
|
|
3012
|
+
stateControllersRef.current = [];
|
|
3013
|
+
};
|
|
3014
|
+
}, []);
|
|
3015
|
+
}
|
|
3016
|
+
function makeAnimated(tag) {
|
|
3017
|
+
const AnimatedComponent = forwardRef((props, ref) => {
|
|
3018
|
+
const nodeRef = useRef(null);
|
|
3019
|
+
const propsRef = useRef(props);
|
|
3020
|
+
const isExitingRef = useRef(false);
|
|
3021
|
+
const animateValuesRef = useRef({});
|
|
3022
|
+
const enterControllersRef = useRef([]);
|
|
3023
|
+
propsRef.current = props;
|
|
3024
|
+
useExitAnimations(nodeRef, propsRef, isExitingRef, animateValuesRef, enterControllersRef);
|
|
3025
|
+
useEnterAnimations(nodeRef, propsRef, props.animate, isExitingRef, animateValuesRef, enterControllersRef);
|
|
3026
|
+
useStateAnimations(nodeRef, propsRef, props.hover, props.press, props.focus, animateValuesRef);
|
|
3027
|
+
useViewAnimations(nodeRef, propsRef, props.view, props.viewOptions, animateValuesRef);
|
|
3028
|
+
useLayoutAnimations(nodeRef, propsRef, isExitingRef, animateValuesRef);
|
|
3029
|
+
useLayoutIdAnimations(nodeRef, propsRef, isExitingRef, animateValuesRef);
|
|
3030
|
+
const { animate, exit, hover, press, focus, view, viewOptions, layout, layoutOptions, layoutId, style, ...restProps } = props;
|
|
3031
|
+
// Filter style object to prevent conflicts between CSS and JS animations
|
|
3032
|
+
const filteredStyle = {};
|
|
3033
|
+
const animatedKeys = new Set([
|
|
3034
|
+
...Object.keys(animateValuesRef.current),
|
|
3035
|
+
...(animate ? Object.keys(animate) : []),
|
|
3036
|
+
]);
|
|
3037
|
+
if (style) {
|
|
3038
|
+
for (const [key, value] of Object.entries(style)) {
|
|
3039
|
+
if (isTransformKey(key) || animatedKeys.has(key))
|
|
3040
|
+
continue;
|
|
3041
|
+
if (value && typeof value === 'object' && 'subscribe' in value)
|
|
3042
|
+
continue;
|
|
3043
|
+
filteredStyle[key] = value;
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
return createElement(tag, {
|
|
3047
|
+
...restProps,
|
|
3048
|
+
style: filteredStyle,
|
|
3049
|
+
ref: combineRefs(nodeRef, ref),
|
|
3050
|
+
});
|
|
3051
|
+
});
|
|
3052
|
+
AnimatedComponent.displayName = `Animated.${tag}`;
|
|
3053
|
+
return AnimatedComponent;
|
|
3054
|
+
}const cache = new Map();
|
|
3055
|
+
const animate = /*#__PURE__*/ new Proxy({}, {
|
|
3056
|
+
get(_, tag) {
|
|
3057
|
+
if (!cache.has(tag)) {
|
|
3058
|
+
cache.set(tag, makeAnimated(tag));
|
|
3059
|
+
}
|
|
3060
|
+
return cache.get(tag);
|
|
3061
|
+
},
|
|
3062
|
+
});/**
|
|
3063
|
+
* Combines multiple AnimateValue instances into a single AnimateValue
|
|
3064
|
+
* using a combiner function.
|
|
3065
|
+
*/
|
|
3066
|
+
function combine(inputs, combiner) {
|
|
3067
|
+
const initial = inputs.map((value) => value.current);
|
|
3068
|
+
const output = new AnimateValue(combiner(...initial));
|
|
3069
|
+
const update = () => {
|
|
3070
|
+
const values = inputs.map((value) => value.current);
|
|
3071
|
+
output.set(combiner(...values));
|
|
3072
|
+
};
|
|
3073
|
+
inputs.forEach((value) => value.subscribe(() => update()));
|
|
3074
|
+
return output;
|
|
3075
|
+
}function to(input, inRange, outRange, config) {
|
|
3076
|
+
return to$1(inRange, outRange, config)(input);
|
|
3077
|
+
}const DEFAULT_STIFFNESS = 158;
|
|
3078
|
+
const DEFAULT_DAMPING = 20;
|
|
3079
|
+
const DEFAULT_MASS = 1;
|
|
3080
|
+
const SPRING_OPTION_KEYS = new Set([
|
|
3081
|
+
'stiffness',
|
|
3082
|
+
'damping',
|
|
3083
|
+
'mass',
|
|
3084
|
+
'from',
|
|
3085
|
+
'onStart',
|
|
3086
|
+
'onChange',
|
|
3087
|
+
'onComplete',
|
|
3088
|
+
]);
|
|
3089
|
+
const TIMING_OPTION_KEYS = new Set([
|
|
3090
|
+
'duration',
|
|
3091
|
+
'easing',
|
|
3092
|
+
'from',
|
|
3093
|
+
'onStart',
|
|
3094
|
+
'onChange',
|
|
3095
|
+
'onComplete',
|
|
3096
|
+
]);
|
|
3097
|
+
// True when `value` is a plain options bag (every key is a known option),
|
|
3098
|
+
// including `{}`. Used to support the target-less overload:
|
|
3099
|
+
// withSpring({ stiffness: 400 })
|
|
3100
|
+
// withTiming({ duration: 300 })
|
|
3101
|
+
// without breaking object targets like withSpring({ x: 10, y: 20 }).
|
|
3102
|
+
function isOptionsOnly(value, optionKeys) {
|
|
3103
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
3104
|
+
return false;
|
|
3105
|
+
}
|
|
3106
|
+
return Object.keys(value).every((key) => optionKeys.has(key));
|
|
3107
|
+
}
|
|
3108
|
+
function withSpring(toOrOpts, opts) {
|
|
3109
|
+
if (opts === undefined && isOptionsOnly(toOrOpts, SPRING_OPTION_KEYS)) {
|
|
3110
|
+
const options = toOrOpts;
|
|
3111
|
+
return {
|
|
3112
|
+
type: 'spring',
|
|
3113
|
+
options: {
|
|
3114
|
+
stiffness: options.stiffness ?? DEFAULT_STIFFNESS,
|
|
3115
|
+
damping: options.damping ?? DEFAULT_DAMPING,
|
|
3116
|
+
mass: options.mass ?? DEFAULT_MASS,
|
|
3117
|
+
from: options.from,
|
|
3118
|
+
onStart: options.onStart,
|
|
3119
|
+
onChange: options.onChange,
|
|
3120
|
+
onComplete: options.onComplete,
|
|
3121
|
+
},
|
|
3122
|
+
};
|
|
3123
|
+
}
|
|
3124
|
+
const to = toOrOpts;
|
|
3125
|
+
return {
|
|
3126
|
+
type: 'spring',
|
|
3127
|
+
to,
|
|
3128
|
+
options: {
|
|
3129
|
+
stiffness: opts?.stiffness ?? DEFAULT_STIFFNESS,
|
|
3130
|
+
damping: opts?.damping ?? DEFAULT_DAMPING,
|
|
3131
|
+
mass: opts?.mass ?? DEFAULT_MASS,
|
|
3132
|
+
from: opts?.from,
|
|
3133
|
+
onStart: opts?.onStart,
|
|
3134
|
+
onChange: opts?.onChange,
|
|
3135
|
+
onComplete: opts?.onComplete,
|
|
3136
|
+
},
|
|
3137
|
+
};
|
|
3138
|
+
}
|
|
3139
|
+
function withTiming(toOrOpts, opts) {
|
|
3140
|
+
if (opts === undefined && isOptionsOnly(toOrOpts, TIMING_OPTION_KEYS)) {
|
|
3141
|
+
const options = toOrOpts;
|
|
3142
|
+
return {
|
|
3143
|
+
type: 'timing',
|
|
3144
|
+
options: {
|
|
3145
|
+
duration: options.duration,
|
|
3146
|
+
easing: options.easing,
|
|
3147
|
+
from: options.from,
|
|
3148
|
+
onStart: options.onStart,
|
|
3149
|
+
onChange: options.onChange,
|
|
3150
|
+
onComplete: options.onComplete,
|
|
3151
|
+
},
|
|
3152
|
+
};
|
|
3153
|
+
}
|
|
3154
|
+
const to = toOrOpts;
|
|
3155
|
+
return {
|
|
3156
|
+
type: 'timing',
|
|
3157
|
+
to,
|
|
3158
|
+
options: {
|
|
3159
|
+
duration: opts?.duration,
|
|
3160
|
+
easing: opts?.easing,
|
|
3161
|
+
from: opts?.from,
|
|
3162
|
+
onStart: opts?.onStart,
|
|
3163
|
+
onChange: opts?.onChange,
|
|
3164
|
+
onComplete: opts?.onComplete,
|
|
3165
|
+
},
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
3168
|
+
const withDecay = (velocity, opts) => ({
|
|
3169
|
+
type: 'decay',
|
|
3170
|
+
options: {
|
|
3171
|
+
velocity,
|
|
3172
|
+
clamp: opts?.clamp,
|
|
3173
|
+
elastic: opts?.elastic,
|
|
3174
|
+
onStart: opts?.onStart,
|
|
3175
|
+
onChange: opts?.onChange,
|
|
3176
|
+
onComplete: opts?.onComplete,
|
|
3177
|
+
},
|
|
3178
|
+
});
|
|
3179
|
+
const withDelay = (ms) => ({
|
|
3180
|
+
type: 'delay',
|
|
3181
|
+
options: { delay: ms },
|
|
3182
|
+
});
|
|
3183
|
+
const withSequence = (animations, opts) => ({
|
|
3184
|
+
type: 'sequence',
|
|
3185
|
+
options: {
|
|
3186
|
+
animations,
|
|
3187
|
+
onStart: opts?.onStart,
|
|
3188
|
+
onComplete: opts?.onComplete,
|
|
3189
|
+
},
|
|
3190
|
+
});
|
|
3191
|
+
// Wraps `descriptor` with a delay proportional to `index`, so animating a
|
|
3192
|
+
// list of items with increasing `index` makes them start one after another
|
|
3193
|
+
// instead of all at once. `each` is the delay step between consecutive
|
|
3194
|
+
// items (default 50ms); `delay` is a base delay applied before staggering
|
|
3195
|
+
// starts (default 0).
|
|
3196
|
+
const withStagger = (index, descriptor, opts) => {
|
|
3197
|
+
const each = opts?.each ?? 50;
|
|
3198
|
+
const baseDelay = opts?.delay ?? 0;
|
|
3199
|
+
const totalDelay = baseDelay + index * each;
|
|
3200
|
+
if (totalDelay <= 0)
|
|
3201
|
+
return descriptor;
|
|
3202
|
+
return withSequence([withDelay(totalDelay), descriptor]);
|
|
3203
|
+
};
|
|
3204
|
+
const withLoop = (animation, iterations = Infinity, opts) => ({
|
|
3205
|
+
type: 'loop',
|
|
3206
|
+
options: {
|
|
3207
|
+
animation,
|
|
3208
|
+
iterations,
|
|
3209
|
+
onStart: opts?.onStart,
|
|
3210
|
+
onComplete: opts?.onComplete,
|
|
3211
|
+
},
|
|
3212
|
+
});function useValue(initial) {
|
|
3213
|
+
const controllerRef = useRef(null);
|
|
3214
|
+
const value = useMemo(() => {
|
|
3215
|
+
if (Array.isArray(initial)) {
|
|
3216
|
+
return initial.map((v) => new AnimateValue(v));
|
|
3217
|
+
}
|
|
3218
|
+
if (typeof initial === 'object') {
|
|
3219
|
+
return Object.fromEntries(Object.entries(initial).map(([k, v]) => [k, new AnimateValue(v)]));
|
|
3220
|
+
}
|
|
3221
|
+
return new AnimateValue(initial);
|
|
3222
|
+
}, []);
|
|
3223
|
+
function set(to) {
|
|
3224
|
+
let ctrl = null;
|
|
3225
|
+
if (Array.isArray(initial)) {
|
|
3226
|
+
ctrl = handleArray(value, to);
|
|
3227
|
+
}
|
|
3228
|
+
else if (typeof initial === 'object') {
|
|
3229
|
+
ctrl = handleObject(value, to);
|
|
3230
|
+
}
|
|
3231
|
+
else {
|
|
3232
|
+
ctrl = handlePrimitive(value, to);
|
|
3233
|
+
}
|
|
3234
|
+
controllerRef.current = ctrl;
|
|
3235
|
+
if (ctrl)
|
|
3236
|
+
ctrl.start();
|
|
3237
|
+
}
|
|
3238
|
+
const controls = {
|
|
3239
|
+
start: () => controllerRef.current?.start(),
|
|
3240
|
+
pause: () => controllerRef.current?.pause(),
|
|
3241
|
+
resume: () => controllerRef.current?.resume(),
|
|
3242
|
+
cancel: () => controllerRef.current?.cancel(),
|
|
3243
|
+
reset: () => controllerRef.current?.reset(),
|
|
3244
|
+
};
|
|
3245
|
+
return [value, set, controls];
|
|
3246
|
+
}
|
|
3247
|
+
function handlePrimitive(value, to) {
|
|
3248
|
+
if (typeof to === 'number' || typeof to === 'string') {
|
|
3249
|
+
value.set(to);
|
|
3250
|
+
return null;
|
|
3251
|
+
}
|
|
3252
|
+
if (to.type === 'sequence') {
|
|
3253
|
+
const animations = to.options?.animations ?? [];
|
|
3254
|
+
const controllers = animations.map((step) => buildAnimation(value, step));
|
|
3255
|
+
return sequence(controllers, to.options);
|
|
3256
|
+
}
|
|
3257
|
+
if (to.type === 'loop') {
|
|
3258
|
+
const animation = to.options?.animation;
|
|
3259
|
+
if (!animation)
|
|
3260
|
+
return null;
|
|
3261
|
+
if (animation.type === 'sequence') {
|
|
3262
|
+
const animations = animation.options?.animations ?? [];
|
|
3263
|
+
const controllers = animations.map((step) => buildAnimation(value, step));
|
|
3264
|
+
return loop(sequence(controllers), to.options?.iterations ?? 0, to.options);
|
|
3265
|
+
}
|
|
3266
|
+
return loop(buildAnimation(value, animation), to.options?.iterations ?? 0, to.options);
|
|
3267
|
+
}
|
|
3268
|
+
return buildAnimation(value, to);
|
|
3269
|
+
}
|
|
3270
|
+
function handleArray(values, to) {
|
|
3271
|
+
if (!isDescriptor(to)) {
|
|
3272
|
+
to.forEach((val, i) => {
|
|
3273
|
+
values[i]?.set(val);
|
|
3274
|
+
});
|
|
3275
|
+
return null;
|
|
3276
|
+
}
|
|
3277
|
+
const desc = to;
|
|
3278
|
+
const valuesRecord = Object.fromEntries(values.map((value, idx) => [idx.toString(), value]));
|
|
3279
|
+
switch (desc.type) {
|
|
3280
|
+
case 'sequence': {
|
|
3281
|
+
const controllers = desc.options.animations.map((step) => step.type === 'delay'
|
|
3282
|
+
? delay(step.options?.delay ?? 0)
|
|
3283
|
+
: buildParallel(valuesRecord, {
|
|
3284
|
+
...step,
|
|
3285
|
+
to: Array.isArray(step.to)
|
|
3286
|
+
? Object.fromEntries(step.to.map((v, i) => [i.toString(), v]))
|
|
3287
|
+
: step.to,
|
|
3288
|
+
}));
|
|
3289
|
+
return sequence(controllers, desc.options);
|
|
3290
|
+
}
|
|
3291
|
+
case 'loop': {
|
|
3292
|
+
const inner = desc.options.animation;
|
|
3293
|
+
if (inner.type === 'sequence') {
|
|
3294
|
+
const seqControllers = inner.options.animations.map((step) => buildParallel(valuesRecord, {
|
|
3295
|
+
...step,
|
|
3296
|
+
to: Array.isArray(step.to)
|
|
3297
|
+
? Object.fromEntries(step.to.map((v, i) => [i.toString(), v]))
|
|
3298
|
+
: step.to,
|
|
3299
|
+
}));
|
|
3300
|
+
const seq = sequence(seqControllers, filterCallbackOptions(inner.options, true));
|
|
3301
|
+
return loop(seq, desc.options.iterations ?? 0, filterCallbackOptions(desc.options, true));
|
|
3302
|
+
}
|
|
3303
|
+
const parallel = buildParallel(valuesRecord, inner);
|
|
3304
|
+
return loop(parallel, desc.options.iterations ?? 0, filterCallbackOptions(desc.options, true));
|
|
3305
|
+
}
|
|
3306
|
+
case 'decay':
|
|
3307
|
+
return buildParallel(valuesRecord, desc);
|
|
3308
|
+
default:
|
|
3309
|
+
return buildParallel(valuesRecord, desc);
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
function handleObject(values, to) {
|
|
3313
|
+
if (isDescriptor(to)) {
|
|
3314
|
+
switch (to.type) {
|
|
3315
|
+
case 'sequence': {
|
|
3316
|
+
const controllers = to.options.animations.map((step) => step.type === 'delay'
|
|
3317
|
+
? delay(step.options.delay ?? 0)
|
|
3318
|
+
: buildParallel(values, step));
|
|
3319
|
+
return sequence(controllers, to.options);
|
|
3320
|
+
}
|
|
3321
|
+
case 'loop': {
|
|
3322
|
+
const inner = to.options.animation;
|
|
3323
|
+
if (inner.type === 'sequence') {
|
|
3324
|
+
const controllers = inner.options.animations.map((step) => buildParallel(values, step));
|
|
3325
|
+
return loop(sequence(controllers, filterCallbackOptions(inner.options, true)), to.options.iterations ?? 0, filterCallbackOptions(to.options, true));
|
|
3326
|
+
}
|
|
3327
|
+
return loop(buildParallel(values, inner), to.options.iterations ?? 0, filterCallbackOptions(to.options, true));
|
|
3328
|
+
}
|
|
3329
|
+
case 'decay':
|
|
3330
|
+
return buildParallel(values, to);
|
|
3331
|
+
default:
|
|
3332
|
+
return buildParallel(values, to);
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
Object.entries(to).forEach(([key, val]) => {
|
|
3336
|
+
values[key]?.set(val);
|
|
3337
|
+
});
|
|
3338
|
+
return null;
|
|
3339
|
+
}/**
|
|
3340
|
+
* Animation Recipes - Pre-built, tested animations for common use cases
|
|
3341
|
+
*/
|
|
3342
|
+
// Shared spring configs, reused across recipes below (deduped so the
|
|
3343
|
+
// minifier can collapse repeated `{ stiffness, damping }` literals into a
|
|
3344
|
+
// single shared reference instead of ~40 structurally-identical copies).
|
|
3345
|
+
const SOFT = { stiffness: 100, damping: 15 };
|
|
3346
|
+
const SCALE = { stiffness: 200, damping: 20 };
|
|
3347
|
+
const BOUNCE = { stiffness: 300, damping: 10 };
|
|
3348
|
+
const SPIN = { stiffness: 50, damping: 10 };
|
|
3349
|
+
const FLIP = { stiffness: 150, damping: 15 };
|
|
3350
|
+
const HOVER = { stiffness: 300, damping: 20 };
|
|
3351
|
+
const PRESS = { stiffness: 400, damping: 25 };
|
|
3352
|
+
const soft = (to) => /*#__PURE__*/ withSpring(to, SOFT);
|
|
3353
|
+
const scale = (to) => /*#__PURE__*/ withSpring(to, SCALE);
|
|
3354
|
+
const fadeIn = { opacity: soft(1) };
|
|
3355
|
+
const fadeOut = { opacity: soft(0) };
|
|
3356
|
+
const fadeInUp = { opacity: soft(1), translateY: soft(0) };
|
|
3357
|
+
const fadeInDown = { opacity: soft(1), translateY: soft(0) };
|
|
3358
|
+
const fadeInLeft = { opacity: soft(1), translateX: soft(0) };
|
|
3359
|
+
const fadeInRight = { opacity: soft(1), translateX: soft(0) };
|
|
3360
|
+
const slideInUp = { translateY: soft(0) };
|
|
3361
|
+
const slideInDown = { translateY: soft(0) };
|
|
3362
|
+
const slideInLeft = { translateX: soft(0) };
|
|
3363
|
+
const slideInRight = { translateX: soft(0) };
|
|
3364
|
+
const slideOutUp = { translateY: soft(-100) };
|
|
3365
|
+
const slideOutDown = { translateY: soft(100) };
|
|
3366
|
+
const slideOutLeft = { translateX: soft(-100) };
|
|
3367
|
+
const slideOutRight = { translateX: soft(100) };
|
|
3368
|
+
const scaleIn = { scale: scale(1), opacity: soft(1) };
|
|
3369
|
+
const scaleOut = { scale: scale(0), opacity: soft(0) };
|
|
3370
|
+
const scaleUp = {
|
|
3371
|
+
scale: /*#__PURE__*/ withSpring(1.1, { stiffness: 150, damping: 15 }),
|
|
3372
|
+
};
|
|
3373
|
+
const scaleDown = {
|
|
3374
|
+
scale: /*#__PURE__*/ withSpring(0.9, { stiffness: 150, damping: 15 }),
|
|
3375
|
+
};
|
|
3376
|
+
const bounceIn = {
|
|
3377
|
+
scale: /*#__PURE__*/ withSpring(1, BOUNCE),
|
|
3378
|
+
opacity: soft(1),
|
|
3379
|
+
};
|
|
3380
|
+
const bounceOut = {
|
|
3381
|
+
scale: /*#__PURE__*/ withSpring(0, BOUNCE),
|
|
3382
|
+
opacity: soft(0),
|
|
3383
|
+
};
|
|
3384
|
+
const rotateIn = { rotate: soft(0), opacity: soft(1) };
|
|
3385
|
+
const rotateOut = { rotate: soft(180), opacity: soft(0) };
|
|
3386
|
+
const spin = {
|
|
3387
|
+
rotate: /*#__PURE__*/ withSpring(360, SPIN),
|
|
3388
|
+
};
|
|
3389
|
+
const zoomIn = { scale: scale(1), opacity: soft(1) };
|
|
3390
|
+
const zoomOut = { scale: scale(0), opacity: soft(0) };
|
|
3391
|
+
const flipX = {
|
|
3392
|
+
rotateX: /*#__PURE__*/ withSpring(0, FLIP),
|
|
3393
|
+
};
|
|
3394
|
+
const flipY = {
|
|
3395
|
+
rotateY: /*#__PURE__*/ withSpring(0, FLIP),
|
|
3396
|
+
};
|
|
3397
|
+
const slideFadeIn = { opacity: soft(1), translateY: soft(0) };
|
|
3398
|
+
const slideFadeOut = { opacity: soft(0), translateY: soft(20) };
|
|
3399
|
+
const scaleFadeIn = { scale: scale(1), opacity: soft(1) };
|
|
3400
|
+
const scaleFadeOut = {
|
|
3401
|
+
scale: /*#__PURE__*/ withSpring(0.8, SCALE),
|
|
3402
|
+
opacity: soft(0),
|
|
3403
|
+
};
|
|
3404
|
+
const hoverScale = {
|
|
3405
|
+
scale: /*#__PURE__*/ withSpring(1.05, HOVER),
|
|
3406
|
+
};
|
|
3407
|
+
const hoverLift = {
|
|
3408
|
+
translateY: /*#__PURE__*/ withSpring(-5, HOVER),
|
|
3409
|
+
scale: /*#__PURE__*/ withSpring(1.02, HOVER),
|
|
3410
|
+
};
|
|
3411
|
+
const hoverGlow = {
|
|
3412
|
+
scale: /*#__PURE__*/ withSpring(1.05, HOVER),
|
|
3413
|
+
opacity: /*#__PURE__*/ withSpring(0.9, HOVER),
|
|
3414
|
+
};
|
|
3415
|
+
const pressScale = {
|
|
3416
|
+
scale: /*#__PURE__*/ withSpring(0.95, PRESS),
|
|
3417
|
+
};
|
|
3418
|
+
const pressDown = {
|
|
3419
|
+
translateY: /*#__PURE__*/ withSpring(2, PRESS),
|
|
3420
|
+
scale: /*#__PURE__*/ withSpring(0.98, PRESS),
|
|
3421
|
+
};
|
|
3422
|
+
const exitFade = { opacity: soft(0) };
|
|
3423
|
+
const exitSlideUp = { opacity: soft(0), translateY: soft(-20) };
|
|
3424
|
+
const exitSlideDown = { opacity: soft(0), translateY: soft(20) };
|
|
3425
|
+
const exitScale = {
|
|
3426
|
+
scale: /*#__PURE__*/ withSpring(0.8, SCALE),
|
|
3427
|
+
opacity: soft(0),
|
|
3428
|
+
};
|
|
3429
|
+
const recipes = {
|
|
3430
|
+
fadeIn,
|
|
3431
|
+
fadeOut,
|
|
3432
|
+
fadeInUp,
|
|
3433
|
+
fadeInDown,
|
|
3434
|
+
fadeInLeft,
|
|
3435
|
+
fadeInRight,
|
|
3436
|
+
slideInUp,
|
|
3437
|
+
slideInDown,
|
|
3438
|
+
slideInLeft,
|
|
3439
|
+
slideInRight,
|
|
3440
|
+
slideOutUp,
|
|
3441
|
+
slideOutDown,
|
|
3442
|
+
slideOutLeft,
|
|
3443
|
+
slideOutRight,
|
|
3444
|
+
scaleIn,
|
|
3445
|
+
scaleOut,
|
|
3446
|
+
scaleUp,
|
|
3447
|
+
scaleDown,
|
|
3448
|
+
bounceIn,
|
|
3449
|
+
bounceOut,
|
|
3450
|
+
rotateIn,
|
|
3451
|
+
rotateOut,
|
|
3452
|
+
spin,
|
|
3453
|
+
zoomIn,
|
|
3454
|
+
zoomOut,
|
|
3455
|
+
flipX,
|
|
3456
|
+
flipY,
|
|
3457
|
+
slideFadeIn,
|
|
3458
|
+
slideFadeOut,
|
|
3459
|
+
scaleFadeIn,
|
|
3460
|
+
scaleFadeOut,
|
|
3461
|
+
hoverScale,
|
|
3462
|
+
hoverLift,
|
|
3463
|
+
hoverGlow,
|
|
3464
|
+
pressScale,
|
|
3465
|
+
pressDown,
|
|
3466
|
+
exitFade,
|
|
3467
|
+
exitSlideUp,
|
|
3468
|
+
exitSlideDown,
|
|
3469
|
+
exitScale,
|
|
3470
|
+
};function useOutsideClick(ref, callback, deps = []) {
|
|
3471
|
+
const cbRef = useRef(callback);
|
|
3472
|
+
useEffect(() => {
|
|
3473
|
+
cbRef.current = callback;
|
|
3474
|
+
}, [callback, ...deps]);
|
|
3475
|
+
useEffect(() => {
|
|
3476
|
+
function onClick(event) {
|
|
3477
|
+
const el = ref.current;
|
|
3478
|
+
const target = event.target;
|
|
3479
|
+
if (!el || !target || !target.isConnected || !el.isConnected)
|
|
3480
|
+
return;
|
|
3481
|
+
if (!el.contains(target)) {
|
|
3482
|
+
cbRef.current(event);
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
document.addEventListener('mousedown', onClick);
|
|
3486
|
+
document.addEventListener('touchstart', onClick);
|
|
3487
|
+
return () => {
|
|
3488
|
+
document.removeEventListener('mousedown', onClick);
|
|
3489
|
+
document.removeEventListener('touchstart', onClick);
|
|
3490
|
+
};
|
|
3491
|
+
}, [ref]);
|
|
3492
|
+
}class Gesture {
|
|
3493
|
+
constructor() {
|
|
3494
|
+
this.changeListeners = new Set();
|
|
3495
|
+
this.endListeners = new Set();
|
|
3496
|
+
}
|
|
3497
|
+
onChange(listener) {
|
|
3498
|
+
this.changeListeners.add(listener);
|
|
3499
|
+
return this;
|
|
3500
|
+
}
|
|
3501
|
+
onEnd(listener) {
|
|
3502
|
+
this.endListeners.add(listener);
|
|
3503
|
+
return this;
|
|
3504
|
+
}
|
|
3505
|
+
emitChange(event) {
|
|
3506
|
+
this.changeListeners.forEach((fn) => fn(event));
|
|
3507
|
+
}
|
|
3508
|
+
emitEnd(event) {
|
|
3509
|
+
this.endListeners.forEach((fn) => fn(event));
|
|
3510
|
+
}
|
|
3511
|
+
}
|
|
3512
|
+
Gesture.VELOCITY_LIMIT = 20;class DragGesture extends Gesture {
|
|
3513
|
+
constructor(config = {}) {
|
|
3514
|
+
super();
|
|
3515
|
+
this.prev = { x: 0, y: 0 };
|
|
3516
|
+
this.lastTime = 0;
|
|
3517
|
+
this.movement = { x: 0, y: 0 };
|
|
3518
|
+
this.velocity = { x: 0, y: 0 };
|
|
3519
|
+
this.start = { x: 0, y: 0 };
|
|
3520
|
+
this.offset = { x: 0, y: 0 };
|
|
3521
|
+
this.pointerCaptured = false;
|
|
3522
|
+
this.activePointerId = null;
|
|
3523
|
+
this.attachedEls = new Set();
|
|
3524
|
+
this.activeEl = null;
|
|
3525
|
+
this.pointerDownPos = { x: 0, y: 0 };
|
|
3526
|
+
this.thresholdPassed = false;
|
|
3527
|
+
this.config = config;
|
|
3528
|
+
}
|
|
3529
|
+
attach(elements) {
|
|
3530
|
+
if (elements === window)
|
|
3531
|
+
return () => { };
|
|
3532
|
+
const els = Array.isArray(elements) ? elements : [elements];
|
|
3533
|
+
const down = this.onDown.bind(this);
|
|
3534
|
+
const move = this.onMove.bind(this);
|
|
3535
|
+
const up = this.onUp.bind(this);
|
|
3536
|
+
els.forEach((el) => {
|
|
3537
|
+
this.attachedEls.add(el);
|
|
3538
|
+
el.addEventListener('pointerdown', down, { passive: false });
|
|
3539
|
+
});
|
|
3540
|
+
window.addEventListener('pointermove', move, { passive: false });
|
|
3541
|
+
window.addEventListener('pointerup', up);
|
|
3542
|
+
window.addEventListener('pointercancel', up);
|
|
3543
|
+
return () => {
|
|
3544
|
+
els.forEach((el) => {
|
|
3545
|
+
el.removeEventListener('pointerdown', down);
|
|
3546
|
+
this.attachedEls.delete(el);
|
|
3547
|
+
});
|
|
3548
|
+
window.removeEventListener('pointermove', move);
|
|
3549
|
+
window.removeEventListener('pointerup', up);
|
|
3550
|
+
window.removeEventListener('pointercancel', up);
|
|
3551
|
+
};
|
|
3552
|
+
}
|
|
3553
|
+
onDown(e) {
|
|
3554
|
+
if (e.button !== 0)
|
|
3555
|
+
return;
|
|
3556
|
+
const target = e.currentTarget;
|
|
3557
|
+
if (!this.attachedEls.has(target))
|
|
3558
|
+
return;
|
|
3559
|
+
this.activeEl = target;
|
|
3560
|
+
this.activePointerId = e.pointerId;
|
|
3561
|
+
this.pointerCaptured = false;
|
|
3562
|
+
this.start =
|
|
3563
|
+
this.thresholdPassed === false && this.start.x === 0 && this.start.y === 0
|
|
3564
|
+
? this.config.initial?.() ?? { x: 0, y: 0 }
|
|
3565
|
+
: { ...this.offset };
|
|
3566
|
+
this.offset = { ...this.start };
|
|
3567
|
+
this.movement = { x: 0, y: 0 };
|
|
3568
|
+
this.velocity = { x: 0, y: 0 };
|
|
3569
|
+
this.pointerDownPos = { x: e.clientX, y: e.clientY };
|
|
3570
|
+
this.thresholdPassed = false;
|
|
3571
|
+
this.prev = { x: e.clientX, y: e.clientY };
|
|
3572
|
+
this.lastTime = e.timeStamp;
|
|
3573
|
+
this.emitChange({
|
|
3574
|
+
down: true,
|
|
3575
|
+
movement: { x: 0, y: 0 },
|
|
3576
|
+
offset: { ...this.offset },
|
|
3577
|
+
velocity: { x: 0, y: 0 },
|
|
3578
|
+
event: e,
|
|
3579
|
+
cancel: this.cancel.bind(this),
|
|
3580
|
+
});
|
|
3581
|
+
}
|
|
3582
|
+
onMove(e) {
|
|
3583
|
+
if (this.activePointerId !== e.pointerId || !this.activeEl)
|
|
3584
|
+
return;
|
|
3585
|
+
const threshold = this.config.threshold ?? 0;
|
|
3586
|
+
if (!this.thresholdPassed) {
|
|
3587
|
+
const dxTotal = e.clientX - this.pointerDownPos.x;
|
|
3588
|
+
const dyTotal = e.clientY - this.pointerDownPos.y;
|
|
3589
|
+
const dist = Math.hypot(dxTotal, dyTotal);
|
|
3590
|
+
if (dist < threshold)
|
|
3591
|
+
return;
|
|
3592
|
+
this.thresholdPassed = true;
|
|
3593
|
+
this.activeEl.setPointerCapture(e.pointerId);
|
|
3594
|
+
this.pointerCaptured = true;
|
|
3595
|
+
}
|
|
3596
|
+
if (this.pointerCaptured) {
|
|
3597
|
+
e.preventDefault();
|
|
3598
|
+
}
|
|
3599
|
+
const dt = Math.max((e.timeStamp - this.lastTime) / 1000, 1e-6);
|
|
3600
|
+
this.lastTime = e.timeStamp;
|
|
3601
|
+
const dx = e.clientX - this.prev.x;
|
|
3602
|
+
const dy = e.clientY - this.prev.y;
|
|
3603
|
+
const rawX = dx / dt / 1000;
|
|
3604
|
+
const rawY = dy / dt / 1000;
|
|
3605
|
+
this.velocity = {
|
|
3606
|
+
x: clamp(rawX, -Gesture.VELOCITY_LIMIT, Gesture.VELOCITY_LIMIT),
|
|
3607
|
+
y: clamp(rawY, -Gesture.VELOCITY_LIMIT, Gesture.VELOCITY_LIMIT),
|
|
3608
|
+
};
|
|
3609
|
+
const moveRaw = {
|
|
3610
|
+
x: e.clientX - this.pointerDownPos.x,
|
|
3611
|
+
y: e.clientY - this.pointerDownPos.y,
|
|
3612
|
+
};
|
|
3613
|
+
this.movement = {
|
|
3614
|
+
x: this.config.axis === 'y' ? 0 : moveRaw.x,
|
|
3615
|
+
y: this.config.axis === 'x' ? 0 : moveRaw.y,
|
|
3616
|
+
};
|
|
3617
|
+
this.offset = {
|
|
3618
|
+
x: this.start.x + this.movement.x,
|
|
3619
|
+
y: this.start.y + this.movement.y,
|
|
3620
|
+
};
|
|
3621
|
+
this.prev = { x: e.clientX, y: e.clientY };
|
|
3622
|
+
this.emitChange({
|
|
3623
|
+
down: true,
|
|
3624
|
+
movement: { ...this.movement },
|
|
3625
|
+
offset: { ...this.offset },
|
|
3626
|
+
velocity: { ...this.velocity },
|
|
3627
|
+
event: e,
|
|
3628
|
+
cancel: this.cancel.bind(this),
|
|
3629
|
+
});
|
|
3630
|
+
}
|
|
3631
|
+
onUp(e) {
|
|
3632
|
+
if (this.activePointerId !== e.pointerId || !this.activeEl)
|
|
3633
|
+
return;
|
|
3634
|
+
this.activeEl.releasePointerCapture(e.pointerId);
|
|
3635
|
+
this.emitEnd({
|
|
3636
|
+
down: false,
|
|
3637
|
+
movement: { ...this.movement },
|
|
3638
|
+
offset: { ...this.offset },
|
|
3639
|
+
velocity: { ...this.velocity },
|
|
3640
|
+
event: e,
|
|
3641
|
+
cancel: this.cancel.bind(this),
|
|
3642
|
+
});
|
|
3643
|
+
this.activePointerId = null;
|
|
3644
|
+
this.pointerCaptured = false;
|
|
3645
|
+
}
|
|
3646
|
+
cancel() {
|
|
3647
|
+
if (this.activeEl && this.activePointerId !== null) {
|
|
3648
|
+
this.activeEl.releasePointerCapture(this.activePointerId);
|
|
3649
|
+
this.activePointerId = null;
|
|
3650
|
+
this.activeEl = null;
|
|
3651
|
+
}
|
|
3652
|
+
}
|
|
3653
|
+
}function useRecognizer(GestureClass, refs, onEvent, config) {
|
|
3654
|
+
const handlerRef = useLatest(onEvent);
|
|
3655
|
+
const configRef = useLatest(config);
|
|
3656
|
+
if (refs === window) {
|
|
3657
|
+
const gestureRef = useRef();
|
|
3658
|
+
if (!gestureRef.current) {
|
|
3659
|
+
const g = new GestureClass(configRef.current);
|
|
3660
|
+
const handler = (e) => handlerRef.current({ ...e, index: 0 });
|
|
3661
|
+
g.onChange(handler).onEnd(handler);
|
|
3662
|
+
gestureRef.current = g;
|
|
3663
|
+
}
|
|
3664
|
+
useEffect(() => {
|
|
3665
|
+
const cleanup = gestureRef.current.attach(window);
|
|
3666
|
+
return () => {
|
|
3667
|
+
cleanup();
|
|
3668
|
+
};
|
|
3669
|
+
}, [refs]);
|
|
3670
|
+
return;
|
|
3671
|
+
}
|
|
3672
|
+
const list = Array.isArray(refs) ? refs : [refs];
|
|
3673
|
+
const gesturesRef = useRef([]);
|
|
3674
|
+
const prevCleanupsRef = useRef([]);
|
|
3675
|
+
const listLengthRef = useRef(list.length);
|
|
3676
|
+
if (gesturesRef.current.length !== list.length) {
|
|
3677
|
+
gesturesRef.current = list.map((_, i) => {
|
|
3678
|
+
const g = new GestureClass(configRef.current);
|
|
3679
|
+
const handler = (e) => handlerRef.current({ ...e, index: i });
|
|
3680
|
+
g.onChange(handler).onEnd(handler);
|
|
3681
|
+
return g;
|
|
3682
|
+
});
|
|
3683
|
+
listLengthRef.current = list.length;
|
|
3684
|
+
}
|
|
3685
|
+
useEffect(() => {
|
|
3686
|
+
prevCleanupsRef.current.forEach((fn) => fn());
|
|
3687
|
+
prevCleanupsRef.current = [];
|
|
3688
|
+
const cleanups = [];
|
|
3689
|
+
list.forEach((r, i) => {
|
|
3690
|
+
const el = r.current;
|
|
3691
|
+
if (el && gesturesRef.current[i]) {
|
|
3692
|
+
const cleanup = gesturesRef.current[i].attach(el);
|
|
3693
|
+
cleanups.push(cleanup);
|
|
3694
|
+
}
|
|
3695
|
+
});
|
|
3696
|
+
prevCleanupsRef.current = cleanups;
|
|
3697
|
+
return () => {
|
|
3698
|
+
cleanups.forEach((fn) => fn());
|
|
3699
|
+
prevCleanupsRef.current = [];
|
|
3700
|
+
};
|
|
3701
|
+
}, [list]);
|
|
3702
|
+
}
|
|
3703
|
+
function useLatest(value) {
|
|
3704
|
+
const ref = useRef(value);
|
|
3705
|
+
useEffect(() => {
|
|
3706
|
+
ref.current = value;
|
|
3707
|
+
}, [value]);
|
|
3708
|
+
return ref;
|
|
3709
|
+
}function useDrag(refs, onDrag, config) {
|
|
3710
|
+
return useRecognizer(DragGesture, refs, onDrag, config);
|
|
3711
|
+
}class MoveGesture extends Gesture {
|
|
3712
|
+
constructor() {
|
|
3713
|
+
super(...arguments);
|
|
3714
|
+
this.attachedEls = new Set();
|
|
3715
|
+
this.prev = { x: 0, y: 0 };
|
|
3716
|
+
this.lastTime = 0;
|
|
3717
|
+
this.movement = { x: 0, y: 0 };
|
|
3718
|
+
this.offset = { x: 0, y: 0 };
|
|
3719
|
+
this.velocity = { x: 0, y: 0 };
|
|
3720
|
+
this.startPos = null;
|
|
3721
|
+
}
|
|
3722
|
+
attach(elements) {
|
|
3723
|
+
const els = Array.isArray(elements) ? elements : [elements];
|
|
3724
|
+
const move = this.onMove.bind(this);
|
|
3725
|
+
const leave = this.onLeave.bind(this);
|
|
3726
|
+
els.forEach((el) => {
|
|
3727
|
+
this.attachedEls.add(el);
|
|
3728
|
+
el.addEventListener('pointermove', move, { passive: false });
|
|
3729
|
+
el.addEventListener('pointerleave', leave);
|
|
3730
|
+
});
|
|
3731
|
+
return () => {
|
|
3732
|
+
els.forEach((el) => {
|
|
3733
|
+
el.removeEventListener('pointermove', move);
|
|
3734
|
+
el.removeEventListener('pointerleave', leave);
|
|
3735
|
+
this.attachedEls.delete(el);
|
|
3736
|
+
});
|
|
3737
|
+
};
|
|
3738
|
+
}
|
|
3739
|
+
cancel() { }
|
|
3740
|
+
onMove(e) {
|
|
3741
|
+
const now = e.timeStamp;
|
|
3742
|
+
if (this.startPos === null) {
|
|
3743
|
+
this.startPos = { x: e.clientX, y: e.clientY };
|
|
3744
|
+
this.prev = { x: e.clientX, y: e.clientY };
|
|
3745
|
+
this.lastTime = now;
|
|
3746
|
+
}
|
|
3747
|
+
const dt = Math.max((now - this.lastTime) / 1000, 1e-6);
|
|
3748
|
+
this.lastTime = now;
|
|
3749
|
+
const dx = e.clientX - this.prev.x;
|
|
3750
|
+
const dy = e.clientY - this.prev.y;
|
|
3751
|
+
this.prev = { x: e.clientX, y: e.clientY };
|
|
3752
|
+
this.movement = {
|
|
3753
|
+
x: e.clientX - this.startPos.x,
|
|
3754
|
+
y: e.clientY - this.startPos.y,
|
|
3755
|
+
};
|
|
3756
|
+
const tgt = e.currentTarget;
|
|
3757
|
+
const rect = tgt instanceof HTMLElement
|
|
3758
|
+
? tgt.getBoundingClientRect()
|
|
3759
|
+
: { left: 0, top: 0 };
|
|
3760
|
+
this.offset = {
|
|
3761
|
+
x: e.clientX - rect.left,
|
|
3762
|
+
y: e.clientY - rect.top,
|
|
3763
|
+
};
|
|
3764
|
+
const rawVx = dx / dt / 1000;
|
|
3765
|
+
const rawVy = dy / dt / 1000;
|
|
3766
|
+
this.velocity = {
|
|
3767
|
+
x: clamp(rawVx, -Gesture.VELOCITY_LIMIT, Gesture.VELOCITY_LIMIT),
|
|
3768
|
+
y: clamp(rawVy, -Gesture.VELOCITY_LIMIT, Gesture.VELOCITY_LIMIT),
|
|
3769
|
+
};
|
|
3770
|
+
this.emitChange({
|
|
3771
|
+
movement: { ...this.movement },
|
|
3772
|
+
offset: { ...this.offset },
|
|
3773
|
+
velocity: { ...this.velocity },
|
|
3774
|
+
event: e,
|
|
3775
|
+
cancel: () => this.onLeave(e),
|
|
3776
|
+
});
|
|
3777
|
+
}
|
|
3778
|
+
onLeave(e) {
|
|
3779
|
+
this.emitEnd({
|
|
3780
|
+
movement: { ...this.movement },
|
|
3781
|
+
offset: { ...this.offset },
|
|
3782
|
+
velocity: { ...this.velocity },
|
|
3783
|
+
event: e,
|
|
3784
|
+
cancel: () => { },
|
|
3785
|
+
});
|
|
3786
|
+
}
|
|
3787
|
+
}function useMove(refs, onMove) {
|
|
3788
|
+
return useRecognizer(MoveGesture, refs, onMove);
|
|
3789
|
+
}class ScrollGesture extends Gesture {
|
|
3790
|
+
constructor() {
|
|
3791
|
+
super(...arguments);
|
|
3792
|
+
this.attachedEls = new Set();
|
|
3793
|
+
this.movement = { x: 0, y: 0 };
|
|
3794
|
+
this.offset = { x: 0, y: 0 };
|
|
3795
|
+
this.velocity = { x: 0, y: 0 };
|
|
3796
|
+
this.prevScroll = { x: 0, y: 0 };
|
|
3797
|
+
this.lastTime = 0;
|
|
3798
|
+
}
|
|
3799
|
+
attach(elements) {
|
|
3800
|
+
const els = Array.isArray(elements) ? elements : [elements];
|
|
3801
|
+
const scroll = this.onScroll.bind(this);
|
|
3802
|
+
els.forEach((el) => {
|
|
3803
|
+
this.attachedEls.add(el);
|
|
3804
|
+
el.addEventListener('scroll', scroll, { passive: true });
|
|
3805
|
+
});
|
|
3806
|
+
return () => {
|
|
3807
|
+
els.forEach((el) => {
|
|
3808
|
+
el.removeEventListener('scroll', scroll);
|
|
3809
|
+
this.attachedEls.delete(el);
|
|
3810
|
+
});
|
|
3811
|
+
if (this.endTimeout != null) {
|
|
3812
|
+
clearTimeout(this.endTimeout);
|
|
3813
|
+
this.endTimeout = undefined;
|
|
3814
|
+
}
|
|
3815
|
+
};
|
|
3816
|
+
}
|
|
3817
|
+
cancel() { }
|
|
3818
|
+
onScroll(e) {
|
|
3819
|
+
const now = Date.now();
|
|
3820
|
+
const dt = Math.max((now - this.lastTime) / 1000, 1e-6);
|
|
3821
|
+
this.lastTime = now;
|
|
3822
|
+
const tgt = e.currentTarget;
|
|
3823
|
+
const x = tgt instanceof HTMLElement ? tgt.scrollLeft : window.scrollX;
|
|
3824
|
+
const y = tgt instanceof HTMLElement ? tgt.scrollTop : window.scrollY;
|
|
3825
|
+
const dx = x - this.prevScroll.x;
|
|
3826
|
+
const dy = y - this.prevScroll.y;
|
|
3827
|
+
this.prevScroll = { x, y };
|
|
3828
|
+
this.movement = { x: dx, y: dy };
|
|
3829
|
+
this.offset = { x, y };
|
|
3830
|
+
const rawX = dx / dt / 1000;
|
|
3831
|
+
const rawY = dy / dt / 1000;
|
|
3832
|
+
this.velocity = {
|
|
3833
|
+
x: clamp(rawX, -Gesture.VELOCITY_LIMIT, Gesture.VELOCITY_LIMIT),
|
|
3834
|
+
y: clamp(rawY, -Gesture.VELOCITY_LIMIT, Gesture.VELOCITY_LIMIT),
|
|
3835
|
+
};
|
|
3836
|
+
this.emitChange({
|
|
3837
|
+
movement: { ...this.movement },
|
|
3838
|
+
offset: { ...this.offset },
|
|
3839
|
+
velocity: { ...this.velocity },
|
|
3840
|
+
event: e,
|
|
3841
|
+
cancel: () => {
|
|
3842
|
+
if (this.endTimeout != null)
|
|
3843
|
+
clearTimeout(this.endTimeout);
|
|
3844
|
+
},
|
|
3845
|
+
});
|
|
3846
|
+
if (this.endTimeout != null)
|
|
3847
|
+
clearTimeout(this.endTimeout);
|
|
3848
|
+
this.endTimeout = window.setTimeout(() => {
|
|
3849
|
+
this.emitEnd({
|
|
3850
|
+
movement: { ...this.movement },
|
|
3851
|
+
offset: { ...this.offset },
|
|
3852
|
+
velocity: { ...this.velocity },
|
|
3853
|
+
event: e,
|
|
3854
|
+
cancel: () => { },
|
|
3855
|
+
});
|
|
3856
|
+
}, 150);
|
|
3857
|
+
}
|
|
3858
|
+
}function useScrollProgress(refs, { target, axis = 'y', offset = ['start start', 'end end'], animate = true, toDescriptor = (v) => withSpring(v), } = {}) {
|
|
3859
|
+
const [yProgress, setYProgress] = useValue(0);
|
|
3860
|
+
const [xProgress, setXProgress] = useValue(0);
|
|
3861
|
+
const rangeRef = useRef([0, 0]);
|
|
3862
|
+
useEffect(() => {
|
|
3863
|
+
const containerEl = refs instanceof Window ? window : refs.current;
|
|
3864
|
+
const targetEl = target?.current ?? document.documentElement;
|
|
3865
|
+
rangeRef.current = getScrollRange(offset, targetEl, containerEl, axis);
|
|
3866
|
+
}, [refs, target, axis, offset]);
|
|
3867
|
+
useRecognizer(ScrollGesture, refs, (e) => {
|
|
3868
|
+
const pos = axis === 'y' ? e.offset.y : e.offset.x;
|
|
3869
|
+
const [start, end] = rangeRef.current;
|
|
3870
|
+
const raw = end === start ? (pos < start ? 0 : 1) : (pos - start) / (end - start);
|
|
3871
|
+
const t = Math.min(Math.max(raw, 0), 1);
|
|
3872
|
+
const apply = animate ? toDescriptor : (v) => v;
|
|
3873
|
+
if (axis === 'y') {
|
|
3874
|
+
setYProgress(apply(t));
|
|
3875
|
+
setXProgress(0);
|
|
3876
|
+
}
|
|
3877
|
+
else {
|
|
3878
|
+
setXProgress(apply(t));
|
|
3879
|
+
setYProgress(0);
|
|
3880
|
+
}
|
|
3881
|
+
});
|
|
3882
|
+
return { scrollYProgress: yProgress, scrollXProgress: xProgress };
|
|
3883
|
+
}
|
|
3884
|
+
function getScroll(el, axis) {
|
|
3885
|
+
if (el instanceof HTMLElement) {
|
|
3886
|
+
return axis === 'y' ? el.scrollTop : el.scrollLeft;
|
|
3887
|
+
}
|
|
3888
|
+
return axis === 'y' ? window.scrollY : window.scrollX;
|
|
3889
|
+
}
|
|
3890
|
+
function getSize(el, axis) {
|
|
3891
|
+
if (el instanceof HTMLElement) {
|
|
3892
|
+
return axis === 'y' ? el.clientHeight : el.clientWidth;
|
|
3893
|
+
}
|
|
3894
|
+
return axis === 'y' ? window.innerHeight : window.innerWidth;
|
|
3895
|
+
}
|
|
3896
|
+
function getScrollRange([startMarker, endMarker], targetEl, containerEl, axis) {
|
|
3897
|
+
return [
|
|
3898
|
+
resolveMarker(startMarker, targetEl, containerEl, axis),
|
|
3899
|
+
resolveMarker(endMarker, targetEl, containerEl, axis),
|
|
3900
|
+
];
|
|
3901
|
+
}
|
|
3902
|
+
function resolveMarker(marker, targetEl, containerEl, axis) {
|
|
3903
|
+
const [tMark, cMark = tMark] = marker.trim().split(/\s+/);
|
|
3904
|
+
if (containerEl instanceof HTMLElement) {
|
|
3905
|
+
const tRect = targetEl.getBoundingClientRect();
|
|
3906
|
+
const cRect = containerEl.getBoundingClientRect();
|
|
3907
|
+
const scroll = getScroll(containerEl, axis);
|
|
3908
|
+
const elementStart = (axis === 'y' ? tRect.top - cRect.top : tRect.left - cRect.left) + scroll;
|
|
3909
|
+
const elementSize = axis === 'y' ? tRect.height : tRect.width;
|
|
3910
|
+
const containerSize = getSize(containerEl, axis);
|
|
3911
|
+
const elemPos = resolveEdge(tMark, elementStart, elementSize, containerSize);
|
|
3912
|
+
const contPos = resolveEdge(cMark, 0, containerSize, containerSize);
|
|
3913
|
+
return elemPos - contPos;
|
|
3914
|
+
}
|
|
3915
|
+
else {
|
|
3916
|
+
const elemPos = parseEdgeValue(tMark, axis, targetEl, false);
|
|
3917
|
+
const contPos = parseEdgeValue(cMark, axis, window, true);
|
|
3918
|
+
return elemPos - contPos;
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
function resolveEdge(edge, base, size, containerSize) {
|
|
3922
|
+
if (edge === 'start')
|
|
3923
|
+
return base;
|
|
3924
|
+
if (edge === 'center')
|
|
3925
|
+
return base + size / 2;
|
|
3926
|
+
if (edge === 'end')
|
|
3927
|
+
return base + size;
|
|
3928
|
+
const m = edge.match(/^(-?\d+(?:\.\d+)?)(px|%|vw|vh)?$/);
|
|
3929
|
+
if (!m)
|
|
3930
|
+
throw new Error(`Invalid edge marker “${edge}”`);
|
|
3931
|
+
const n = parseFloat(m[1]);
|
|
3932
|
+
const unit = m[2];
|
|
3933
|
+
switch (unit) {
|
|
3934
|
+
case 'px':
|
|
3935
|
+
return base + n;
|
|
3936
|
+
case '%':
|
|
3937
|
+
return base + (n / 100) * size;
|
|
3938
|
+
case 'vw':
|
|
3939
|
+
return base + (n / 100) * containerSize;
|
|
3940
|
+
case 'vh':
|
|
3941
|
+
return base + (n / 100) * containerSize;
|
|
3942
|
+
default:
|
|
3943
|
+
return base + n * size;
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
function parseEdgeValue(edge, axis, el, isContainer) {
|
|
3947
|
+
const scrollTarget = isContainer ? el : el;
|
|
3948
|
+
const base = isContainer
|
|
3949
|
+
? 0
|
|
3950
|
+
: (() => {
|
|
3951
|
+
if (!(el instanceof HTMLElement))
|
|
3952
|
+
throw new Error('Expected HTMLElement for element-relative edge');
|
|
3953
|
+
const rect = el.getBoundingClientRect();
|
|
3954
|
+
const pageScroll = axis === 'y'
|
|
3955
|
+
? window.pageYOffset || window.scrollY
|
|
3956
|
+
: window.pageXOffset || window.scrollX;
|
|
3957
|
+
return (axis === 'y' ? rect.top : rect.left) + pageScroll;
|
|
3958
|
+
})();
|
|
3959
|
+
const size = isContainer
|
|
3960
|
+
? getSize(el, axis)
|
|
3961
|
+
: (() => {
|
|
3962
|
+
if (!(el instanceof HTMLElement))
|
|
3963
|
+
throw new Error();
|
|
3964
|
+
const rect = el.getBoundingClientRect();
|
|
3965
|
+
return axis === 'y' ? rect.height : rect.width;
|
|
3966
|
+
})();
|
|
3967
|
+
return resolveEdge(edge, base, size, getSize(scrollTarget, axis));
|
|
3968
|
+
}function useScroll(refs, arg) {
|
|
3969
|
+
if (typeof arg === 'function') {
|
|
3970
|
+
return useRecognizer(ScrollGesture, refs, arg);
|
|
3971
|
+
}
|
|
3972
|
+
return useScrollProgress(refs, arg);
|
|
3973
|
+
}class WheelGesture extends Gesture {
|
|
3974
|
+
constructor() {
|
|
3975
|
+
super(...arguments);
|
|
3976
|
+
this.attachedEls = new Set();
|
|
3977
|
+
this.movement = { x: 0, y: 0 };
|
|
3978
|
+
this.offset = { x: 0, y: 0 };
|
|
3979
|
+
this.velocity = { x: 0, y: 0 };
|
|
3980
|
+
this.lastTime = 0;
|
|
3981
|
+
}
|
|
3982
|
+
attach(elements) {
|
|
3983
|
+
const els = Array.isArray(elements) ? elements : [elements];
|
|
3984
|
+
const wheel = this.onWheel.bind(this);
|
|
3985
|
+
els.forEach((el) => {
|
|
3986
|
+
this.attachedEls.add(el);
|
|
3987
|
+
el.addEventListener('wheel', wheel, { passive: false });
|
|
3988
|
+
});
|
|
3989
|
+
return () => {
|
|
3990
|
+
els.forEach((el) => {
|
|
3991
|
+
el.removeEventListener('wheel', wheel);
|
|
3992
|
+
this.attachedEls.delete(el);
|
|
3993
|
+
});
|
|
3994
|
+
if (this.endTimeout != null) {
|
|
3995
|
+
clearTimeout(this.endTimeout);
|
|
3996
|
+
this.endTimeout = undefined;
|
|
3997
|
+
}
|
|
3998
|
+
};
|
|
3999
|
+
}
|
|
4000
|
+
cancel() { }
|
|
4001
|
+
onWheel(e) {
|
|
4002
|
+
e.preventDefault();
|
|
4003
|
+
const now = e.timeStamp;
|
|
4004
|
+
const dt = Math.max((now - this.lastTime) / 1000, 1e-6);
|
|
4005
|
+
this.lastTime = now;
|
|
4006
|
+
const dx = e.deltaX;
|
|
4007
|
+
const dy = e.deltaY;
|
|
4008
|
+
this.movement = { x: dx, y: dy };
|
|
4009
|
+
this.offset.x += dx;
|
|
4010
|
+
this.offset.y += dy;
|
|
4011
|
+
const rawX = dx / dt / 1000;
|
|
4012
|
+
const rawY = dy / dt / 1000;
|
|
4013
|
+
this.velocity = {
|
|
4014
|
+
x: clamp(rawX, -Gesture.VELOCITY_LIMIT, Gesture.VELOCITY_LIMIT),
|
|
4015
|
+
y: clamp(rawY, -Gesture.VELOCITY_LIMIT, Gesture.VELOCITY_LIMIT),
|
|
4016
|
+
};
|
|
4017
|
+
this.emitChange({
|
|
4018
|
+
movement: { ...this.movement },
|
|
4019
|
+
offset: { ...this.offset },
|
|
4020
|
+
velocity: { ...this.velocity },
|
|
4021
|
+
event: e,
|
|
4022
|
+
cancel: () => {
|
|
4023
|
+
if (this.endTimeout != null)
|
|
4024
|
+
clearTimeout(this.endTimeout);
|
|
4025
|
+
},
|
|
4026
|
+
});
|
|
4027
|
+
if (this.endTimeout != null)
|
|
4028
|
+
clearTimeout(this.endTimeout);
|
|
4029
|
+
this.endTimeout = window.setTimeout(() => {
|
|
4030
|
+
this.emitEnd({
|
|
4031
|
+
movement: { ...this.movement },
|
|
4032
|
+
offset: { ...this.offset },
|
|
4033
|
+
velocity: { ...this.velocity },
|
|
4034
|
+
event: e,
|
|
4035
|
+
cancel: () => { },
|
|
4036
|
+
});
|
|
4037
|
+
}, 150);
|
|
4038
|
+
}
|
|
4039
|
+
}function useWheel(refs, onWheel) {
|
|
4040
|
+
return useRecognizer(WheelGesture, refs, onWheel);
|
|
4041
|
+
}export{AnimateValue,Easing,Presence,PresenceContext,animate,bounceIn,bounceOut,clamp,combine,decay,delay,exitFade,exitScale,exitSlideDown,exitSlideUp,fadeIn,fadeInDown,fadeInLeft,fadeInRight,fadeInUp,fadeOut,flipX,flipY,hoverGlow,hoverLift,hoverScale,isAnimateValue,loop,makeAnimated,move,parallel,pressDown,pressScale,recipes,rotateIn,rotateOut,rubberClamp,scaleDown,scaleFadeIn,scaleFadeOut,scaleIn,scaleOut,scaleUp,sequence,slideFadeIn,slideFadeOut,slideInDown,slideInLeft,slideInRight,slideInUp,slideOutDown,slideOutLeft,slideOutRight,slideOutUp,snapTo,spin,spring,timing,to,useDrag,useInView,useIsPresent,useMove,useOutsideClick,usePresence,useRecognizer,useScroll,useScrollProgress,useValue,useWheel,withDecay,withDelay,withLoop,withSequence,withSpring,withStagger,withTiming,zoomIn,zoomOut};
|