framer-motion.dev.js 523 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385
  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) :
  3. typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) :
  4. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Motion = {}, global.React));
  5. })(this, (function (exports, React$1) { 'use strict';
  6. function _interopNamespaceDefault(e) {
  7. var n = Object.create(null);
  8. if (e) {
  9. Object.keys(e).forEach(function (k) {
  10. if (k !== 'default') {
  11. var d = Object.getOwnPropertyDescriptor(e, k);
  12. Object.defineProperty(n, k, d.get ? d : {
  13. enumerable: true,
  14. get: function () { return e[k]; }
  15. });
  16. }
  17. });
  18. }
  19. n.default = e;
  20. return Object.freeze(n);
  21. }
  22. var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React$1);
  23. // source: react/cjs/react-jsx-runtime.production.min.js
  24. /**
  25. * @license React
  26. * react-jsx-runtime.production.min.js
  27. *
  28. * Copyright (c) Facebook, Inc. and its affiliates.
  29. *
  30. * This source code is licensed under the MIT license found in the
  31. * LICENSE file in the root directory of this source tree.
  32. */
  33. var f = React,
  34. k = Symbol.for("react.element"),
  35. l = Symbol.for("react.fragment"),
  36. m$1 = Object.prototype.hasOwnProperty,
  37. n = f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,
  38. p = { key: !0, ref: !0, __self: !0, __source: !0 };
  39. function q(c, a, g) {
  40. var b,
  41. d = {},
  42. e = null,
  43. h = null;
  44. void 0 !== g && (e = "" + g);
  45. void 0 !== a.key && (e = "" + a.key);
  46. void 0 !== a.ref && (h = a.ref);
  47. for (b in a) m$1.call(a, b) && !p.hasOwnProperty(b) && (d[b] = a[b]);
  48. if (c && c.defaultProps)
  49. for (b in ((a = c.defaultProps), a)) void 0 === d[b] && (d[b] = a[b]);
  50. return { $$typeof: k, type: c, key: e, ref: h, props: d, _owner: n.current }
  51. }
  52. const Fragment = l;
  53. const jsx = q;
  54. const jsxs = q;
  55. const LayoutGroupContext = React$1.createContext({});
  56. /**
  57. * Creates a constant value over the lifecycle of a component.
  58. *
  59. * Even if `useMemo` is provided an empty array as its final argument, it doesn't offer
  60. * a guarantee that it won't re-run for performance reasons later on. By using `useConstant`
  61. * you can ensure that initialisers don't execute twice or more.
  62. */
  63. function useConstant(init) {
  64. const ref = React$1.useRef(null);
  65. if (ref.current === null) {
  66. ref.current = init();
  67. }
  68. return ref.current;
  69. }
  70. const isBrowser = typeof window !== "undefined";
  71. const useIsomorphicLayoutEffect = isBrowser ? React$1.useLayoutEffect : React$1.useEffect;
  72. /**
  73. * @public
  74. */
  75. const PresenceContext =
  76. /* @__PURE__ */ React$1.createContext(null);
  77. /**
  78. * @public
  79. */
  80. const MotionConfigContext = React$1.createContext({
  81. transformPagePoint: (p) => p,
  82. isStatic: false,
  83. reducedMotion: "never",
  84. });
  85. /**
  86. * Measurement functionality has to be within a separate component
  87. * to leverage snapshot lifecycle.
  88. */
  89. class PopChildMeasure extends React__namespace.Component {
  90. getSnapshotBeforeUpdate(prevProps) {
  91. const element = this.props.childRef.current;
  92. if (element && prevProps.isPresent && !this.props.isPresent) {
  93. const parent = element.offsetParent;
  94. const parentWidth = parent instanceof HTMLElement ? parent.offsetWidth || 0 : 0;
  95. const size = this.props.sizeRef.current;
  96. size.height = element.offsetHeight || 0;
  97. size.width = element.offsetWidth || 0;
  98. size.top = element.offsetTop;
  99. size.left = element.offsetLeft;
  100. size.right = parentWidth - size.width - size.left;
  101. }
  102. return null;
  103. }
  104. /**
  105. * Required with getSnapshotBeforeUpdate to stop React complaining.
  106. */
  107. componentDidUpdate() { }
  108. render() {
  109. return this.props.children;
  110. }
  111. }
  112. function PopChild({ children, isPresent, anchorX }) {
  113. const id = React$1.useId();
  114. const ref = React$1.useRef(null);
  115. const size = React$1.useRef({
  116. width: 0,
  117. height: 0,
  118. top: 0,
  119. left: 0,
  120. right: 0,
  121. });
  122. const { nonce } = React$1.useContext(MotionConfigContext);
  123. /**
  124. * We create and inject a style block so we can apply this explicit
  125. * sizing in a non-destructive manner by just deleting the style block.
  126. *
  127. * We can't apply size via render as the measurement happens
  128. * in getSnapshotBeforeUpdate (post-render), likewise if we apply the
  129. * styles directly on the DOM node, we might be overwriting
  130. * styles set via the style prop.
  131. */
  132. React$1.useInsertionEffect(() => {
  133. const { width, height, top, left, right } = size.current;
  134. if (isPresent || !ref.current || !width || !height)
  135. return;
  136. const x = anchorX === "left" ? `left: ${left}` : `right: ${right}`;
  137. ref.current.dataset.motionPopId = id;
  138. const style = document.createElement("style");
  139. if (nonce)
  140. style.nonce = nonce;
  141. document.head.appendChild(style);
  142. if (style.sheet) {
  143. style.sheet.insertRule(`
  144. [data-motion-pop-id="${id}"] {
  145. position: absolute !important;
  146. width: ${width}px !important;
  147. height: ${height}px !important;
  148. ${x}px !important;
  149. top: ${top}px !important;
  150. }
  151. `);
  152. }
  153. return () => {
  154. document.head.removeChild(style);
  155. };
  156. }, [isPresent]);
  157. return (jsx(PopChildMeasure, { isPresent: isPresent, childRef: ref, sizeRef: size, children: React__namespace.cloneElement(children, { ref }) }));
  158. }
  159. const PresenceChild = ({ children, initial, isPresent, onExitComplete, custom, presenceAffectsLayout, mode, anchorX, }) => {
  160. const presenceChildren = useConstant(newChildrenMap);
  161. const id = React$1.useId();
  162. const memoizedOnExitComplete = React$1.useCallback((childId) => {
  163. presenceChildren.set(childId, true);
  164. for (const isComplete of presenceChildren.values()) {
  165. if (!isComplete)
  166. return; // can stop searching when any is incomplete
  167. }
  168. onExitComplete && onExitComplete();
  169. }, [presenceChildren, onExitComplete]);
  170. const context = React$1.useMemo(() => ({
  171. id,
  172. initial,
  173. isPresent,
  174. custom,
  175. onExitComplete: memoizedOnExitComplete,
  176. register: (childId) => {
  177. presenceChildren.set(childId, false);
  178. return () => presenceChildren.delete(childId);
  179. },
  180. }),
  181. /**
  182. * If the presence of a child affects the layout of the components around it,
  183. * we want to make a new context value to ensure they get re-rendered
  184. * so they can detect that layout change.
  185. */
  186. presenceAffectsLayout
  187. ? [Math.random(), memoizedOnExitComplete]
  188. : [isPresent, memoizedOnExitComplete]);
  189. React$1.useMemo(() => {
  190. presenceChildren.forEach((_, key) => presenceChildren.set(key, false));
  191. }, [isPresent]);
  192. /**
  193. * If there's no `motion` components to fire exit animations, we want to remove this
  194. * component immediately.
  195. */
  196. React__namespace.useEffect(() => {
  197. !isPresent &&
  198. !presenceChildren.size &&
  199. onExitComplete &&
  200. onExitComplete();
  201. }, [isPresent]);
  202. if (mode === "popLayout") {
  203. children = (jsx(PopChild, { isPresent: isPresent, anchorX: anchorX, children: children }));
  204. }
  205. return (jsx(PresenceContext.Provider, { value: context, children: children }));
  206. };
  207. function newChildrenMap() {
  208. return new Map();
  209. }
  210. /**
  211. * When a component is the child of `AnimatePresence`, it can use `usePresence`
  212. * to access information about whether it's still present in the React tree.
  213. *
  214. * ```jsx
  215. * import { usePresence } from "framer-motion"
  216. *
  217. * export const Component = () => {
  218. * const [isPresent, safeToRemove] = usePresence()
  219. *
  220. * useEffect(() => {
  221. * !isPresent && setTimeout(safeToRemove, 1000)
  222. * }, [isPresent])
  223. *
  224. * return <div />
  225. * }
  226. * ```
  227. *
  228. * If `isPresent` is `false`, it means that a component has been removed the tree, but
  229. * `AnimatePresence` won't really remove it until `safeToRemove` has been called.
  230. *
  231. * @public
  232. */
  233. function usePresence(subscribe = true) {
  234. const context = React$1.useContext(PresenceContext);
  235. if (context === null)
  236. return [true, null];
  237. const { isPresent, onExitComplete, register } = context;
  238. // It's safe to call the following hooks conditionally (after an early return) because the context will always
  239. // either be null or non-null for the lifespan of the component.
  240. const id = React$1.useId();
  241. React$1.useEffect(() => {
  242. if (subscribe) {
  243. return register(id);
  244. }
  245. }, [subscribe]);
  246. const safeToRemove = React$1.useCallback(() => subscribe && onExitComplete && onExitComplete(id), [id, onExitComplete, subscribe]);
  247. return !isPresent && onExitComplete ? [false, safeToRemove] : [true];
  248. }
  249. /**
  250. * Similar to `usePresence`, except `useIsPresent` simply returns whether or not the component is present.
  251. * There is no `safeToRemove` function.
  252. *
  253. * ```jsx
  254. * import { useIsPresent } from "framer-motion"
  255. *
  256. * export const Component = () => {
  257. * const isPresent = useIsPresent()
  258. *
  259. * useEffect(() => {
  260. * !isPresent && console.log("I've been removed!")
  261. * }, [isPresent])
  262. *
  263. * return <div />
  264. * }
  265. * ```
  266. *
  267. * @public
  268. */
  269. function useIsPresent() {
  270. return isPresent(React$1.useContext(PresenceContext));
  271. }
  272. function isPresent(context) {
  273. return context === null ? true : context.isPresent;
  274. }
  275. const getChildKey = (child) => child.key || "";
  276. function onlyElements(children) {
  277. const filtered = [];
  278. // We use forEach here instead of map as map mutates the component key by preprending `.$`
  279. React$1.Children.forEach(children, (child) => {
  280. if (React$1.isValidElement(child))
  281. filtered.push(child);
  282. });
  283. return filtered;
  284. }
  285. /**
  286. * `AnimatePresence` enables the animation of components that have been removed from the tree.
  287. *
  288. * When adding/removing more than a single child, every child **must** be given a unique `key` prop.
  289. *
  290. * Any `motion` components that have an `exit` property defined will animate out when removed from
  291. * the tree.
  292. *
  293. * ```jsx
  294. * import { motion, AnimatePresence } from 'framer-motion'
  295. *
  296. * export const Items = ({ items }) => (
  297. * <AnimatePresence>
  298. * {items.map(item => (
  299. * <motion.div
  300. * key={item.id}
  301. * initial={{ opacity: 0 }}
  302. * animate={{ opacity: 1 }}
  303. * exit={{ opacity: 0 }}
  304. * />
  305. * ))}
  306. * </AnimatePresence>
  307. * )
  308. * ```
  309. *
  310. * You can sequence exit animations throughout a tree using variants.
  311. *
  312. * If a child contains multiple `motion` components with `exit` props, it will only unmount the child
  313. * once all `motion` components have finished animating out. Likewise, any components using
  314. * `usePresence` all need to call `safeToRemove`.
  315. *
  316. * @public
  317. */
  318. const AnimatePresence = ({ children, custom, initial = true, onExitComplete, presenceAffectsLayout = true, mode = "sync", propagate = false, anchorX = "left", }) => {
  319. const [isParentPresent, safeToRemove] = usePresence(propagate);
  320. /**
  321. * Filter any children that aren't ReactElements. We can only track components
  322. * between renders with a props.key.
  323. */
  324. const presentChildren = React$1.useMemo(() => onlyElements(children), [children]);
  325. /**
  326. * Track the keys of the currently rendered children. This is used to
  327. * determine which children are exiting.
  328. */
  329. const presentKeys = propagate && !isParentPresent ? [] : presentChildren.map(getChildKey);
  330. /**
  331. * If `initial={false}` we only want to pass this to components in the first render.
  332. */
  333. const isInitialRender = React$1.useRef(true);
  334. /**
  335. * A ref containing the currently present children. When all exit animations
  336. * are complete, we use this to re-render the component with the latest children
  337. * *committed* rather than the latest children *rendered*.
  338. */
  339. const pendingPresentChildren = React$1.useRef(presentChildren);
  340. /**
  341. * Track which exiting children have finished animating out.
  342. */
  343. const exitComplete = useConstant(() => new Map());
  344. /**
  345. * Save children to render as React state. To ensure this component is concurrent-safe,
  346. * we check for exiting children via an effect.
  347. */
  348. const [diffedChildren, setDiffedChildren] = React$1.useState(presentChildren);
  349. const [renderedChildren, setRenderedChildren] = React$1.useState(presentChildren);
  350. useIsomorphicLayoutEffect(() => {
  351. isInitialRender.current = false;
  352. pendingPresentChildren.current = presentChildren;
  353. /**
  354. * Update complete status of exiting children.
  355. */
  356. for (let i = 0; i < renderedChildren.length; i++) {
  357. const key = getChildKey(renderedChildren[i]);
  358. if (!presentKeys.includes(key)) {
  359. if (exitComplete.get(key) !== true) {
  360. exitComplete.set(key, false);
  361. }
  362. }
  363. else {
  364. exitComplete.delete(key);
  365. }
  366. }
  367. }, [renderedChildren, presentKeys.length, presentKeys.join("-")]);
  368. const exitingChildren = [];
  369. if (presentChildren !== diffedChildren) {
  370. let nextChildren = [...presentChildren];
  371. /**
  372. * Loop through all the currently rendered components and decide which
  373. * are exiting.
  374. */
  375. for (let i = 0; i < renderedChildren.length; i++) {
  376. const child = renderedChildren[i];
  377. const key = getChildKey(child);
  378. if (!presentKeys.includes(key)) {
  379. nextChildren.splice(i, 0, child);
  380. exitingChildren.push(child);
  381. }
  382. }
  383. /**
  384. * If we're in "wait" mode, and we have exiting children, we want to
  385. * only render these until they've all exited.
  386. */
  387. if (mode === "wait" && exitingChildren.length) {
  388. nextChildren = exitingChildren;
  389. }
  390. setRenderedChildren(onlyElements(nextChildren));
  391. setDiffedChildren(presentChildren);
  392. /**
  393. * Early return to ensure once we've set state with the latest diffed
  394. * children, we can immediately re-render.
  395. */
  396. return null;
  397. }
  398. if (mode === "wait" &&
  399. renderedChildren.length > 1) {
  400. console.warn(`You're attempting to animate multiple children within AnimatePresence, but its mode is set to "wait". This will lead to odd visual behaviour.`);
  401. }
  402. /**
  403. * If we've been provided a forceRender function by the LayoutGroupContext,
  404. * we can use it to force a re-render amongst all surrounding components once
  405. * all components have finished animating out.
  406. */
  407. const { forceRender } = React$1.useContext(LayoutGroupContext);
  408. return (jsx(Fragment, { children: renderedChildren.map((child) => {
  409. const key = getChildKey(child);
  410. const isPresent = propagate && !isParentPresent
  411. ? false
  412. : presentChildren === renderedChildren ||
  413. presentKeys.includes(key);
  414. const onExit = () => {
  415. if (exitComplete.has(key)) {
  416. exitComplete.set(key, true);
  417. }
  418. else {
  419. return;
  420. }
  421. let isEveryExitComplete = true;
  422. exitComplete.forEach((isExitComplete) => {
  423. if (!isExitComplete)
  424. isEveryExitComplete = false;
  425. });
  426. if (isEveryExitComplete) {
  427. forceRender?.();
  428. setRenderedChildren(pendingPresentChildren.current);
  429. propagate && safeToRemove?.();
  430. onExitComplete && onExitComplete();
  431. }
  432. };
  433. return (jsx(PresenceChild, { isPresent: isPresent, initial: !isInitialRender.current || initial
  434. ? undefined
  435. : false, custom: custom, presenceAffectsLayout: presenceAffectsLayout, mode: mode, onExitComplete: isPresent ? undefined : onExit, anchorX: anchorX, children: child }, key));
  436. }) }));
  437. };
  438. /**
  439. * Note: Still used by components generated by old versions of Framer
  440. *
  441. * @deprecated
  442. */
  443. const DeprecatedLayoutGroupContext = React$1.createContext(null);
  444. function addUniqueItem(arr, item) {
  445. if (arr.indexOf(item) === -1)
  446. arr.push(item);
  447. }
  448. function removeItem(arr, item) {
  449. const index = arr.indexOf(item);
  450. if (index > -1)
  451. arr.splice(index, 1);
  452. }
  453. // Adapted from array-move
  454. function moveItem([...arr], fromIndex, toIndex) {
  455. const startIndex = fromIndex < 0 ? arr.length + fromIndex : fromIndex;
  456. if (startIndex >= 0 && startIndex < arr.length) {
  457. const endIndex = toIndex < 0 ? arr.length + toIndex : toIndex;
  458. const [item] = arr.splice(fromIndex, 1);
  459. arr.splice(endIndex, 0, item);
  460. }
  461. return arr;
  462. }
  463. let warning = () => { };
  464. exports.invariant = () => { };
  465. {
  466. warning = (check, message) => {
  467. if (!check && typeof console !== "undefined") {
  468. console.warn(message);
  469. }
  470. };
  471. exports.invariant = (check, message) => {
  472. if (!check) {
  473. throw new Error(message);
  474. }
  475. };
  476. }
  477. const MotionGlobalConfig = {
  478. skipAnimations: false,
  479. useManualTiming: false,
  480. };
  481. /*#__NO_SIDE_EFFECTS__*/
  482. function memo(callback) {
  483. let result;
  484. return () => {
  485. if (result === undefined)
  486. result = callback();
  487. return result;
  488. };
  489. }
  490. /*#__NO_SIDE_EFFECTS__*/
  491. const noop = (any) => any;
  492. /*
  493. Progress within given range
  494. Given a lower limit and an upper limit, we return the progress
  495. (expressed as a number 0-1) represented by the given value, and
  496. limit that progress to within 0-1.
  497. @param [number]: Lower limit
  498. @param [number]: Upper limit
  499. @param [number]: Value to find progress within given range
  500. @return [number]: Progress of value within range as expressed 0-1
  501. */
  502. /*#__NO_SIDE_EFFECTS__*/
  503. const progress = (from, to, value) => {
  504. const toFromDifference = to - from;
  505. return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
  506. };
  507. class SubscriptionManager {
  508. constructor() {
  509. this.subscriptions = [];
  510. }
  511. add(handler) {
  512. addUniqueItem(this.subscriptions, handler);
  513. return () => removeItem(this.subscriptions, handler);
  514. }
  515. notify(a, b, c) {
  516. const numSubscriptions = this.subscriptions.length;
  517. if (!numSubscriptions)
  518. return;
  519. if (numSubscriptions === 1) {
  520. /**
  521. * If there's only a single handler we can just call it without invoking a loop.
  522. */
  523. this.subscriptions[0](a, b, c);
  524. }
  525. else {
  526. for (let i = 0; i < numSubscriptions; i++) {
  527. /**
  528. * Check whether the handler exists before firing as it's possible
  529. * the subscriptions were modified during this loop running.
  530. */
  531. const handler = this.subscriptions[i];
  532. handler && handler(a, b, c);
  533. }
  534. }
  535. }
  536. getSize() {
  537. return this.subscriptions.length;
  538. }
  539. clear() {
  540. this.subscriptions.length = 0;
  541. }
  542. }
  543. /**
  544. * Converts seconds to milliseconds
  545. *
  546. * @param seconds - Time in seconds.
  547. * @return milliseconds - Converted time in milliseconds.
  548. */
  549. /*#__NO_SIDE_EFFECTS__*/
  550. const secondsToMilliseconds = (seconds) => seconds * 1000;
  551. /*#__NO_SIDE_EFFECTS__*/
  552. const millisecondsToSeconds = (milliseconds) => milliseconds / 1000;
  553. /*
  554. Convert velocity into velocity per second
  555. @param [number]: Unit per frame
  556. @param [number]: Frame duration in ms
  557. */
  558. function velocityPerSecond(velocity, frameDuration) {
  559. return frameDuration ? velocity * (1000 / frameDuration) : 0;
  560. }
  561. const warned = new Set();
  562. function warnOnce(condition, message, element) {
  563. if (condition || warned.has(message))
  564. return;
  565. console.warn(message);
  566. if (element)
  567. console.warn(element);
  568. warned.add(message);
  569. }
  570. const supportsScrollTimeline = /* @__PURE__ */ memo(() => window.ScrollTimeline !== undefined);
  571. class GroupAnimation {
  572. constructor(animations) {
  573. // Bound to accomodate common `return animation.stop` pattern
  574. this.stop = () => this.runAll("stop");
  575. this.animations = animations.filter(Boolean);
  576. }
  577. get finished() {
  578. return Promise.all(this.animations.map((animation) => animation.finished));
  579. }
  580. /**
  581. * TODO: Filter out cancelled or stopped animations before returning
  582. */
  583. getAll(propName) {
  584. return this.animations[0][propName];
  585. }
  586. setAll(propName, newValue) {
  587. for (let i = 0; i < this.animations.length; i++) {
  588. this.animations[i][propName] = newValue;
  589. }
  590. }
  591. attachTimeline(timeline, fallback) {
  592. const subscriptions = this.animations.map((animation) => {
  593. if (supportsScrollTimeline() && animation.attachTimeline) {
  594. return animation.attachTimeline(timeline);
  595. }
  596. else if (typeof fallback === "function") {
  597. return fallback(animation);
  598. }
  599. });
  600. return () => {
  601. subscriptions.forEach((cancel, i) => {
  602. cancel && cancel();
  603. this.animations[i].stop();
  604. });
  605. };
  606. }
  607. get time() {
  608. return this.getAll("time");
  609. }
  610. set time(time) {
  611. this.setAll("time", time);
  612. }
  613. get speed() {
  614. return this.getAll("speed");
  615. }
  616. set speed(speed) {
  617. this.setAll("speed", speed);
  618. }
  619. get startTime() {
  620. return this.getAll("startTime");
  621. }
  622. get duration() {
  623. let max = 0;
  624. for (let i = 0; i < this.animations.length; i++) {
  625. max = Math.max(max, this.animations[i].duration);
  626. }
  627. return max;
  628. }
  629. runAll(methodName) {
  630. this.animations.forEach((controls) => controls[methodName]());
  631. }
  632. flatten() {
  633. this.runAll("flatten");
  634. }
  635. play() {
  636. this.runAll("play");
  637. }
  638. pause() {
  639. this.runAll("pause");
  640. }
  641. cancel() {
  642. this.runAll("cancel");
  643. }
  644. complete() {
  645. this.runAll("complete");
  646. }
  647. }
  648. class GroupAnimationWithThen extends GroupAnimation {
  649. then(onResolve, _onReject) {
  650. return this.finished.finally(onResolve).then(() => { });
  651. }
  652. }
  653. const isCSSVar = (name) => name.startsWith("--");
  654. const style = {
  655. set: (element, name, value) => {
  656. isCSSVar(name)
  657. ? element.style.setProperty(name, value)
  658. : (element.style[name] = value);
  659. },
  660. get: (element, name) => {
  661. return isCSSVar(name)
  662. ? element.style.getPropertyValue(name)
  663. : element.style[name];
  664. },
  665. };
  666. const isNotNull$1 = (value) => value !== null;
  667. function getFinalKeyframe$1(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
  668. const resolvedKeyframes = keyframes.filter(isNotNull$1);
  669. const index = repeat && repeatType !== "loop" && repeat % 2 === 1
  670. ? 0
  671. : resolvedKeyframes.length - 1;
  672. return !index || finalKeyframe === undefined
  673. ? resolvedKeyframes[index]
  674. : finalKeyframe;
  675. }
  676. const supportsPartialKeyframes = /*@__PURE__*/ memo(() => {
  677. try {
  678. document.createElement("div").animate({ opacity: [1] });
  679. }
  680. catch (e) {
  681. return false;
  682. }
  683. return true;
  684. });
  685. const pxValues = new Set([
  686. // Border props
  687. "borderWidth",
  688. "borderTopWidth",
  689. "borderRightWidth",
  690. "borderBottomWidth",
  691. "borderLeftWidth",
  692. "borderRadius",
  693. "radius",
  694. "borderTopLeftRadius",
  695. "borderTopRightRadius",
  696. "borderBottomRightRadius",
  697. "borderBottomLeftRadius",
  698. // Positioning props
  699. "width",
  700. "maxWidth",
  701. "height",
  702. "maxHeight",
  703. "top",
  704. "right",
  705. "bottom",
  706. "left",
  707. // Spacing props
  708. "padding",
  709. "paddingTop",
  710. "paddingRight",
  711. "paddingBottom",
  712. "paddingLeft",
  713. "margin",
  714. "marginTop",
  715. "marginRight",
  716. "marginBottom",
  717. "marginLeft",
  718. // Misc
  719. "backgroundPositionX",
  720. "backgroundPositionY",
  721. ]);
  722. function hydrateKeyframes(element, name, keyframes, pseudoElement) {
  723. if (!Array.isArray(keyframes)) {
  724. keyframes = [keyframes];
  725. }
  726. for (let i = 0; i < keyframes.length; i++) {
  727. if (keyframes[i] === null) {
  728. keyframes[i] =
  729. i === 0 && !pseudoElement
  730. ? style.get(element, name)
  731. : keyframes[i - 1];
  732. }
  733. if (typeof keyframes[i] === "number" && pxValues.has(name)) {
  734. keyframes[i] = keyframes[i] + "px";
  735. }
  736. }
  737. if (!pseudoElement && !supportsPartialKeyframes() && keyframes.length < 2) {
  738. keyframes.unshift(style.get(element, name));
  739. }
  740. return keyframes;
  741. }
  742. const statsBuffer = {
  743. value: null,
  744. addProjectionMetrics: null,
  745. };
  746. const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number";
  747. /**
  748. * Add the ability for test suites to manually set support flags
  749. * to better test more environments.
  750. */
  751. const supportsFlags = {};
  752. function memoSupports(callback, supportsFlag) {
  753. const memoized = memo(callback);
  754. return () => supportsFlags[supportsFlag] ?? memoized();
  755. }
  756. const supportsLinearEasing = /*@__PURE__*/ memoSupports(() => {
  757. try {
  758. document
  759. .createElement("div")
  760. .animate({ opacity: 0 }, { easing: "linear(0, 1)" });
  761. }
  762. catch (e) {
  763. return false;
  764. }
  765. return true;
  766. }, "linearEasing");
  767. const generateLinearEasing = (easing, duration, // as milliseconds
  768. resolution = 10 // as milliseconds
  769. ) => {
  770. let points = "";
  771. const numPoints = Math.max(Math.round(duration / resolution), 2);
  772. for (let i = 0; i < numPoints; i++) {
  773. points += easing(i / (numPoints - 1)) + ", ";
  774. }
  775. return `linear(${points.substring(0, points.length - 2)})`;
  776. };
  777. const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
  778. const supportedWaapiEasing = {
  779. linear: "linear",
  780. ease: "ease",
  781. easeIn: "ease-in",
  782. easeOut: "ease-out",
  783. easeInOut: "ease-in-out",
  784. circIn: /*@__PURE__*/ cubicBezierAsString([0, 0.65, 0.55, 1]),
  785. circOut: /*@__PURE__*/ cubicBezierAsString([0.55, 0, 1, 0.45]),
  786. backIn: /*@__PURE__*/ cubicBezierAsString([0.31, 0.01, 0.66, -0.59]),
  787. backOut: /*@__PURE__*/ cubicBezierAsString([0.33, 1.53, 0.69, 0.99]),
  788. };
  789. function mapEasingToNativeEasing(easing, duration) {
  790. if (!easing) {
  791. return undefined;
  792. }
  793. else if (typeof easing === "function" && supportsLinearEasing()) {
  794. return generateLinearEasing(easing, duration);
  795. }
  796. else if (isBezierDefinition(easing)) {
  797. return cubicBezierAsString(easing);
  798. }
  799. else if (Array.isArray(easing)) {
  800. return easing.map((segmentEasing) => mapEasingToNativeEasing(segmentEasing, duration) ||
  801. supportedWaapiEasing.easeOut);
  802. }
  803. else {
  804. return supportedWaapiEasing[easing];
  805. }
  806. }
  807. function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duration = 300, repeat = 0, repeatType = "loop", ease = "easeInOut", times, } = {}, pseudoElement = undefined) {
  808. const keyframeOptions = {
  809. [valueName]: keyframes,
  810. };
  811. if (times)
  812. keyframeOptions.offset = times;
  813. const easing = mapEasingToNativeEasing(ease, duration);
  814. /**
  815. * If this is an easing array, apply to keyframes, not animation as a whole
  816. */
  817. if (Array.isArray(easing))
  818. keyframeOptions.easing = easing;
  819. const animation = element.animate(keyframeOptions, {
  820. delay,
  821. duration,
  822. easing: !Array.isArray(easing) ? easing : "linear",
  823. fill: "both",
  824. iterations: repeat + 1,
  825. direction: repeatType === "reverse" ? "alternate" : "normal",
  826. pseudoElement,
  827. });
  828. return animation;
  829. }
  830. function isGenerator(type) {
  831. return typeof type === "function" && "applyToOptions" in type;
  832. }
  833. function applyGeneratorOptions({ type, ...options }) {
  834. if (isGenerator(type)) {
  835. return type.applyToOptions(options);
  836. }
  837. else {
  838. options.duration ?? (options.duration = 300);
  839. options.ease ?? (options.ease = "easeOut");
  840. }
  841. return options;
  842. }
  843. const animationMaps = new WeakMap();
  844. const animationMapKey = (name, pseudoElement) => `${name}:${pseudoElement}`;
  845. function getAnimationMap(element) {
  846. const map = animationMaps.get(element) || new Map();
  847. animationMaps.set(element, map);
  848. return map;
  849. }
  850. /**
  851. * NativeAnimation implements AnimationPlaybackControls for the browser's Web Animations API.
  852. */
  853. class NativeAnimation {
  854. constructor(options) {
  855. /**
  856. * If we already have an animation, we don't need to instantiate one
  857. * and can just use this as a controls interface.
  858. */
  859. if ("animation" in options) {
  860. this.animation = options.animation;
  861. return;
  862. }
  863. const { element, name, keyframes: unresolvedKeyframes, pseudoElement, allowFlatten = false, } = options;
  864. let { transition } = options;
  865. this.isPseudoElement = Boolean(pseudoElement);
  866. this.allowFlatten = allowFlatten;
  867. /**
  868. * Stop any existing animations on the element before reading existing keyframes.
  869. *
  870. * TODO: Check for VisualElement before using animation state. This is a fallback
  871. * for mini animate(). Do this when implementing NativeAnimationExtended.
  872. */
  873. const animationMap = getAnimationMap(element);
  874. const key = animationMapKey(name, pseudoElement || "");
  875. const currentAnimation = animationMap.get(key);
  876. currentAnimation && currentAnimation.stop();
  877. /**
  878. * TODO: If these keyframes aren't correctly hydrated then we want to throw
  879. * run an instant animation.
  880. */
  881. const keyframes = hydrateKeyframes(element, name, unresolvedKeyframes, pseudoElement);
  882. exports.invariant(typeof transition.type !== "string", `animateMini doesn't support "type" as a string. Did you mean to import { spring } from "motion"?`);
  883. transition = applyGeneratorOptions(transition);
  884. this.animation = startWaapiAnimation(element, name, keyframes, transition, pseudoElement);
  885. if (transition.autoplay === false) {
  886. this.animation.pause();
  887. }
  888. this.removeAnimation = () => animationMap.delete(key);
  889. this.animation.onfinish = () => {
  890. if (!pseudoElement) {
  891. style.set(element, name, getFinalKeyframe$1(keyframes, transition));
  892. this.cancel();
  893. }
  894. };
  895. /**
  896. * TODO: Check for VisualElement before using animation state.
  897. */
  898. animationMap.set(key, this);
  899. }
  900. play() {
  901. this.animation.play();
  902. }
  903. pause() {
  904. this.animation.pause();
  905. }
  906. complete() {
  907. this.animation.finish();
  908. }
  909. cancel() {
  910. try {
  911. this.animation.cancel();
  912. }
  913. catch (e) { }
  914. this.removeAnimation();
  915. }
  916. stop() {
  917. const { state } = this;
  918. if (state === "idle" || state === "finished") {
  919. return;
  920. }
  921. this.commitStyles();
  922. this.cancel();
  923. }
  924. /**
  925. * WAAPI doesn't natively have any interruption capabilities.
  926. *
  927. * In this method, we commit styles back to the DOM before cancelling
  928. * the animation.
  929. *
  930. * This is designed to be overridden by NativeAnimationExtended, which
  931. * will create a renderless JS animation and sample it twice to calculate
  932. * its current value, "previous" value, and therefore allow
  933. * Motion to also correctly calculate velocity for any subsequent animation
  934. * while deferring the commit until the next animation frame.
  935. */
  936. commitStyles() {
  937. if (!this.isPseudoElement) {
  938. this.animation.commitStyles?.();
  939. }
  940. }
  941. get duration() {
  942. const duration = this.animation.effect?.getComputedTiming().duration || 0;
  943. return millisecondsToSeconds(Number(duration));
  944. }
  945. get time() {
  946. return millisecondsToSeconds(Number(this.animation.currentTime) || 0);
  947. }
  948. set time(newTime) {
  949. this.animation.currentTime = secondsToMilliseconds(newTime);
  950. }
  951. /**
  952. * The playback speed of the animation.
  953. * 1 = normal speed, 2 = double speed, 0.5 = half speed.
  954. */
  955. get speed() {
  956. return this.animation.playbackRate;
  957. }
  958. set speed(newSpeed) {
  959. this.animation.playbackRate = newSpeed;
  960. }
  961. get state() {
  962. return this.animation.playState;
  963. }
  964. get startTime() {
  965. return Number(this.animation.startTime);
  966. }
  967. get finished() {
  968. return this.animation.finished;
  969. }
  970. flatten() {
  971. if (this.allowFlatten) {
  972. this.animation.effect?.updateTiming({ easing: "linear" });
  973. }
  974. }
  975. /**
  976. * Attaches a timeline to the animation, for instance the `ScrollTimeline`.
  977. */
  978. attachTimeline(timeline) {
  979. this.animation.timeline = timeline;
  980. this.animation.onfinish = null;
  981. return noop;
  982. }
  983. /**
  984. * Allows the animation to be awaited.
  985. *
  986. * @deprecated Use `finished` instead.
  987. */
  988. then(onResolve, onReject) {
  989. return this.finished.then(onResolve).catch(onReject);
  990. }
  991. }
  992. function getValueTransition$1(transition, key) {
  993. return (transition?.[key] ??
  994. transition?.["default"] ??
  995. transition);
  996. }
  997. /**
  998. * Implement a practical max duration for keyframe generation
  999. * to prevent infinite loops
  1000. */
  1001. const maxGeneratorDuration = 20000;
  1002. function calcGeneratorDuration(generator) {
  1003. let duration = 0;
  1004. const timeStep = 50;
  1005. let state = generator.next(duration);
  1006. while (!state.done && duration < maxGeneratorDuration) {
  1007. duration += timeStep;
  1008. state = generator.next(duration);
  1009. }
  1010. return duration >= maxGeneratorDuration ? Infinity : duration;
  1011. }
  1012. /**
  1013. * Create a progress => progress easing function from a generator.
  1014. */
  1015. function createGeneratorEasing(options, scale = 100, createGenerator) {
  1016. const generator = createGenerator({ ...options, keyframes: [0, scale] });
  1017. const duration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
  1018. return {
  1019. type: "keyframes",
  1020. ease: (progress) => {
  1021. return generator.next(duration * progress).value / scale;
  1022. },
  1023. duration: millisecondsToSeconds(duration),
  1024. };
  1025. }
  1026. function isWaapiSupportedEasing(easing) {
  1027. return Boolean((typeof easing === "function" && supportsLinearEasing()) ||
  1028. !easing ||
  1029. (typeof easing === "string" &&
  1030. (easing in supportedWaapiEasing || supportsLinearEasing())) ||
  1031. isBezierDefinition(easing) ||
  1032. (Array.isArray(easing) && easing.every(isWaapiSupportedEasing)));
  1033. }
  1034. function attachTimeline(animation, timeline) {
  1035. animation.timeline = timeline;
  1036. animation.onfinish = null;
  1037. }
  1038. const stepsOrder = [
  1039. "read", // Read
  1040. "resolveKeyframes", // Write/Read/Write/Read
  1041. "update", // Compute
  1042. "preRender", // Compute
  1043. "render", // Write
  1044. "postRender", // Compute
  1045. ];
  1046. function createRenderStep(runNextFrame, stepName) {
  1047. /**
  1048. * We create and reuse two queues, one to queue jobs for the current frame
  1049. * and one for the next. We reuse to avoid triggering GC after x frames.
  1050. */
  1051. let thisFrame = new Set();
  1052. let nextFrame = new Set();
  1053. /**
  1054. * Track whether we're currently processing jobs in this step. This way
  1055. * we can decide whether to schedule new jobs for this frame or next.
  1056. */
  1057. let isProcessing = false;
  1058. let flushNextFrame = false;
  1059. /**
  1060. * A set of processes which were marked keepAlive when scheduled.
  1061. */
  1062. const toKeepAlive = new WeakSet();
  1063. let latestFrameData = {
  1064. delta: 0.0,
  1065. timestamp: 0.0,
  1066. isProcessing: false,
  1067. };
  1068. let numCalls = 0;
  1069. function triggerCallback(callback) {
  1070. if (toKeepAlive.has(callback)) {
  1071. step.schedule(callback);
  1072. runNextFrame();
  1073. }
  1074. numCalls++;
  1075. callback(latestFrameData);
  1076. }
  1077. const step = {
  1078. /**
  1079. * Schedule a process to run on the next frame.
  1080. */
  1081. schedule: (callback, keepAlive = false, immediate = false) => {
  1082. const addToCurrentFrame = immediate && isProcessing;
  1083. const queue = addToCurrentFrame ? thisFrame : nextFrame;
  1084. if (keepAlive)
  1085. toKeepAlive.add(callback);
  1086. if (!queue.has(callback))
  1087. queue.add(callback);
  1088. return callback;
  1089. },
  1090. /**
  1091. * Cancel the provided callback from running on the next frame.
  1092. */
  1093. cancel: (callback) => {
  1094. nextFrame.delete(callback);
  1095. toKeepAlive.delete(callback);
  1096. },
  1097. /**
  1098. * Execute all schedule callbacks.
  1099. */
  1100. process: (frameData) => {
  1101. latestFrameData = frameData;
  1102. /**
  1103. * If we're already processing we've probably been triggered by a flushSync
  1104. * inside an existing process. Instead of executing, mark flushNextFrame
  1105. * as true and ensure we flush the following frame at the end of this one.
  1106. */
  1107. if (isProcessing) {
  1108. flushNextFrame = true;
  1109. return;
  1110. }
  1111. isProcessing = true;
  1112. [thisFrame, nextFrame] = [nextFrame, thisFrame];
  1113. // Execute this frame
  1114. thisFrame.forEach(triggerCallback);
  1115. /**
  1116. * If we're recording stats then
  1117. */
  1118. if (stepName && statsBuffer.value) {
  1119. statsBuffer.value.frameloop[stepName].push(numCalls);
  1120. }
  1121. numCalls = 0;
  1122. // Clear the frame so no callbacks remain. This is to avoid
  1123. // memory leaks should this render step not run for a while.
  1124. thisFrame.clear();
  1125. isProcessing = false;
  1126. if (flushNextFrame) {
  1127. flushNextFrame = false;
  1128. step.process(frameData);
  1129. }
  1130. },
  1131. };
  1132. return step;
  1133. }
  1134. const maxElapsed$1 = 40;
  1135. function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
  1136. let runNextFrame = false;
  1137. let useDefaultElapsed = true;
  1138. const state = {
  1139. delta: 0.0,
  1140. timestamp: 0.0,
  1141. isProcessing: false,
  1142. };
  1143. const flagRunNextFrame = () => (runNextFrame = true);
  1144. const steps = stepsOrder.reduce((acc, key) => {
  1145. acc[key] = createRenderStep(flagRunNextFrame, allowKeepAlive ? key : undefined);
  1146. return acc;
  1147. }, {});
  1148. const { read, resolveKeyframes, update, preRender, render, postRender } = steps;
  1149. const processBatch = () => {
  1150. const timestamp = MotionGlobalConfig.useManualTiming
  1151. ? state.timestamp
  1152. : performance.now();
  1153. runNextFrame = false;
  1154. if (!MotionGlobalConfig.useManualTiming) {
  1155. state.delta = useDefaultElapsed
  1156. ? 1000 / 60
  1157. : Math.max(Math.min(timestamp - state.timestamp, maxElapsed$1), 1);
  1158. }
  1159. state.timestamp = timestamp;
  1160. state.isProcessing = true;
  1161. // Unrolled render loop for better per-frame performance
  1162. read.process(state);
  1163. resolveKeyframes.process(state);
  1164. update.process(state);
  1165. preRender.process(state);
  1166. render.process(state);
  1167. postRender.process(state);
  1168. state.isProcessing = false;
  1169. if (runNextFrame && allowKeepAlive) {
  1170. useDefaultElapsed = false;
  1171. scheduleNextBatch(processBatch);
  1172. }
  1173. };
  1174. const wake = () => {
  1175. runNextFrame = true;
  1176. useDefaultElapsed = true;
  1177. if (!state.isProcessing) {
  1178. scheduleNextBatch(processBatch);
  1179. }
  1180. };
  1181. const schedule = stepsOrder.reduce((acc, key) => {
  1182. const step = steps[key];
  1183. acc[key] = (process, keepAlive = false, immediate = false) => {
  1184. if (!runNextFrame)
  1185. wake();
  1186. return step.schedule(process, keepAlive, immediate);
  1187. };
  1188. return acc;
  1189. }, {});
  1190. const cancel = (process) => {
  1191. for (let i = 0; i < stepsOrder.length; i++) {
  1192. steps[stepsOrder[i]].cancel(process);
  1193. }
  1194. };
  1195. return { schedule, cancel, state, steps };
  1196. }
  1197. const { schedule: microtask, cancel: cancelMicrotask } =
  1198. /* @__PURE__ */ createRenderBatcher(queueMicrotask, false);
  1199. const { schedule: frame, cancel: cancelFrame, state: frameData, steps: frameSteps, } = /* @__PURE__ */ createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop, true);
  1200. let now;
  1201. function clearTime() {
  1202. now = undefined;
  1203. }
  1204. /**
  1205. * An eventloop-synchronous alternative to performance.now().
  1206. *
  1207. * Ensures that time measurements remain consistent within a synchronous context.
  1208. * Usually calling performance.now() twice within the same synchronous context
  1209. * will return different values which isn't useful for animations when we're usually
  1210. * trying to sync animations to the same frame.
  1211. */
  1212. const time = {
  1213. now: () => {
  1214. if (now === undefined) {
  1215. time.set(frameData.isProcessing || MotionGlobalConfig.useManualTiming
  1216. ? frameData.timestamp
  1217. : performance.now());
  1218. }
  1219. return now;
  1220. },
  1221. set: (newTime) => {
  1222. now = newTime;
  1223. queueMicrotask(clearTime);
  1224. },
  1225. };
  1226. const isDragging = {
  1227. x: false,
  1228. y: false,
  1229. };
  1230. function isDragActive() {
  1231. return isDragging.x || isDragging.y;
  1232. }
  1233. function setDragLock(axis) {
  1234. if (axis === "x" || axis === "y") {
  1235. if (isDragging[axis]) {
  1236. return null;
  1237. }
  1238. else {
  1239. isDragging[axis] = true;
  1240. return () => {
  1241. isDragging[axis] = false;
  1242. };
  1243. }
  1244. }
  1245. else {
  1246. if (isDragging.x || isDragging.y) {
  1247. return null;
  1248. }
  1249. else {
  1250. isDragging.x = isDragging.y = true;
  1251. return () => {
  1252. isDragging.x = isDragging.y = false;
  1253. };
  1254. }
  1255. }
  1256. }
  1257. function resolveElements(elementOrSelector, scope, selectorCache) {
  1258. if (elementOrSelector instanceof EventTarget) {
  1259. return [elementOrSelector];
  1260. }
  1261. else if (typeof elementOrSelector === "string") {
  1262. let root = document;
  1263. if (scope) {
  1264. root = scope.current;
  1265. }
  1266. const elements = selectorCache?.[elementOrSelector] ??
  1267. root.querySelectorAll(elementOrSelector);
  1268. return elements ? Array.from(elements) : [];
  1269. }
  1270. return Array.from(elementOrSelector);
  1271. }
  1272. function setupGesture(elementOrSelector, options) {
  1273. const elements = resolveElements(elementOrSelector);
  1274. const gestureAbortController = new AbortController();
  1275. const eventOptions = {
  1276. passive: true,
  1277. ...options,
  1278. signal: gestureAbortController.signal,
  1279. };
  1280. const cancel = () => gestureAbortController.abort();
  1281. return [elements, eventOptions, cancel];
  1282. }
  1283. function isValidHover(event) {
  1284. return !(event.pointerType === "touch" || isDragActive());
  1285. }
  1286. /**
  1287. * Create a hover gesture. hover() is different to .addEventListener("pointerenter")
  1288. * in that it has an easier syntax, filters out polyfilled touch events, interoperates
  1289. * with drag gestures, and automatically removes the "pointerennd" event listener when the hover ends.
  1290. *
  1291. * @public
  1292. */
  1293. function hover(elementOrSelector, onHoverStart, options = {}) {
  1294. const [elements, eventOptions, cancel] = setupGesture(elementOrSelector, options);
  1295. const onPointerEnter = (enterEvent) => {
  1296. if (!isValidHover(enterEvent))
  1297. return;
  1298. const { target } = enterEvent;
  1299. const onHoverEnd = onHoverStart(target, enterEvent);
  1300. if (typeof onHoverEnd !== "function" || !target)
  1301. return;
  1302. const onPointerLeave = (leaveEvent) => {
  1303. if (!isValidHover(leaveEvent))
  1304. return;
  1305. onHoverEnd(leaveEvent);
  1306. target.removeEventListener("pointerleave", onPointerLeave);
  1307. };
  1308. target.addEventListener("pointerleave", onPointerLeave, eventOptions);
  1309. };
  1310. elements.forEach((element) => {
  1311. element.addEventListener("pointerenter", onPointerEnter, eventOptions);
  1312. });
  1313. return cancel;
  1314. }
  1315. /**
  1316. * Recursively traverse up the tree to check whether the provided child node
  1317. * is the parent or a descendant of it.
  1318. *
  1319. * @param parent - Element to find
  1320. * @param child - Element to test against parent
  1321. */
  1322. const isNodeOrChild = (parent, child) => {
  1323. if (!child) {
  1324. return false;
  1325. }
  1326. else if (parent === child) {
  1327. return true;
  1328. }
  1329. else {
  1330. return isNodeOrChild(parent, child.parentElement);
  1331. }
  1332. };
  1333. const isPrimaryPointer = (event) => {
  1334. if (event.pointerType === "mouse") {
  1335. return typeof event.button !== "number" || event.button <= 0;
  1336. }
  1337. else {
  1338. /**
  1339. * isPrimary is true for all mice buttons, whereas every touch point
  1340. * is regarded as its own input. So subsequent concurrent touch points
  1341. * will be false.
  1342. *
  1343. * Specifically match against false here as incomplete versions of
  1344. * PointerEvents in very old browser might have it set as undefined.
  1345. */
  1346. return event.isPrimary !== false;
  1347. }
  1348. };
  1349. const focusableElements = new Set([
  1350. "BUTTON",
  1351. "INPUT",
  1352. "SELECT",
  1353. "TEXTAREA",
  1354. "A",
  1355. ]);
  1356. function isElementKeyboardAccessible(element) {
  1357. return (focusableElements.has(element.tagName) ||
  1358. element.tabIndex !== -1);
  1359. }
  1360. const isPressing = new WeakSet();
  1361. /**
  1362. * Filter out events that are not "Enter" keys.
  1363. */
  1364. function filterEvents(callback) {
  1365. return (event) => {
  1366. if (event.key !== "Enter")
  1367. return;
  1368. callback(event);
  1369. };
  1370. }
  1371. function firePointerEvent(target, type) {
  1372. target.dispatchEvent(new PointerEvent("pointer" + type, { isPrimary: true, bubbles: true }));
  1373. }
  1374. const enableKeyboardPress = (focusEvent, eventOptions) => {
  1375. const element = focusEvent.currentTarget;
  1376. if (!element)
  1377. return;
  1378. const handleKeydown = filterEvents(() => {
  1379. if (isPressing.has(element))
  1380. return;
  1381. firePointerEvent(element, "down");
  1382. const handleKeyup = filterEvents(() => {
  1383. firePointerEvent(element, "up");
  1384. });
  1385. const handleBlur = () => firePointerEvent(element, "cancel");
  1386. element.addEventListener("keyup", handleKeyup, eventOptions);
  1387. element.addEventListener("blur", handleBlur, eventOptions);
  1388. });
  1389. element.addEventListener("keydown", handleKeydown, eventOptions);
  1390. /**
  1391. * Add an event listener that fires on blur to remove the keydown events.
  1392. */
  1393. element.addEventListener("blur", () => element.removeEventListener("keydown", handleKeydown), eventOptions);
  1394. };
  1395. /**
  1396. * Filter out events that are not primary pointer events, or are triggering
  1397. * while a Motion gesture is active.
  1398. */
  1399. function isValidPressEvent(event) {
  1400. return isPrimaryPointer(event) && !isDragActive();
  1401. }
  1402. /**
  1403. * Create a press gesture.
  1404. *
  1405. * Press is different to `"pointerdown"`, `"pointerup"` in that it
  1406. * automatically filters out secondary pointer events like right
  1407. * click and multitouch.
  1408. *
  1409. * It also adds accessibility support for keyboards, where
  1410. * an element with a press gesture will receive focus and
  1411. * trigger on Enter `"keydown"` and `"keyup"` events.
  1412. *
  1413. * This is different to a browser's `"click"` event, which does
  1414. * respond to keyboards but only for the `"click"` itself, rather
  1415. * than the press start and end/cancel. The element also needs
  1416. * to be focusable for this to work, whereas a press gesture will
  1417. * make an element focusable by default.
  1418. *
  1419. * @public
  1420. */
  1421. function press(targetOrSelector, onPressStart, options = {}) {
  1422. const [targets, eventOptions, cancelEvents] = setupGesture(targetOrSelector, options);
  1423. const startPress = (startEvent) => {
  1424. const target = startEvent.currentTarget;
  1425. if (!isValidPressEvent(startEvent) || isPressing.has(target))
  1426. return;
  1427. isPressing.add(target);
  1428. const onPressEnd = onPressStart(target, startEvent);
  1429. const onPointerEnd = (endEvent, success) => {
  1430. window.removeEventListener("pointerup", onPointerUp);
  1431. window.removeEventListener("pointercancel", onPointerCancel);
  1432. if (!isValidPressEvent(endEvent) || !isPressing.has(target)) {
  1433. return;
  1434. }
  1435. isPressing.delete(target);
  1436. if (typeof onPressEnd === "function") {
  1437. onPressEnd(endEvent, { success });
  1438. }
  1439. };
  1440. const onPointerUp = (upEvent) => {
  1441. onPointerEnd(upEvent, target === window ||
  1442. target === document ||
  1443. options.useGlobalTarget ||
  1444. isNodeOrChild(target, upEvent.target));
  1445. };
  1446. const onPointerCancel = (cancelEvent) => {
  1447. onPointerEnd(cancelEvent, false);
  1448. };
  1449. window.addEventListener("pointerup", onPointerUp, eventOptions);
  1450. window.addEventListener("pointercancel", onPointerCancel, eventOptions);
  1451. };
  1452. targets.forEach((target) => {
  1453. const pointerDownTarget = options.useGlobalTarget ? window : target;
  1454. pointerDownTarget.addEventListener("pointerdown", startPress, eventOptions);
  1455. if (target instanceof HTMLElement) {
  1456. target.addEventListener("focus", (event) => enableKeyboardPress(event, eventOptions));
  1457. if (!isElementKeyboardAccessible(target) &&
  1458. !target.hasAttribute("tabindex")) {
  1459. target.tabIndex = 0;
  1460. }
  1461. }
  1462. });
  1463. return cancelEvents;
  1464. }
  1465. /**
  1466. * Maximum time between the value of two frames, beyond which we
  1467. * assume the velocity has since been 0.
  1468. */
  1469. const MAX_VELOCITY_DELTA = 30;
  1470. const isFloat = (value) => {
  1471. return !isNaN(parseFloat(value));
  1472. };
  1473. const collectMotionValues = {
  1474. current: undefined,
  1475. };
  1476. /**
  1477. * `MotionValue` is used to track the state and velocity of motion values.
  1478. *
  1479. * @public
  1480. */
  1481. class MotionValue {
  1482. /**
  1483. * @param init - The initiating value
  1484. * @param config - Optional configuration options
  1485. *
  1486. * - `transformer`: A function to transform incoming values with.
  1487. */
  1488. constructor(init, options = {}) {
  1489. /**
  1490. * This will be replaced by the build step with the latest version number.
  1491. * When MotionValues are provided to motion components, warn if versions are mixed.
  1492. */
  1493. this.version = "12.7.3";
  1494. /**
  1495. * Tracks whether this value can output a velocity. Currently this is only true
  1496. * if the value is numerical, but we might be able to widen the scope here and support
  1497. * other value types.
  1498. *
  1499. * @internal
  1500. */
  1501. this.canTrackVelocity = null;
  1502. /**
  1503. * An object containing a SubscriptionManager for each active event.
  1504. */
  1505. this.events = {};
  1506. this.updateAndNotify = (v, render = true) => {
  1507. const currentTime = time.now();
  1508. /**
  1509. * If we're updating the value during another frame or eventloop
  1510. * than the previous frame, then the we set the previous frame value
  1511. * to current.
  1512. */
  1513. if (this.updatedAt !== currentTime) {
  1514. this.setPrevFrameValue();
  1515. }
  1516. this.prev = this.current;
  1517. this.setCurrent(v);
  1518. // Update update subscribers
  1519. if (this.current !== this.prev && this.events.change) {
  1520. this.events.change.notify(this.current);
  1521. }
  1522. // Update render subscribers
  1523. if (render && this.events.renderRequest) {
  1524. this.events.renderRequest.notify(this.current);
  1525. }
  1526. };
  1527. this.hasAnimated = false;
  1528. this.setCurrent(init);
  1529. this.owner = options.owner;
  1530. }
  1531. setCurrent(current) {
  1532. this.current = current;
  1533. this.updatedAt = time.now();
  1534. if (this.canTrackVelocity === null && current !== undefined) {
  1535. this.canTrackVelocity = isFloat(this.current);
  1536. }
  1537. }
  1538. setPrevFrameValue(prevFrameValue = this.current) {
  1539. this.prevFrameValue = prevFrameValue;
  1540. this.prevUpdatedAt = this.updatedAt;
  1541. }
  1542. /**
  1543. * Adds a function that will be notified when the `MotionValue` is updated.
  1544. *
  1545. * It returns a function that, when called, will cancel the subscription.
  1546. *
  1547. * When calling `onChange` inside a React component, it should be wrapped with the
  1548. * `useEffect` hook. As it returns an unsubscribe function, this should be returned
  1549. * from the `useEffect` function to ensure you don't add duplicate subscribers..
  1550. *
  1551. * ```jsx
  1552. * export const MyComponent = () => {
  1553. * const x = useMotionValue(0)
  1554. * const y = useMotionValue(0)
  1555. * const opacity = useMotionValue(1)
  1556. *
  1557. * useEffect(() => {
  1558. * function updateOpacity() {
  1559. * const maxXY = Math.max(x.get(), y.get())
  1560. * const newOpacity = transform(maxXY, [0, 100], [1, 0])
  1561. * opacity.set(newOpacity)
  1562. * }
  1563. *
  1564. * const unsubscribeX = x.on("change", updateOpacity)
  1565. * const unsubscribeY = y.on("change", updateOpacity)
  1566. *
  1567. * return () => {
  1568. * unsubscribeX()
  1569. * unsubscribeY()
  1570. * }
  1571. * }, [])
  1572. *
  1573. * return <motion.div style={{ x }} />
  1574. * }
  1575. * ```
  1576. *
  1577. * @param subscriber - A function that receives the latest value.
  1578. * @returns A function that, when called, will cancel this subscription.
  1579. *
  1580. * @deprecated
  1581. */
  1582. onChange(subscription) {
  1583. {
  1584. warnOnce(false, `value.onChange(callback) is deprecated. Switch to value.on("change", callback).`);
  1585. }
  1586. return this.on("change", subscription);
  1587. }
  1588. on(eventName, callback) {
  1589. if (!this.events[eventName]) {
  1590. this.events[eventName] = new SubscriptionManager();
  1591. }
  1592. const unsubscribe = this.events[eventName].add(callback);
  1593. if (eventName === "change") {
  1594. return () => {
  1595. unsubscribe();
  1596. /**
  1597. * If we have no more change listeners by the start
  1598. * of the next frame, stop active animations.
  1599. */
  1600. frame.read(() => {
  1601. if (!this.events.change.getSize()) {
  1602. this.stop();
  1603. }
  1604. });
  1605. };
  1606. }
  1607. return unsubscribe;
  1608. }
  1609. clearListeners() {
  1610. for (const eventManagers in this.events) {
  1611. this.events[eventManagers].clear();
  1612. }
  1613. }
  1614. /**
  1615. * Attaches a passive effect to the `MotionValue`.
  1616. */
  1617. attach(passiveEffect, stopPassiveEffect) {
  1618. this.passiveEffect = passiveEffect;
  1619. this.stopPassiveEffect = stopPassiveEffect;
  1620. }
  1621. /**
  1622. * Sets the state of the `MotionValue`.
  1623. *
  1624. * @remarks
  1625. *
  1626. * ```jsx
  1627. * const x = useMotionValue(0)
  1628. * x.set(10)
  1629. * ```
  1630. *
  1631. * @param latest - Latest value to set.
  1632. * @param render - Whether to notify render subscribers. Defaults to `true`
  1633. *
  1634. * @public
  1635. */
  1636. set(v, render = true) {
  1637. if (!render || !this.passiveEffect) {
  1638. this.updateAndNotify(v, render);
  1639. }
  1640. else {
  1641. this.passiveEffect(v, this.updateAndNotify);
  1642. }
  1643. }
  1644. setWithVelocity(prev, current, delta) {
  1645. this.set(current);
  1646. this.prev = undefined;
  1647. this.prevFrameValue = prev;
  1648. this.prevUpdatedAt = this.updatedAt - delta;
  1649. }
  1650. /**
  1651. * Set the state of the `MotionValue`, stopping any active animations,
  1652. * effects, and resets velocity to `0`.
  1653. */
  1654. jump(v, endAnimation = true) {
  1655. this.updateAndNotify(v);
  1656. this.prev = v;
  1657. this.prevUpdatedAt = this.prevFrameValue = undefined;
  1658. endAnimation && this.stop();
  1659. if (this.stopPassiveEffect)
  1660. this.stopPassiveEffect();
  1661. }
  1662. /**
  1663. * Returns the latest state of `MotionValue`
  1664. *
  1665. * @returns - The latest state of `MotionValue`
  1666. *
  1667. * @public
  1668. */
  1669. get() {
  1670. if (collectMotionValues.current) {
  1671. collectMotionValues.current.push(this);
  1672. }
  1673. return this.current;
  1674. }
  1675. /**
  1676. * @public
  1677. */
  1678. getPrevious() {
  1679. return this.prev;
  1680. }
  1681. /**
  1682. * Returns the latest velocity of `MotionValue`
  1683. *
  1684. * @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical.
  1685. *
  1686. * @public
  1687. */
  1688. getVelocity() {
  1689. const currentTime = time.now();
  1690. if (!this.canTrackVelocity ||
  1691. this.prevFrameValue === undefined ||
  1692. currentTime - this.updatedAt > MAX_VELOCITY_DELTA) {
  1693. return 0;
  1694. }
  1695. const delta = Math.min(this.updatedAt - this.prevUpdatedAt, MAX_VELOCITY_DELTA);
  1696. // Casts because of parseFloat's poor typing
  1697. return velocityPerSecond(parseFloat(this.current) -
  1698. parseFloat(this.prevFrameValue), delta);
  1699. }
  1700. /**
  1701. * Registers a new animation to control this `MotionValue`. Only one
  1702. * animation can drive a `MotionValue` at one time.
  1703. *
  1704. * ```jsx
  1705. * value.start()
  1706. * ```
  1707. *
  1708. * @param animation - A function that starts the provided animation
  1709. */
  1710. start(startAnimation) {
  1711. this.stop();
  1712. return new Promise((resolve) => {
  1713. this.hasAnimated = true;
  1714. this.animation = startAnimation(resolve);
  1715. if (this.events.animationStart) {
  1716. this.events.animationStart.notify();
  1717. }
  1718. }).then(() => {
  1719. if (this.events.animationComplete) {
  1720. this.events.animationComplete.notify();
  1721. }
  1722. this.clearAnimation();
  1723. });
  1724. }
  1725. /**
  1726. * Stop the currently active animation.
  1727. *
  1728. * @public
  1729. */
  1730. stop() {
  1731. if (this.animation) {
  1732. this.animation.stop();
  1733. if (this.events.animationCancel) {
  1734. this.events.animationCancel.notify();
  1735. }
  1736. }
  1737. this.clearAnimation();
  1738. }
  1739. /**
  1740. * Returns `true` if this value is currently animating.
  1741. *
  1742. * @public
  1743. */
  1744. isAnimating() {
  1745. return !!this.animation;
  1746. }
  1747. clearAnimation() {
  1748. delete this.animation;
  1749. }
  1750. /**
  1751. * Destroy and clean up subscribers to this `MotionValue`.
  1752. *
  1753. * The `MotionValue` hooks like `useMotionValue` and `useTransform` automatically
  1754. * handle the lifecycle of the returned `MotionValue`, so this method is only necessary if you've manually
  1755. * created a `MotionValue` via the `motionValue` function.
  1756. *
  1757. * @public
  1758. */
  1759. destroy() {
  1760. this.clearListeners();
  1761. this.stop();
  1762. if (this.stopPassiveEffect) {
  1763. this.stopPassiveEffect();
  1764. }
  1765. }
  1766. }
  1767. function motionValue(init, options) {
  1768. return new MotionValue(init, options);
  1769. }
  1770. /**
  1771. * @deprecated
  1772. *
  1773. * Import as `frame` instead.
  1774. */
  1775. const sync = frame;
  1776. /**
  1777. * @deprecated
  1778. *
  1779. * Use cancelFrame(callback) instead.
  1780. */
  1781. const cancelSync = stepsOrder.reduce((acc, key) => {
  1782. acc[key] = (process) => cancelFrame(process);
  1783. return acc;
  1784. }, {});
  1785. /*
  1786. Value in range from progress
  1787. Given a lower limit and an upper limit, we return the value within
  1788. that range as expressed by progress (usually a number from 0 to 1)
  1789. So progress = 0.5 would change
  1790. from -------- to
  1791. to
  1792. from ---- to
  1793. E.g. from = 10, to = 20, progress = 0.5 => 15
  1794. @param [number]: Lower limit of range
  1795. @param [number]: Upper limit of range
  1796. @param [number]: The progress between lower and upper limits expressed 0-1
  1797. @return [number]: Value as calculated from progress within range (not limited within range)
  1798. */
  1799. const mixNumber$1 = (from, to, progress) => {
  1800. return from + (to - from) * progress;
  1801. };
  1802. const SCALE_PRECISION = 0.0001;
  1803. const SCALE_MIN = 1 - SCALE_PRECISION;
  1804. const SCALE_MAX = 1 + SCALE_PRECISION;
  1805. const TRANSLATE_PRECISION = 0.01;
  1806. const TRANSLATE_MIN = 0 - TRANSLATE_PRECISION;
  1807. const TRANSLATE_MAX = 0 + TRANSLATE_PRECISION;
  1808. function calcLength(axis) {
  1809. return axis.max - axis.min;
  1810. }
  1811. function isNear(value, target, maxDistance) {
  1812. return Math.abs(value - target) <= maxDistance;
  1813. }
  1814. function calcAxisDelta(delta, source, target, origin = 0.5) {
  1815. delta.origin = origin;
  1816. delta.originPoint = mixNumber$1(source.min, source.max, delta.origin);
  1817. delta.scale = calcLength(target) / calcLength(source);
  1818. delta.translate =
  1819. mixNumber$1(target.min, target.max, delta.origin) - delta.originPoint;
  1820. if ((delta.scale >= SCALE_MIN && delta.scale <= SCALE_MAX) ||
  1821. isNaN(delta.scale)) {
  1822. delta.scale = 1.0;
  1823. }
  1824. if ((delta.translate >= TRANSLATE_MIN &&
  1825. delta.translate <= TRANSLATE_MAX) ||
  1826. isNaN(delta.translate)) {
  1827. delta.translate = 0.0;
  1828. }
  1829. }
  1830. function calcBoxDelta(delta, source, target, origin) {
  1831. calcAxisDelta(delta.x, source.x, target.x, origin ? origin.originX : undefined);
  1832. calcAxisDelta(delta.y, source.y, target.y, origin ? origin.originY : undefined);
  1833. }
  1834. function calcRelativeAxis(target, relative, parent) {
  1835. target.min = parent.min + relative.min;
  1836. target.max = target.min + calcLength(relative);
  1837. }
  1838. function calcRelativeBox(target, relative, parent) {
  1839. calcRelativeAxis(target.x, relative.x, parent.x);
  1840. calcRelativeAxis(target.y, relative.y, parent.y);
  1841. }
  1842. function calcRelativeAxisPosition(target, layout, parent) {
  1843. target.min = layout.min - parent.min;
  1844. target.max = target.min + calcLength(layout);
  1845. }
  1846. function calcRelativePosition(target, layout, parent) {
  1847. calcRelativeAxisPosition(target.x, layout.x, parent.x);
  1848. calcRelativeAxisPosition(target.y, layout.y, parent.y);
  1849. }
  1850. const notify = (node) => !node.isLayoutDirty && node.willUpdate(false);
  1851. function nodeGroup() {
  1852. const nodes = new Set();
  1853. const subscriptions = new WeakMap();
  1854. const dirtyAll = () => nodes.forEach(notify);
  1855. return {
  1856. add: (node) => {
  1857. nodes.add(node);
  1858. subscriptions.set(node, node.addEventListener("willUpdate", dirtyAll));
  1859. },
  1860. remove: (node) => {
  1861. nodes.delete(node);
  1862. const unsubscribe = subscriptions.get(node);
  1863. if (unsubscribe) {
  1864. unsubscribe();
  1865. subscriptions.delete(node);
  1866. }
  1867. dirtyAll();
  1868. },
  1869. dirty: dirtyAll,
  1870. };
  1871. }
  1872. const isMotionValue = (value) => Boolean(value && value.getVelocity);
  1873. const instantAnimationState = {
  1874. current: false,
  1875. };
  1876. /*
  1877. Bezier function generator
  1878. This has been modified from Gaëtan Renaudeau's BezierEasing
  1879. https://github.com/gre/bezier-easing/blob/master/src/index.js
  1880. https://github.com/gre/bezier-easing/blob/master/LICENSE
  1881. I've removed the newtonRaphsonIterate algo because in benchmarking it
  1882. wasn't noticiably faster than binarySubdivision, indeed removing it
  1883. usually improved times, depending on the curve.
  1884. I also removed the lookup table, as for the added bundle size and loop we're
  1885. only cutting ~4 or so subdivision iterations. I bumped the max iterations up
  1886. to 12 to compensate and this still tended to be faster for no perceivable
  1887. loss in accuracy.
  1888. Usage
  1889. const easeOut = cubicBezier(.17,.67,.83,.67);
  1890. const x = easeOut(0.5); // returns 0.627...
  1891. */
  1892. // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
  1893. const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) *
  1894. t;
  1895. const subdivisionPrecision = 0.0000001;
  1896. const subdivisionMaxIterations = 12;
  1897. function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) {
  1898. let currentX;
  1899. let currentT;
  1900. let i = 0;
  1901. do {
  1902. currentT = lowerBound + (upperBound - lowerBound) / 2.0;
  1903. currentX = calcBezier(currentT, mX1, mX2) - x;
  1904. if (currentX > 0.0) {
  1905. upperBound = currentT;
  1906. }
  1907. else {
  1908. lowerBound = currentT;
  1909. }
  1910. } while (Math.abs(currentX) > subdivisionPrecision &&
  1911. ++i < subdivisionMaxIterations);
  1912. return currentT;
  1913. }
  1914. function cubicBezier(mX1, mY1, mX2, mY2) {
  1915. // If this is a linear gradient, return linear easing
  1916. if (mX1 === mY1 && mX2 === mY2)
  1917. return noop;
  1918. const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2);
  1919. // If animation is at start/end, return t without easing
  1920. return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
  1921. }
  1922. // Accepts an easing function and returns a new one that outputs mirrored values for
  1923. // the second half of the animation. Turns easeIn into easeInOut.
  1924. const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;
  1925. // Accepts an easing function and returns a new one that outputs reversed values.
  1926. // Turns easeIn into easeOut.
  1927. const reverseEasing = (easing) => (p) => 1 - easing(1 - p);
  1928. const backOut = /*@__PURE__*/ cubicBezier(0.33, 1.53, 0.69, 0.99);
  1929. const backIn = /*@__PURE__*/ reverseEasing(backOut);
  1930. const backInOut = /*@__PURE__*/ mirrorEasing(backIn);
  1931. const anticipate = (p) => (p *= 2) < 1 ? 0.5 * backIn(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
  1932. const circIn = (p) => 1 - Math.sin(Math.acos(p));
  1933. const circOut = reverseEasing(circIn);
  1934. const circInOut = mirrorEasing(circIn);
  1935. /**
  1936. * Check if the value is a zero value string like "0px" or "0%"
  1937. */
  1938. const isZeroValueString = (v) => /^0[^.\s]+$/u.test(v);
  1939. function isNone(value) {
  1940. if (typeof value === "number") {
  1941. return value === 0;
  1942. }
  1943. else if (value !== null) {
  1944. return value === "none" || value === "0" || isZeroValueString(value);
  1945. }
  1946. else {
  1947. return true;
  1948. }
  1949. }
  1950. /**
  1951. * Generate a list of every possible transform key.
  1952. */
  1953. const transformPropOrder = [
  1954. "transformPerspective",
  1955. "x",
  1956. "y",
  1957. "z",
  1958. "translateX",
  1959. "translateY",
  1960. "translateZ",
  1961. "scale",
  1962. "scaleX",
  1963. "scaleY",
  1964. "rotate",
  1965. "rotateX",
  1966. "rotateY",
  1967. "rotateZ",
  1968. "skew",
  1969. "skewX",
  1970. "skewY",
  1971. ];
  1972. /**
  1973. * A quick lookup for transform props.
  1974. */
  1975. const transformProps = new Set(transformPropOrder);
  1976. const positionalKeys = new Set([
  1977. "width",
  1978. "height",
  1979. "top",
  1980. "left",
  1981. "right",
  1982. "bottom",
  1983. ...transformPropOrder,
  1984. ]);
  1985. const clamp = (min, max, v) => {
  1986. if (v > max)
  1987. return max;
  1988. if (v < min)
  1989. return min;
  1990. return v;
  1991. };
  1992. const number = {
  1993. test: (v) => typeof v === "number",
  1994. parse: parseFloat,
  1995. transform: (v) => v,
  1996. };
  1997. const alpha = {
  1998. ...number,
  1999. transform: (v) => clamp(0, 1, v),
  2000. };
  2001. const scale = {
  2002. ...number,
  2003. default: 1,
  2004. };
  2005. // If this number is a decimal, make it just five decimal places
  2006. // to avoid exponents
  2007. const sanitize = (v) => Math.round(v * 100000) / 100000;
  2008. const floatRegex = /-?(?:\d+(?:\.\d+)?|\.\d+)/gu;
  2009. function isNullish(v) {
  2010. return v == null;
  2011. }
  2012. const singleColorRegex = /^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu;
  2013. /**
  2014. * Returns true if the provided string is a color, ie rgba(0,0,0,0) or #000,
  2015. * but false if a number or multiple colors
  2016. */
  2017. const isColorString = (type, testProp) => (v) => {
  2018. return Boolean((typeof v === "string" &&
  2019. singleColorRegex.test(v) &&
  2020. v.startsWith(type)) ||
  2021. (testProp &&
  2022. !isNullish(v) &&
  2023. Object.prototype.hasOwnProperty.call(v, testProp)));
  2024. };
  2025. const splitColor = (aName, bName, cName) => (v) => {
  2026. if (typeof v !== "string")
  2027. return v;
  2028. const [a, b, c, alpha] = v.match(floatRegex);
  2029. return {
  2030. [aName]: parseFloat(a),
  2031. [bName]: parseFloat(b),
  2032. [cName]: parseFloat(c),
  2033. alpha: alpha !== undefined ? parseFloat(alpha) : 1,
  2034. };
  2035. };
  2036. const clampRgbUnit = (v) => clamp(0, 255, v);
  2037. const rgbUnit = {
  2038. ...number,
  2039. transform: (v) => Math.round(clampRgbUnit(v)),
  2040. };
  2041. const rgba = {
  2042. test: /*@__PURE__*/ isColorString("rgb", "red"),
  2043. parse: /*@__PURE__*/ splitColor("red", "green", "blue"),
  2044. transform: ({ red, green, blue, alpha: alpha$1 = 1 }) => "rgba(" +
  2045. rgbUnit.transform(red) +
  2046. ", " +
  2047. rgbUnit.transform(green) +
  2048. ", " +
  2049. rgbUnit.transform(blue) +
  2050. ", " +
  2051. sanitize(alpha.transform(alpha$1)) +
  2052. ")",
  2053. };
  2054. function parseHex(v) {
  2055. let r = "";
  2056. let g = "";
  2057. let b = "";
  2058. let a = "";
  2059. // If we have 6 characters, ie #FF0000
  2060. if (v.length > 5) {
  2061. r = v.substring(1, 3);
  2062. g = v.substring(3, 5);
  2063. b = v.substring(5, 7);
  2064. a = v.substring(7, 9);
  2065. // Or we have 3 characters, ie #F00
  2066. }
  2067. else {
  2068. r = v.substring(1, 2);
  2069. g = v.substring(2, 3);
  2070. b = v.substring(3, 4);
  2071. a = v.substring(4, 5);
  2072. r += r;
  2073. g += g;
  2074. b += b;
  2075. a += a;
  2076. }
  2077. return {
  2078. red: parseInt(r, 16),
  2079. green: parseInt(g, 16),
  2080. blue: parseInt(b, 16),
  2081. alpha: a ? parseInt(a, 16) / 255 : 1,
  2082. };
  2083. }
  2084. const hex = {
  2085. test: /*@__PURE__*/ isColorString("#"),
  2086. parse: parseHex,
  2087. transform: rgba.transform,
  2088. };
  2089. const createUnitType = (unit) => ({
  2090. test: (v) => typeof v === "string" && v.endsWith(unit) && v.split(" ").length === 1,
  2091. parse: parseFloat,
  2092. transform: (v) => `${v}${unit}`,
  2093. });
  2094. const degrees = /*@__PURE__*/ createUnitType("deg");
  2095. const percent = /*@__PURE__*/ createUnitType("%");
  2096. const px = /*@__PURE__*/ createUnitType("px");
  2097. const vh = /*@__PURE__*/ createUnitType("vh");
  2098. const vw = /*@__PURE__*/ createUnitType("vw");
  2099. const progressPercentage = {
  2100. ...percent,
  2101. parse: (v) => percent.parse(v) / 100,
  2102. transform: (v) => percent.transform(v * 100),
  2103. };
  2104. const hsla = {
  2105. test: /*@__PURE__*/ isColorString("hsl", "hue"),
  2106. parse: /*@__PURE__*/ splitColor("hue", "saturation", "lightness"),
  2107. transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => {
  2108. return ("hsla(" +
  2109. Math.round(hue) +
  2110. ", " +
  2111. percent.transform(sanitize(saturation)) +
  2112. ", " +
  2113. percent.transform(sanitize(lightness)) +
  2114. ", " +
  2115. sanitize(alpha.transform(alpha$1)) +
  2116. ")");
  2117. },
  2118. };
  2119. const color = {
  2120. test: (v) => rgba.test(v) || hex.test(v) || hsla.test(v),
  2121. parse: (v) => {
  2122. if (rgba.test(v)) {
  2123. return rgba.parse(v);
  2124. }
  2125. else if (hsla.test(v)) {
  2126. return hsla.parse(v);
  2127. }
  2128. else {
  2129. return hex.parse(v);
  2130. }
  2131. },
  2132. transform: (v) => {
  2133. return typeof v === "string"
  2134. ? v
  2135. : v.hasOwnProperty("red")
  2136. ? rgba.transform(v)
  2137. : hsla.transform(v);
  2138. },
  2139. };
  2140. const colorRegex = /(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu;
  2141. function test(v) {
  2142. return (isNaN(v) &&
  2143. typeof v === "string" &&
  2144. (v.match(floatRegex)?.length || 0) +
  2145. (v.match(colorRegex)?.length || 0) >
  2146. 0);
  2147. }
  2148. const NUMBER_TOKEN = "number";
  2149. const COLOR_TOKEN = "color";
  2150. const VAR_TOKEN = "var";
  2151. const VAR_FUNCTION_TOKEN = "var(";
  2152. const SPLIT_TOKEN = "${}";
  2153. // this regex consists of the `singleCssVariableRegex|rgbHSLValueRegex|digitRegex`
  2154. const complexRegex = /var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu;
  2155. function analyseComplexValue(value) {
  2156. const originalValue = value.toString();
  2157. const values = [];
  2158. const indexes = {
  2159. color: [],
  2160. number: [],
  2161. var: [],
  2162. };
  2163. const types = [];
  2164. let i = 0;
  2165. const tokenised = originalValue.replace(complexRegex, (parsedValue) => {
  2166. if (color.test(parsedValue)) {
  2167. indexes.color.push(i);
  2168. types.push(COLOR_TOKEN);
  2169. values.push(color.parse(parsedValue));
  2170. }
  2171. else if (parsedValue.startsWith(VAR_FUNCTION_TOKEN)) {
  2172. indexes.var.push(i);
  2173. types.push(VAR_TOKEN);
  2174. values.push(parsedValue);
  2175. }
  2176. else {
  2177. indexes.number.push(i);
  2178. types.push(NUMBER_TOKEN);
  2179. values.push(parseFloat(parsedValue));
  2180. }
  2181. ++i;
  2182. return SPLIT_TOKEN;
  2183. });
  2184. const split = tokenised.split(SPLIT_TOKEN);
  2185. return { values, split, indexes, types };
  2186. }
  2187. function parseComplexValue(v) {
  2188. return analyseComplexValue(v).values;
  2189. }
  2190. function createTransformer(source) {
  2191. const { split, types } = analyseComplexValue(source);
  2192. const numSections = split.length;
  2193. return (v) => {
  2194. let output = "";
  2195. for (let i = 0; i < numSections; i++) {
  2196. output += split[i];
  2197. if (v[i] !== undefined) {
  2198. const type = types[i];
  2199. if (type === NUMBER_TOKEN) {
  2200. output += sanitize(v[i]);
  2201. }
  2202. else if (type === COLOR_TOKEN) {
  2203. output += color.transform(v[i]);
  2204. }
  2205. else {
  2206. output += v[i];
  2207. }
  2208. }
  2209. }
  2210. return output;
  2211. };
  2212. }
  2213. const convertNumbersToZero = (v) => typeof v === "number" ? 0 : v;
  2214. function getAnimatableNone$1(v) {
  2215. const parsed = parseComplexValue(v);
  2216. const transformer = createTransformer(v);
  2217. return transformer(parsed.map(convertNumbersToZero));
  2218. }
  2219. const complex = {
  2220. test,
  2221. parse: parseComplexValue,
  2222. createTransformer,
  2223. getAnimatableNone: getAnimatableNone$1,
  2224. };
  2225. /**
  2226. * Properties that should default to 1 or 100%
  2227. */
  2228. const maxDefaults = new Set(["brightness", "contrast", "saturate", "opacity"]);
  2229. function applyDefaultFilter(v) {
  2230. const [name, value] = v.slice(0, -1).split("(");
  2231. if (name === "drop-shadow")
  2232. return v;
  2233. const [number] = value.match(floatRegex) || [];
  2234. if (!number)
  2235. return v;
  2236. const unit = value.replace(number, "");
  2237. let defaultValue = maxDefaults.has(name) ? 1 : 0;
  2238. if (number !== value)
  2239. defaultValue *= 100;
  2240. return name + "(" + defaultValue + unit + ")";
  2241. }
  2242. const functionRegex = /\b([a-z-]*)\(.*?\)/gu;
  2243. const filter = {
  2244. ...complex,
  2245. getAnimatableNone: (v) => {
  2246. const functions = v.match(functionRegex);
  2247. return functions ? functions.map(applyDefaultFilter).join(" ") : v;
  2248. },
  2249. };
  2250. const browserNumberValueTypes = {
  2251. // Border props
  2252. borderWidth: px,
  2253. borderTopWidth: px,
  2254. borderRightWidth: px,
  2255. borderBottomWidth: px,
  2256. borderLeftWidth: px,
  2257. borderRadius: px,
  2258. radius: px,
  2259. borderTopLeftRadius: px,
  2260. borderTopRightRadius: px,
  2261. borderBottomRightRadius: px,
  2262. borderBottomLeftRadius: px,
  2263. // Positioning props
  2264. width: px,
  2265. maxWidth: px,
  2266. height: px,
  2267. maxHeight: px,
  2268. top: px,
  2269. right: px,
  2270. bottom: px,
  2271. left: px,
  2272. // Spacing props
  2273. padding: px,
  2274. paddingTop: px,
  2275. paddingRight: px,
  2276. paddingBottom: px,
  2277. paddingLeft: px,
  2278. margin: px,
  2279. marginTop: px,
  2280. marginRight: px,
  2281. marginBottom: px,
  2282. marginLeft: px,
  2283. // Misc
  2284. backgroundPositionX: px,
  2285. backgroundPositionY: px,
  2286. };
  2287. const transformValueTypes = {
  2288. rotate: degrees,
  2289. rotateX: degrees,
  2290. rotateY: degrees,
  2291. rotateZ: degrees,
  2292. scale,
  2293. scaleX: scale,
  2294. scaleY: scale,
  2295. scaleZ: scale,
  2296. skew: degrees,
  2297. skewX: degrees,
  2298. skewY: degrees,
  2299. distance: px,
  2300. translateX: px,
  2301. translateY: px,
  2302. translateZ: px,
  2303. x: px,
  2304. y: px,
  2305. z: px,
  2306. perspective: px,
  2307. transformPerspective: px,
  2308. opacity: alpha,
  2309. originX: progressPercentage,
  2310. originY: progressPercentage,
  2311. originZ: px,
  2312. };
  2313. const int = {
  2314. ...number,
  2315. transform: Math.round,
  2316. };
  2317. const numberValueTypes = {
  2318. ...browserNumberValueTypes,
  2319. ...transformValueTypes,
  2320. zIndex: int,
  2321. size: px,
  2322. // SVG
  2323. fillOpacity: alpha,
  2324. strokeOpacity: alpha,
  2325. numOctaves: int,
  2326. };
  2327. /**
  2328. * A map of default value types for common values
  2329. */
  2330. const defaultValueTypes = {
  2331. ...numberValueTypes,
  2332. // Color props
  2333. color,
  2334. backgroundColor: color,
  2335. outlineColor: color,
  2336. fill: color,
  2337. stroke: color,
  2338. // Border props
  2339. borderColor: color,
  2340. borderTopColor: color,
  2341. borderRightColor: color,
  2342. borderBottomColor: color,
  2343. borderLeftColor: color,
  2344. filter,
  2345. WebkitFilter: filter,
  2346. };
  2347. /**
  2348. * Gets the default ValueType for the provided value key
  2349. */
  2350. const getDefaultValueType = (key) => defaultValueTypes[key];
  2351. function getAnimatableNone(key, value) {
  2352. let defaultValueType = getDefaultValueType(key);
  2353. if (defaultValueType !== filter)
  2354. defaultValueType = complex;
  2355. // If value is not recognised as animatable, ie "none", create an animatable version origin based on the target
  2356. return defaultValueType.getAnimatableNone
  2357. ? defaultValueType.getAnimatableNone(value)
  2358. : undefined;
  2359. }
  2360. /**
  2361. * If we encounter keyframes like "none" or "0" and we also have keyframes like
  2362. * "#fff" or "200px 200px" we want to find a keyframe to serve as a template for
  2363. * the "none" keyframes. In this case "#fff" or "200px 200px" - then these get turned into
  2364. * zero equivalents, i.e. "#fff0" or "0px 0px".
  2365. */
  2366. const invalidTemplates = new Set(["auto", "none", "0"]);
  2367. function makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name) {
  2368. let i = 0;
  2369. let animatableTemplate = undefined;
  2370. while (i < unresolvedKeyframes.length && !animatableTemplate) {
  2371. const keyframe = unresolvedKeyframes[i];
  2372. if (typeof keyframe === "string" &&
  2373. !invalidTemplates.has(keyframe) &&
  2374. analyseComplexValue(keyframe).values.length) {
  2375. animatableTemplate = unresolvedKeyframes[i];
  2376. }
  2377. i++;
  2378. }
  2379. if (animatableTemplate && name) {
  2380. for (const noneIndex of noneKeyframeIndexes) {
  2381. unresolvedKeyframes[noneIndex] = getAnimatableNone(name, animatableTemplate);
  2382. }
  2383. }
  2384. }
  2385. const radToDeg = (rad) => (rad * 180) / Math.PI;
  2386. const rotate = (v) => {
  2387. const angle = radToDeg(Math.atan2(v[1], v[0]));
  2388. return rebaseAngle(angle);
  2389. };
  2390. const matrix2dParsers = {
  2391. x: 4,
  2392. y: 5,
  2393. translateX: 4,
  2394. translateY: 5,
  2395. scaleX: 0,
  2396. scaleY: 3,
  2397. scale: (v) => (Math.abs(v[0]) + Math.abs(v[3])) / 2,
  2398. rotate,
  2399. rotateZ: rotate,
  2400. skewX: (v) => radToDeg(Math.atan(v[1])),
  2401. skewY: (v) => radToDeg(Math.atan(v[2])),
  2402. skew: (v) => (Math.abs(v[1]) + Math.abs(v[2])) / 2,
  2403. };
  2404. const rebaseAngle = (angle) => {
  2405. angle = angle % 360;
  2406. if (angle < 0)
  2407. angle += 360;
  2408. return angle;
  2409. };
  2410. const rotateZ = rotate;
  2411. const scaleX = (v) => Math.sqrt(v[0] * v[0] + v[1] * v[1]);
  2412. const scaleY = (v) => Math.sqrt(v[4] * v[4] + v[5] * v[5]);
  2413. const matrix3dParsers = {
  2414. x: 12,
  2415. y: 13,
  2416. z: 14,
  2417. translateX: 12,
  2418. translateY: 13,
  2419. translateZ: 14,
  2420. scaleX,
  2421. scaleY,
  2422. scale: (v) => (scaleX(v) + scaleY(v)) / 2,
  2423. rotateX: (v) => rebaseAngle(radToDeg(Math.atan2(v[6], v[5]))),
  2424. rotateY: (v) => rebaseAngle(radToDeg(Math.atan2(-v[2], v[0]))),
  2425. rotateZ,
  2426. rotate: rotateZ,
  2427. skewX: (v) => radToDeg(Math.atan(v[4])),
  2428. skewY: (v) => radToDeg(Math.atan(v[1])),
  2429. skew: (v) => (Math.abs(v[1]) + Math.abs(v[4])) / 2,
  2430. };
  2431. function defaultTransformValue(name) {
  2432. return name.includes("scale") ? 1 : 0;
  2433. }
  2434. function parseValueFromTransform(transform, name) {
  2435. if (!transform || transform === "none") {
  2436. return defaultTransformValue(name);
  2437. }
  2438. const matrix3dMatch = transform.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);
  2439. let parsers;
  2440. let match;
  2441. if (matrix3dMatch) {
  2442. parsers = matrix3dParsers;
  2443. match = matrix3dMatch;
  2444. }
  2445. else {
  2446. const matrix2dMatch = transform.match(/^matrix\(([-\d.e\s,]+)\)$/u);
  2447. parsers = matrix2dParsers;
  2448. match = matrix2dMatch;
  2449. }
  2450. if (!match) {
  2451. return defaultTransformValue(name);
  2452. }
  2453. const valueParser = parsers[name];
  2454. const values = match[1].split(",").map(convertTransformToNumber);
  2455. return typeof valueParser === "function"
  2456. ? valueParser(values)
  2457. : values[valueParser];
  2458. }
  2459. const readTransformValue = (instance, name) => {
  2460. const { transform = "none" } = getComputedStyle(instance);
  2461. return parseValueFromTransform(transform, name);
  2462. };
  2463. function convertTransformToNumber(value) {
  2464. return parseFloat(value.trim());
  2465. }
  2466. const isNumOrPxType = (v) => v === number || v === px;
  2467. const transformKeys = new Set(["x", "y", "z"]);
  2468. const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key));
  2469. function removeNonTranslationalTransform(visualElement) {
  2470. const removedTransforms = [];
  2471. nonTranslationalTransformKeys.forEach((key) => {
  2472. const value = visualElement.getValue(key);
  2473. if (value !== undefined) {
  2474. removedTransforms.push([key, value.get()]);
  2475. value.set(key.startsWith("scale") ? 1 : 0);
  2476. }
  2477. });
  2478. return removedTransforms;
  2479. }
  2480. const positionalValues = {
  2481. // Dimensions
  2482. width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight),
  2483. height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom),
  2484. top: (_bbox, { top }) => parseFloat(top),
  2485. left: (_bbox, { left }) => parseFloat(left),
  2486. bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min),
  2487. right: ({ x }, { left }) => parseFloat(left) + (x.max - x.min),
  2488. // Transform
  2489. x: (_bbox, { transform }) => parseValueFromTransform(transform, "x"),
  2490. y: (_bbox, { transform }) => parseValueFromTransform(transform, "y"),
  2491. };
  2492. // Alias translate longform names
  2493. positionalValues.translateX = positionalValues.x;
  2494. positionalValues.translateY = positionalValues.y;
  2495. const toResolve = new Set();
  2496. let isScheduled = false;
  2497. let anyNeedsMeasurement = false;
  2498. function measureAllKeyframes() {
  2499. if (anyNeedsMeasurement) {
  2500. const resolversToMeasure = Array.from(toResolve).filter((resolver) => resolver.needsMeasurement);
  2501. const elementsToMeasure = new Set(resolversToMeasure.map((resolver) => resolver.element));
  2502. const transformsToRestore = new Map();
  2503. /**
  2504. * Write pass
  2505. * If we're measuring elements we want to remove bounding box-changing transforms.
  2506. */
  2507. elementsToMeasure.forEach((element) => {
  2508. const removedTransforms = removeNonTranslationalTransform(element);
  2509. if (!removedTransforms.length)
  2510. return;
  2511. transformsToRestore.set(element, removedTransforms);
  2512. element.render();
  2513. });
  2514. // Read
  2515. resolversToMeasure.forEach((resolver) => resolver.measureInitialState());
  2516. // Write
  2517. elementsToMeasure.forEach((element) => {
  2518. element.render();
  2519. const restore = transformsToRestore.get(element);
  2520. if (restore) {
  2521. restore.forEach(([key, value]) => {
  2522. element.getValue(key)?.set(value);
  2523. });
  2524. }
  2525. });
  2526. // Read
  2527. resolversToMeasure.forEach((resolver) => resolver.measureEndState());
  2528. // Write
  2529. resolversToMeasure.forEach((resolver) => {
  2530. if (resolver.suspendedScrollY !== undefined) {
  2531. window.scrollTo(0, resolver.suspendedScrollY);
  2532. }
  2533. });
  2534. }
  2535. anyNeedsMeasurement = false;
  2536. isScheduled = false;
  2537. toResolve.forEach((resolver) => resolver.complete());
  2538. toResolve.clear();
  2539. }
  2540. function readAllKeyframes() {
  2541. toResolve.forEach((resolver) => {
  2542. resolver.readKeyframes();
  2543. if (resolver.needsMeasurement) {
  2544. anyNeedsMeasurement = true;
  2545. }
  2546. });
  2547. }
  2548. function flushKeyframeResolvers() {
  2549. readAllKeyframes();
  2550. measureAllKeyframes();
  2551. }
  2552. class KeyframeResolver {
  2553. constructor(unresolvedKeyframes, onComplete, name, motionValue, element, isAsync = false) {
  2554. /**
  2555. * Track whether this resolver has completed. Once complete, it never
  2556. * needs to attempt keyframe resolution again.
  2557. */
  2558. this.isComplete = false;
  2559. /**
  2560. * Track whether this resolver is async. If it is, it'll be added to the
  2561. * resolver queue and flushed in the next frame. Resolvers that aren't going
  2562. * to trigger read/write thrashing don't need to be async.
  2563. */
  2564. this.isAsync = false;
  2565. /**
  2566. * Track whether this resolver needs to perform a measurement
  2567. * to resolve its keyframes.
  2568. */
  2569. this.needsMeasurement = false;
  2570. /**
  2571. * Track whether this resolver is currently scheduled to resolve
  2572. * to allow it to be cancelled and resumed externally.
  2573. */
  2574. this.isScheduled = false;
  2575. this.unresolvedKeyframes = [...unresolvedKeyframes];
  2576. this.onComplete = onComplete;
  2577. this.name = name;
  2578. this.motionValue = motionValue;
  2579. this.element = element;
  2580. this.isAsync = isAsync;
  2581. }
  2582. scheduleResolve() {
  2583. this.isScheduled = true;
  2584. if (this.isAsync) {
  2585. toResolve.add(this);
  2586. if (!isScheduled) {
  2587. isScheduled = true;
  2588. frame.read(readAllKeyframes);
  2589. frame.resolveKeyframes(measureAllKeyframes);
  2590. }
  2591. }
  2592. else {
  2593. this.readKeyframes();
  2594. this.complete();
  2595. }
  2596. }
  2597. readKeyframes() {
  2598. const { unresolvedKeyframes, name, element, motionValue } = this;
  2599. /**
  2600. * If a keyframe is null, we hydrate it either by reading it from
  2601. * the instance, or propagating from previous keyframes.
  2602. */
  2603. for (let i = 0; i < unresolvedKeyframes.length; i++) {
  2604. if (unresolvedKeyframes[i] === null) {
  2605. /**
  2606. * If the first keyframe is null, we need to find its value by sampling the element
  2607. */
  2608. if (i === 0) {
  2609. const currentValue = motionValue?.get();
  2610. const finalKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
  2611. if (currentValue !== undefined) {
  2612. unresolvedKeyframes[0] = currentValue;
  2613. }
  2614. else if (element && name) {
  2615. const valueAsRead = element.readValue(name, finalKeyframe);
  2616. if (valueAsRead !== undefined && valueAsRead !== null) {
  2617. unresolvedKeyframes[0] = valueAsRead;
  2618. }
  2619. }
  2620. if (unresolvedKeyframes[0] === undefined) {
  2621. unresolvedKeyframes[0] = finalKeyframe;
  2622. }
  2623. if (motionValue && currentValue === undefined) {
  2624. motionValue.set(unresolvedKeyframes[0]);
  2625. }
  2626. }
  2627. else {
  2628. unresolvedKeyframes[i] = unresolvedKeyframes[i - 1];
  2629. }
  2630. }
  2631. }
  2632. }
  2633. setFinalKeyframe() { }
  2634. measureInitialState() { }
  2635. renderEndStyles() { }
  2636. measureEndState() { }
  2637. complete() {
  2638. this.isComplete = true;
  2639. this.onComplete(this.unresolvedKeyframes, this.finalKeyframe);
  2640. toResolve.delete(this);
  2641. }
  2642. cancel() {
  2643. if (!this.isComplete) {
  2644. this.isScheduled = false;
  2645. toResolve.delete(this);
  2646. }
  2647. }
  2648. resume() {
  2649. if (!this.isComplete)
  2650. this.scheduleResolve();
  2651. }
  2652. }
  2653. /**
  2654. * Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1"
  2655. */
  2656. const isNumericalString = (v) => /^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(v);
  2657. const checkStringStartsWith = (token) => (key) => typeof key === "string" && key.startsWith(token);
  2658. const isCSSVariableName =
  2659. /*@__PURE__*/ checkStringStartsWith("--");
  2660. const startsAsVariableToken =
  2661. /*@__PURE__*/ checkStringStartsWith("var(--");
  2662. const isCSSVariableToken = (value) => {
  2663. const startsWithToken = startsAsVariableToken(value);
  2664. if (!startsWithToken)
  2665. return false;
  2666. // Ensure any comments are stripped from the value as this can harm performance of the regex.
  2667. return singleCssVariableRegex.test(value.split("/*")[0].trim());
  2668. };
  2669. const singleCssVariableRegex = /var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu;
  2670. /**
  2671. * Parse Framer's special CSS variable format into a CSS token and a fallback.
  2672. *
  2673. * ```
  2674. * `var(--foo, #fff)` => [`--foo`, '#fff']
  2675. * ```
  2676. *
  2677. * @param current
  2678. */
  2679. const splitCSSVariableRegex =
  2680. // eslint-disable-next-line redos-detector/no-unsafe-regex -- false positive, as it can match a lot of words
  2681. /^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;
  2682. function parseCSSVariable(current) {
  2683. const match = splitCSSVariableRegex.exec(current);
  2684. if (!match)
  2685. return [,];
  2686. const [, token1, token2, fallback] = match;
  2687. return [`--${token1 ?? token2}`, fallback];
  2688. }
  2689. const maxDepth = 4;
  2690. function getVariableValue(current, element, depth = 1) {
  2691. exports.invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`);
  2692. const [token, fallback] = parseCSSVariable(current);
  2693. // No CSS variable detected
  2694. if (!token)
  2695. return;
  2696. // Attempt to read this CSS variable off the element
  2697. const resolved = window.getComputedStyle(element).getPropertyValue(token);
  2698. if (resolved) {
  2699. const trimmed = resolved.trim();
  2700. return isNumericalString(trimmed) ? parseFloat(trimmed) : trimmed;
  2701. }
  2702. return isCSSVariableToken(fallback)
  2703. ? getVariableValue(fallback, element, depth + 1)
  2704. : fallback;
  2705. }
  2706. /**
  2707. * Tests a provided value against a ValueType
  2708. */
  2709. const testValueType = (v) => (type) => type.test(v);
  2710. /**
  2711. * ValueType for "auto"
  2712. */
  2713. const auto = {
  2714. test: (v) => v === "auto",
  2715. parse: (v) => v,
  2716. };
  2717. /**
  2718. * A list of value types commonly used for dimensions
  2719. */
  2720. const dimensionValueTypes = [number, px, percent, degrees, vw, vh, auto];
  2721. /**
  2722. * Tests a dimensional value against the list of dimension ValueTypes
  2723. */
  2724. const findDimensionValueType = (v) => dimensionValueTypes.find(testValueType(v));
  2725. class DOMKeyframesResolver extends KeyframeResolver {
  2726. constructor(unresolvedKeyframes, onComplete, name, motionValue, element) {
  2727. super(unresolvedKeyframes, onComplete, name, motionValue, element, true);
  2728. }
  2729. readKeyframes() {
  2730. const { unresolvedKeyframes, element, name } = this;
  2731. if (!element || !element.current)
  2732. return;
  2733. super.readKeyframes();
  2734. /**
  2735. * If any keyframe is a CSS variable, we need to find its value by sampling the element
  2736. */
  2737. for (let i = 0; i < unresolvedKeyframes.length; i++) {
  2738. let keyframe = unresolvedKeyframes[i];
  2739. if (typeof keyframe === "string") {
  2740. keyframe = keyframe.trim();
  2741. if (isCSSVariableToken(keyframe)) {
  2742. const resolved = getVariableValue(keyframe, element.current);
  2743. if (resolved !== undefined) {
  2744. unresolvedKeyframes[i] = resolved;
  2745. }
  2746. if (i === unresolvedKeyframes.length - 1) {
  2747. this.finalKeyframe = keyframe;
  2748. }
  2749. }
  2750. }
  2751. }
  2752. /**
  2753. * Resolve "none" values. We do this potentially twice - once before and once after measuring keyframes.
  2754. * This could be seen as inefficient but it's a trade-off to avoid measurements in more situations, which
  2755. * have a far bigger performance impact.
  2756. */
  2757. this.resolveNoneKeyframes();
  2758. /**
  2759. * Check to see if unit type has changed. If so schedule jobs that will
  2760. * temporarily set styles to the destination keyframes.
  2761. * Skip if we have more than two keyframes or this isn't a positional value.
  2762. * TODO: We can throw if there are multiple keyframes and the value type changes.
  2763. */
  2764. if (!positionalKeys.has(name) || unresolvedKeyframes.length !== 2) {
  2765. return;
  2766. }
  2767. const [origin, target] = unresolvedKeyframes;
  2768. const originType = findDimensionValueType(origin);
  2769. const targetType = findDimensionValueType(target);
  2770. /**
  2771. * Either we don't recognise these value types or we can animate between them.
  2772. */
  2773. if (originType === targetType)
  2774. return;
  2775. /**
  2776. * If both values are numbers or pixels, we can animate between them by
  2777. * converting them to numbers.
  2778. */
  2779. if (isNumOrPxType(originType) && isNumOrPxType(targetType)) {
  2780. for (let i = 0; i < unresolvedKeyframes.length; i++) {
  2781. const value = unresolvedKeyframes[i];
  2782. if (typeof value === "string") {
  2783. unresolvedKeyframes[i] = parseFloat(value);
  2784. }
  2785. }
  2786. }
  2787. else {
  2788. /**
  2789. * Else, the only way to resolve this is by measuring the element.
  2790. */
  2791. this.needsMeasurement = true;
  2792. }
  2793. }
  2794. resolveNoneKeyframes() {
  2795. const { unresolvedKeyframes, name } = this;
  2796. const noneKeyframeIndexes = [];
  2797. for (let i = 0; i < unresolvedKeyframes.length; i++) {
  2798. if (isNone(unresolvedKeyframes[i])) {
  2799. noneKeyframeIndexes.push(i);
  2800. }
  2801. }
  2802. if (noneKeyframeIndexes.length) {
  2803. makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name);
  2804. }
  2805. }
  2806. measureInitialState() {
  2807. const { element, unresolvedKeyframes, name } = this;
  2808. if (!element || !element.current)
  2809. return;
  2810. if (name === "height") {
  2811. this.suspendedScrollY = window.pageYOffset;
  2812. }
  2813. this.measuredOrigin = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
  2814. unresolvedKeyframes[0] = this.measuredOrigin;
  2815. // Set final key frame to measure after next render
  2816. const measureKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
  2817. if (measureKeyframe !== undefined) {
  2818. element.getValue(name, measureKeyframe).jump(measureKeyframe, false);
  2819. }
  2820. }
  2821. measureEndState() {
  2822. const { element, name, unresolvedKeyframes } = this;
  2823. if (!element || !element.current)
  2824. return;
  2825. const value = element.getValue(name);
  2826. value && value.jump(this.measuredOrigin, false);
  2827. const finalKeyframeIndex = unresolvedKeyframes.length - 1;
  2828. const finalKeyframe = unresolvedKeyframes[finalKeyframeIndex];
  2829. unresolvedKeyframes[finalKeyframeIndex] = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
  2830. if (finalKeyframe !== null && this.finalKeyframe === undefined) {
  2831. this.finalKeyframe = finalKeyframe;
  2832. }
  2833. // If we removed transform values, reapply them before the next render
  2834. if (this.removedTransforms?.length) {
  2835. this.removedTransforms.forEach(([unsetTransformName, unsetTransformValue]) => {
  2836. element
  2837. .getValue(unsetTransformName)
  2838. .set(unsetTransformValue);
  2839. });
  2840. }
  2841. this.resolveNoneKeyframes();
  2842. }
  2843. }
  2844. /**
  2845. * Check if a value is animatable. Examples:
  2846. *
  2847. * ✅: 100, "100px", "#fff"
  2848. * ❌: "block", "url(2.jpg)"
  2849. * @param value
  2850. *
  2851. * @internal
  2852. */
  2853. const isAnimatable = (value, name) => {
  2854. // If the list of keys tat might be non-animatable grows, replace with Set
  2855. if (name === "zIndex")
  2856. return false;
  2857. // If it's a number or a keyframes array, we can animate it. We might at some point
  2858. // need to do a deep isAnimatable check of keyframes, or let Popmotion handle this,
  2859. // but for now lets leave it like this for performance reasons
  2860. if (typeof value === "number" || Array.isArray(value))
  2861. return true;
  2862. if (typeof value === "string" && // It's animatable if we have a string
  2863. (complex.test(value) || value === "0") && // And it contains numbers and/or colors
  2864. !value.startsWith("url(") // Unless it starts with "url("
  2865. ) {
  2866. return true;
  2867. }
  2868. return false;
  2869. };
  2870. function hasKeyframesChanged(keyframes) {
  2871. const current = keyframes[0];
  2872. if (keyframes.length === 1)
  2873. return true;
  2874. for (let i = 0; i < keyframes.length; i++) {
  2875. if (keyframes[i] !== current)
  2876. return true;
  2877. }
  2878. }
  2879. function canAnimate(keyframes, name, type, velocity) {
  2880. /**
  2881. * Check if we're able to animate between the start and end keyframes,
  2882. * and throw a warning if we're attempting to animate between one that's
  2883. * animatable and another that isn't.
  2884. */
  2885. const originKeyframe = keyframes[0];
  2886. if (originKeyframe === null)
  2887. return false;
  2888. /**
  2889. * These aren't traditionally animatable but we do support them.
  2890. * In future we could look into making this more generic or replacing
  2891. * this function with mix() === mixImmediate
  2892. */
  2893. if (name === "display" || name === "visibility")
  2894. return true;
  2895. const targetKeyframe = keyframes[keyframes.length - 1];
  2896. const isOriginAnimatable = isAnimatable(originKeyframe, name);
  2897. const isTargetAnimatable = isAnimatable(targetKeyframe, name);
  2898. warning(isOriginAnimatable === isTargetAnimatable, `You are trying to animate ${name} from "${originKeyframe}" to "${targetKeyframe}". ${originKeyframe} is not an animatable value - to enable this animation set ${originKeyframe} to a value animatable to ${targetKeyframe} via the \`style\` property.`);
  2899. // Always skip if any of these are true
  2900. if (!isOriginAnimatable || !isTargetAnimatable) {
  2901. return false;
  2902. }
  2903. return (hasKeyframesChanged(keyframes) ||
  2904. ((type === "spring" || isGenerator(type)) && velocity));
  2905. }
  2906. const isNotNull = (value) => value !== null;
  2907. function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
  2908. const resolvedKeyframes = keyframes.filter(isNotNull);
  2909. const index = repeat && repeatType !== "loop" && repeat % 2 === 1
  2910. ? 0
  2911. : resolvedKeyframes.length - 1;
  2912. return !index || finalKeyframe === undefined
  2913. ? resolvedKeyframes[index]
  2914. : finalKeyframe;
  2915. }
  2916. /**
  2917. * Maximum time allowed between an animation being created and it being
  2918. * resolved for us to use the latter as the start time.
  2919. *
  2920. * This is to ensure that while we prefer to "start" an animation as soon
  2921. * as it's triggered, we also want to avoid a visual jump if there's a big delay
  2922. * between these two moments.
  2923. */
  2924. const MAX_RESOLVE_DELAY = 40;
  2925. class BaseAnimation {
  2926. constructor({ autoplay = true, delay = 0, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", ...options }) {
  2927. // Track whether the animation has been stopped. Stopped animations won't restart.
  2928. this.isStopped = false;
  2929. this.hasAttemptedResolve = false;
  2930. this.createdAt = time.now();
  2931. this.options = {
  2932. autoplay,
  2933. delay,
  2934. type,
  2935. repeat,
  2936. repeatDelay,
  2937. repeatType,
  2938. ...options,
  2939. };
  2940. this.updateFinishedPromise();
  2941. }
  2942. /**
  2943. * This method uses the createdAt and resolvedAt to calculate the
  2944. * animation startTime. *Ideally*, we would use the createdAt time as t=0
  2945. * as the following frame would then be the first frame of the animation in
  2946. * progress, which would feel snappier.
  2947. *
  2948. * However, if there's a delay (main thread work) between the creation of
  2949. * the animation and the first commited frame, we prefer to use resolvedAt
  2950. * to avoid a sudden jump into the animation.
  2951. */
  2952. calcStartTime() {
  2953. if (!this.resolvedAt)
  2954. return this.createdAt;
  2955. return this.resolvedAt - this.createdAt > MAX_RESOLVE_DELAY
  2956. ? this.resolvedAt
  2957. : this.createdAt;
  2958. }
  2959. /**
  2960. * A getter for resolved data. If keyframes are not yet resolved, accessing
  2961. * this.resolved will synchronously flush all pending keyframe resolvers.
  2962. * This is a deoptimisation, but at its worst still batches read/writes.
  2963. */
  2964. get resolved() {
  2965. if (!this._resolved && !this.hasAttemptedResolve) {
  2966. flushKeyframeResolvers();
  2967. }
  2968. return this._resolved;
  2969. }
  2970. /**
  2971. * A method to be called when the keyframes resolver completes. This method
  2972. * will check if its possible to run the animation and, if not, skip it.
  2973. * Otherwise, it will call initPlayback on the implementing class.
  2974. */
  2975. onKeyframesResolved(keyframes, finalKeyframe) {
  2976. this.resolvedAt = time.now();
  2977. this.hasAttemptedResolve = true;
  2978. const { name, type, velocity, delay, onComplete, onUpdate, isGenerator, } = this.options;
  2979. /**
  2980. * If we can't animate this value with the resolved keyframes
  2981. * then we should complete it immediately.
  2982. */
  2983. if (!isGenerator && !canAnimate(keyframes, name, type, velocity)) {
  2984. // Finish immediately
  2985. if (instantAnimationState.current || !delay) {
  2986. onUpdate &&
  2987. onUpdate(getFinalKeyframe(keyframes, this.options, finalKeyframe));
  2988. onComplete && onComplete();
  2989. this.resolveFinishedPromise();
  2990. return;
  2991. }
  2992. // Finish after a delay
  2993. else {
  2994. this.options.duration = 0;
  2995. }
  2996. }
  2997. const resolvedAnimation = this.initPlayback(keyframes, finalKeyframe);
  2998. if (resolvedAnimation === false)
  2999. return;
  3000. this._resolved = {
  3001. keyframes,
  3002. finalKeyframe,
  3003. ...resolvedAnimation,
  3004. };
  3005. this.onPostResolved();
  3006. }
  3007. onPostResolved() { }
  3008. /**
  3009. * Allows the returned animation to be awaited or promise-chained. Currently
  3010. * resolves when the animation finishes at all but in a future update could/should
  3011. * reject if its cancels.
  3012. */
  3013. then(resolve, reject) {
  3014. return this.currentFinishedPromise.then(resolve, reject);
  3015. }
  3016. flatten() {
  3017. if (!this.options.allowFlatten)
  3018. return;
  3019. this.options.type = "keyframes";
  3020. this.options.ease = "linear";
  3021. }
  3022. updateFinishedPromise() {
  3023. this.currentFinishedPromise = new Promise((resolve) => {
  3024. this.resolveFinishedPromise = resolve;
  3025. });
  3026. }
  3027. }
  3028. // Adapted from https://gist.github.com/mjackson/5311256
  3029. function hueToRgb(p, q, t) {
  3030. if (t < 0)
  3031. t += 1;
  3032. if (t > 1)
  3033. t -= 1;
  3034. if (t < 1 / 6)
  3035. return p + (q - p) * 6 * t;
  3036. if (t < 1 / 2)
  3037. return q;
  3038. if (t < 2 / 3)
  3039. return p + (q - p) * (2 / 3 - t) * 6;
  3040. return p;
  3041. }
  3042. function hslaToRgba({ hue, saturation, lightness, alpha }) {
  3043. hue /= 360;
  3044. saturation /= 100;
  3045. lightness /= 100;
  3046. let red = 0;
  3047. let green = 0;
  3048. let blue = 0;
  3049. if (!saturation) {
  3050. red = green = blue = lightness;
  3051. }
  3052. else {
  3053. const q = lightness < 0.5
  3054. ? lightness * (1 + saturation)
  3055. : lightness + saturation - lightness * saturation;
  3056. const p = 2 * lightness - q;
  3057. red = hueToRgb(p, q, hue + 1 / 3);
  3058. green = hueToRgb(p, q, hue);
  3059. blue = hueToRgb(p, q, hue - 1 / 3);
  3060. }
  3061. return {
  3062. red: Math.round(red * 255),
  3063. green: Math.round(green * 255),
  3064. blue: Math.round(blue * 255),
  3065. alpha,
  3066. };
  3067. }
  3068. function mixImmediate(a, b) {
  3069. return (p) => (p > 0 ? b : a);
  3070. }
  3071. // Linear color space blending
  3072. // Explained https://www.youtube.com/watch?v=LKnqECcg6Gw
  3073. // Demonstrated http://codepen.io/osublake/pen/xGVVaN
  3074. const mixLinearColor = (from, to, v) => {
  3075. const fromExpo = from * from;
  3076. const expo = v * (to * to - fromExpo) + fromExpo;
  3077. return expo < 0 ? 0 : Math.sqrt(expo);
  3078. };
  3079. const colorTypes = [hex, rgba, hsla];
  3080. const getColorType = (v) => colorTypes.find((type) => type.test(v));
  3081. function asRGBA(color) {
  3082. const type = getColorType(color);
  3083. warning(Boolean(type), `'${color}' is not an animatable color. Use the equivalent color code instead.`);
  3084. if (!Boolean(type))
  3085. return false;
  3086. let model = type.parse(color);
  3087. if (type === hsla) {
  3088. // TODO Remove this cast - needed since Motion's stricter typing
  3089. model = hslaToRgba(model);
  3090. }
  3091. return model;
  3092. }
  3093. const mixColor = (from, to) => {
  3094. const fromRGBA = asRGBA(from);
  3095. const toRGBA = asRGBA(to);
  3096. if (!fromRGBA || !toRGBA) {
  3097. return mixImmediate(from, to);
  3098. }
  3099. const blended = { ...fromRGBA };
  3100. return (v) => {
  3101. blended.red = mixLinearColor(fromRGBA.red, toRGBA.red, v);
  3102. blended.green = mixLinearColor(fromRGBA.green, toRGBA.green, v);
  3103. blended.blue = mixLinearColor(fromRGBA.blue, toRGBA.blue, v);
  3104. blended.alpha = mixNumber$1(fromRGBA.alpha, toRGBA.alpha, v);
  3105. return rgba.transform(blended);
  3106. };
  3107. };
  3108. /**
  3109. * Pipe
  3110. * Compose other transformers to run linearily
  3111. * pipe(min(20), max(40))
  3112. * @param {...functions} transformers
  3113. * @return {function}
  3114. */
  3115. const combineFunctions = (a, b) => (v) => b(a(v));
  3116. const pipe = (...transformers) => transformers.reduce(combineFunctions);
  3117. const invisibleValues = new Set(["none", "hidden"]);
  3118. /**
  3119. * Returns a function that, when provided a progress value between 0 and 1,
  3120. * will return the "none" or "hidden" string only when the progress is that of
  3121. * the origin or target.
  3122. */
  3123. function mixVisibility(origin, target) {
  3124. if (invisibleValues.has(origin)) {
  3125. return (p) => (p <= 0 ? origin : target);
  3126. }
  3127. else {
  3128. return (p) => (p >= 1 ? target : origin);
  3129. }
  3130. }
  3131. function mixNumber(a, b) {
  3132. return (p) => mixNumber$1(a, b, p);
  3133. }
  3134. function getMixer$1(a) {
  3135. if (typeof a === "number") {
  3136. return mixNumber;
  3137. }
  3138. else if (typeof a === "string") {
  3139. return isCSSVariableToken(a)
  3140. ? mixImmediate
  3141. : color.test(a)
  3142. ? mixColor
  3143. : mixComplex;
  3144. }
  3145. else if (Array.isArray(a)) {
  3146. return mixArray;
  3147. }
  3148. else if (typeof a === "object") {
  3149. return color.test(a) ? mixColor : mixObject;
  3150. }
  3151. return mixImmediate;
  3152. }
  3153. function mixArray(a, b) {
  3154. const output = [...a];
  3155. const numValues = output.length;
  3156. const blendValue = a.map((v, i) => getMixer$1(v)(v, b[i]));
  3157. return (p) => {
  3158. for (let i = 0; i < numValues; i++) {
  3159. output[i] = blendValue[i](p);
  3160. }
  3161. return output;
  3162. };
  3163. }
  3164. function mixObject(a, b) {
  3165. const output = { ...a, ...b };
  3166. const blendValue = {};
  3167. for (const key in output) {
  3168. if (a[key] !== undefined && b[key] !== undefined) {
  3169. blendValue[key] = getMixer$1(a[key])(a[key], b[key]);
  3170. }
  3171. }
  3172. return (v) => {
  3173. for (const key in blendValue) {
  3174. output[key] = blendValue[key](v);
  3175. }
  3176. return output;
  3177. };
  3178. }
  3179. function matchOrder(origin, target) {
  3180. const orderedOrigin = [];
  3181. const pointers = { color: 0, var: 0, number: 0 };
  3182. for (let i = 0; i < target.values.length; i++) {
  3183. const type = target.types[i];
  3184. const originIndex = origin.indexes[type][pointers[type]];
  3185. const originValue = origin.values[originIndex] ?? 0;
  3186. orderedOrigin[i] = originValue;
  3187. pointers[type]++;
  3188. }
  3189. return orderedOrigin;
  3190. }
  3191. const mixComplex = (origin, target) => {
  3192. const template = complex.createTransformer(target);
  3193. const originStats = analyseComplexValue(origin);
  3194. const targetStats = analyseComplexValue(target);
  3195. const canInterpolate = originStats.indexes.var.length === targetStats.indexes.var.length &&
  3196. originStats.indexes.color.length === targetStats.indexes.color.length &&
  3197. originStats.indexes.number.length >= targetStats.indexes.number.length;
  3198. if (canInterpolate) {
  3199. if ((invisibleValues.has(origin) &&
  3200. !targetStats.values.length) ||
  3201. (invisibleValues.has(target) &&
  3202. !originStats.values.length)) {
  3203. return mixVisibility(origin, target);
  3204. }
  3205. return pipe(mixArray(matchOrder(originStats, targetStats), targetStats.values), template);
  3206. }
  3207. else {
  3208. warning(true, `Complex values '${origin}' and '${target}' too different to mix. Ensure all colors are of the same type, and that each contains the same quantity of number and color values. Falling back to instant transition.`);
  3209. return mixImmediate(origin, target);
  3210. }
  3211. };
  3212. function mix(from, to, p) {
  3213. if (typeof from === "number" &&
  3214. typeof to === "number" &&
  3215. typeof p === "number") {
  3216. return mixNumber$1(from, to, p);
  3217. }
  3218. const mixer = getMixer$1(from);
  3219. return mixer(from, to);
  3220. }
  3221. const velocitySampleDuration = 5; // ms
  3222. function calcGeneratorVelocity(resolveValue, t, current) {
  3223. const prevT = Math.max(t - velocitySampleDuration, 0);
  3224. return velocityPerSecond(current - resolveValue(prevT), t - prevT);
  3225. }
  3226. const springDefaults = {
  3227. // Default spring physics
  3228. stiffness: 100,
  3229. damping: 10,
  3230. mass: 1.0,
  3231. velocity: 0.0,
  3232. // Default duration/bounce-based options
  3233. duration: 800, // in ms
  3234. bounce: 0.3,
  3235. visualDuration: 0.3, // in seconds
  3236. // Rest thresholds
  3237. restSpeed: {
  3238. granular: 0.01,
  3239. default: 2,
  3240. },
  3241. restDelta: {
  3242. granular: 0.005,
  3243. default: 0.5,
  3244. },
  3245. // Limits
  3246. minDuration: 0.01, // in seconds
  3247. maxDuration: 10.0, // in seconds
  3248. minDamping: 0.05,
  3249. maxDamping: 1,
  3250. };
  3251. const safeMin = 0.001;
  3252. function findSpring({ duration = springDefaults.duration, bounce = springDefaults.bounce, velocity = springDefaults.velocity, mass = springDefaults.mass, }) {
  3253. let envelope;
  3254. let derivative;
  3255. warning(duration <= secondsToMilliseconds(springDefaults.maxDuration), "Spring duration must be 10 seconds or less");
  3256. let dampingRatio = 1 - bounce;
  3257. /**
  3258. * Restrict dampingRatio and duration to within acceptable ranges.
  3259. */
  3260. dampingRatio = clamp(springDefaults.minDamping, springDefaults.maxDamping, dampingRatio);
  3261. duration = clamp(springDefaults.minDuration, springDefaults.maxDuration, millisecondsToSeconds(duration));
  3262. if (dampingRatio < 1) {
  3263. /**
  3264. * Underdamped spring
  3265. */
  3266. envelope = (undampedFreq) => {
  3267. const exponentialDecay = undampedFreq * dampingRatio;
  3268. const delta = exponentialDecay * duration;
  3269. const a = exponentialDecay - velocity;
  3270. const b = calcAngularFreq(undampedFreq, dampingRatio);
  3271. const c = Math.exp(-delta);
  3272. return safeMin - (a / b) * c;
  3273. };
  3274. derivative = (undampedFreq) => {
  3275. const exponentialDecay = undampedFreq * dampingRatio;
  3276. const delta = exponentialDecay * duration;
  3277. const d = delta * velocity + velocity;
  3278. const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq, 2) * duration;
  3279. const f = Math.exp(-delta);
  3280. const g = calcAngularFreq(Math.pow(undampedFreq, 2), dampingRatio);
  3281. const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1;
  3282. return (factor * ((d - e) * f)) / g;
  3283. };
  3284. }
  3285. else {
  3286. /**
  3287. * Critically-damped spring
  3288. */
  3289. envelope = (undampedFreq) => {
  3290. const a = Math.exp(-undampedFreq * duration);
  3291. const b = (undampedFreq - velocity) * duration + 1;
  3292. return -safeMin + a * b;
  3293. };
  3294. derivative = (undampedFreq) => {
  3295. const a = Math.exp(-undampedFreq * duration);
  3296. const b = (velocity - undampedFreq) * (duration * duration);
  3297. return a * b;
  3298. };
  3299. }
  3300. const initialGuess = 5 / duration;
  3301. const undampedFreq = approximateRoot(envelope, derivative, initialGuess);
  3302. duration = secondsToMilliseconds(duration);
  3303. if (isNaN(undampedFreq)) {
  3304. return {
  3305. stiffness: springDefaults.stiffness,
  3306. damping: springDefaults.damping,
  3307. duration,
  3308. };
  3309. }
  3310. else {
  3311. const stiffness = Math.pow(undampedFreq, 2) * mass;
  3312. return {
  3313. stiffness,
  3314. damping: dampingRatio * 2 * Math.sqrt(mass * stiffness),
  3315. duration,
  3316. };
  3317. }
  3318. }
  3319. const rootIterations = 12;
  3320. function approximateRoot(envelope, derivative, initialGuess) {
  3321. let result = initialGuess;
  3322. for (let i = 1; i < rootIterations; i++) {
  3323. result = result - envelope(result) / derivative(result);
  3324. }
  3325. return result;
  3326. }
  3327. function calcAngularFreq(undampedFreq, dampingRatio) {
  3328. return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
  3329. }
  3330. const durationKeys = ["duration", "bounce"];
  3331. const physicsKeys = ["stiffness", "damping", "mass"];
  3332. function isSpringType(options, keys) {
  3333. return keys.some((key) => options[key] !== undefined);
  3334. }
  3335. function getSpringOptions(options) {
  3336. let springOptions = {
  3337. velocity: springDefaults.velocity,
  3338. stiffness: springDefaults.stiffness,
  3339. damping: springDefaults.damping,
  3340. mass: springDefaults.mass,
  3341. isResolvedFromDuration: false,
  3342. ...options,
  3343. };
  3344. // stiffness/damping/mass overrides duration/bounce
  3345. if (!isSpringType(options, physicsKeys) &&
  3346. isSpringType(options, durationKeys)) {
  3347. if (options.visualDuration) {
  3348. const visualDuration = options.visualDuration;
  3349. const root = (2 * Math.PI) / (visualDuration * 1.2);
  3350. const stiffness = root * root;
  3351. const damping = 2 *
  3352. clamp(0.05, 1, 1 - (options.bounce || 0)) *
  3353. Math.sqrt(stiffness);
  3354. springOptions = {
  3355. ...springOptions,
  3356. mass: springDefaults.mass,
  3357. stiffness,
  3358. damping,
  3359. };
  3360. }
  3361. else {
  3362. const derived = findSpring(options);
  3363. springOptions = {
  3364. ...springOptions,
  3365. ...derived,
  3366. mass: springDefaults.mass,
  3367. };
  3368. springOptions.isResolvedFromDuration = true;
  3369. }
  3370. }
  3371. return springOptions;
  3372. }
  3373. function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce = springDefaults.bounce) {
  3374. const options = typeof optionsOrVisualDuration !== "object"
  3375. ? {
  3376. visualDuration: optionsOrVisualDuration,
  3377. keyframes: [0, 1],
  3378. bounce,
  3379. }
  3380. : optionsOrVisualDuration;
  3381. let { restSpeed, restDelta } = options;
  3382. const origin = options.keyframes[0];
  3383. const target = options.keyframes[options.keyframes.length - 1];
  3384. /**
  3385. * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
  3386. * to reduce GC during animation.
  3387. */
  3388. const state = { done: false, value: origin };
  3389. const { stiffness, damping, mass, duration, velocity, isResolvedFromDuration, } = getSpringOptions({
  3390. ...options,
  3391. velocity: -millisecondsToSeconds(options.velocity || 0),
  3392. });
  3393. const initialVelocity = velocity || 0.0;
  3394. const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
  3395. const initialDelta = target - origin;
  3396. const undampedAngularFreq = millisecondsToSeconds(Math.sqrt(stiffness / mass));
  3397. /**
  3398. * If we're working on a granular scale, use smaller defaults for determining
  3399. * when the spring is finished.
  3400. *
  3401. * These defaults have been selected emprically based on what strikes a good
  3402. * ratio between feeling good and finishing as soon as changes are imperceptible.
  3403. */
  3404. const isGranularScale = Math.abs(initialDelta) < 5;
  3405. restSpeed || (restSpeed = isGranularScale
  3406. ? springDefaults.restSpeed.granular
  3407. : springDefaults.restSpeed.default);
  3408. restDelta || (restDelta = isGranularScale
  3409. ? springDefaults.restDelta.granular
  3410. : springDefaults.restDelta.default);
  3411. let resolveSpring;
  3412. if (dampingRatio < 1) {
  3413. const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio);
  3414. // Underdamped spring
  3415. resolveSpring = (t) => {
  3416. const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
  3417. return (target -
  3418. envelope *
  3419. (((initialVelocity +
  3420. dampingRatio * undampedAngularFreq * initialDelta) /
  3421. angularFreq) *
  3422. Math.sin(angularFreq * t) +
  3423. initialDelta * Math.cos(angularFreq * t)));
  3424. };
  3425. }
  3426. else if (dampingRatio === 1) {
  3427. // Critically damped spring
  3428. resolveSpring = (t) => target -
  3429. Math.exp(-undampedAngularFreq * t) *
  3430. (initialDelta +
  3431. (initialVelocity + undampedAngularFreq * initialDelta) * t);
  3432. }
  3433. else {
  3434. // Overdamped spring
  3435. const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1);
  3436. resolveSpring = (t) => {
  3437. const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
  3438. // When performing sinh or cosh values can hit Infinity so we cap them here
  3439. const freqForT = Math.min(dampedAngularFreq * t, 300);
  3440. return (target -
  3441. (envelope *
  3442. ((initialVelocity +
  3443. dampingRatio * undampedAngularFreq * initialDelta) *
  3444. Math.sinh(freqForT) +
  3445. dampedAngularFreq *
  3446. initialDelta *
  3447. Math.cosh(freqForT))) /
  3448. dampedAngularFreq);
  3449. };
  3450. }
  3451. const generator = {
  3452. calculatedDuration: isResolvedFromDuration ? duration || null : null,
  3453. next: (t) => {
  3454. const current = resolveSpring(t);
  3455. if (!isResolvedFromDuration) {
  3456. let currentVelocity = 0.0;
  3457. /**
  3458. * We only need to calculate velocity for under-damped springs
  3459. * as over- and critically-damped springs can't overshoot, so
  3460. * checking only for displacement is enough.
  3461. */
  3462. if (dampingRatio < 1) {
  3463. currentVelocity =
  3464. t === 0
  3465. ? secondsToMilliseconds(initialVelocity)
  3466. : calcGeneratorVelocity(resolveSpring, t, current);
  3467. }
  3468. const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;
  3469. const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta;
  3470. state.done =
  3471. isBelowVelocityThreshold && isBelowDisplacementThreshold;
  3472. }
  3473. else {
  3474. state.done = t >= duration;
  3475. }
  3476. state.value = state.done ? target : current;
  3477. return state;
  3478. },
  3479. toString: () => {
  3480. const calculatedDuration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
  3481. const easing = generateLinearEasing((progress) => generator.next(calculatedDuration * progress).value, calculatedDuration, 30);
  3482. return calculatedDuration + "ms " + easing;
  3483. },
  3484. toTransition: () => { },
  3485. };
  3486. return generator;
  3487. }
  3488. spring.applyToOptions = (options) => {
  3489. const generatorOptions = createGeneratorEasing(options, 100, spring);
  3490. options.ease = supportsLinearEasing() ? generatorOptions.ease : "easeOut";
  3491. options.duration = secondsToMilliseconds(generatorOptions.duration);
  3492. options.type = "keyframes";
  3493. return options;
  3494. };
  3495. function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, bounceDamping = 10, bounceStiffness = 500, modifyTarget, min, max, restDelta = 0.5, restSpeed, }) {
  3496. const origin = keyframes[0];
  3497. const state = {
  3498. done: false,
  3499. value: origin,
  3500. };
  3501. const isOutOfBounds = (v) => (min !== undefined && v < min) || (max !== undefined && v > max);
  3502. const nearestBoundary = (v) => {
  3503. if (min === undefined)
  3504. return max;
  3505. if (max === undefined)
  3506. return min;
  3507. return Math.abs(min - v) < Math.abs(max - v) ? min : max;
  3508. };
  3509. let amplitude = power * velocity;
  3510. const ideal = origin + amplitude;
  3511. const target = modifyTarget === undefined ? ideal : modifyTarget(ideal);
  3512. /**
  3513. * If the target has changed we need to re-calculate the amplitude, otherwise
  3514. * the animation will start from the wrong position.
  3515. */
  3516. if (target !== ideal)
  3517. amplitude = target - origin;
  3518. const calcDelta = (t) => -amplitude * Math.exp(-t / timeConstant);
  3519. const calcLatest = (t) => target + calcDelta(t);
  3520. const applyFriction = (t) => {
  3521. const delta = calcDelta(t);
  3522. const latest = calcLatest(t);
  3523. state.done = Math.abs(delta) <= restDelta;
  3524. state.value = state.done ? target : latest;
  3525. };
  3526. /**
  3527. * Ideally this would resolve for t in a stateless way, we could
  3528. * do that by always precalculating the animation but as we know
  3529. * this will be done anyway we can assume that spring will
  3530. * be discovered during that.
  3531. */
  3532. let timeReachedBoundary;
  3533. let spring$1;
  3534. const checkCatchBoundary = (t) => {
  3535. if (!isOutOfBounds(state.value))
  3536. return;
  3537. timeReachedBoundary = t;
  3538. spring$1 = spring({
  3539. keyframes: [state.value, nearestBoundary(state.value)],
  3540. velocity: calcGeneratorVelocity(calcLatest, t, state.value), // TODO: This should be passing * 1000
  3541. damping: bounceDamping,
  3542. stiffness: bounceStiffness,
  3543. restDelta,
  3544. restSpeed,
  3545. });
  3546. };
  3547. checkCatchBoundary(0);
  3548. return {
  3549. calculatedDuration: null,
  3550. next: (t) => {
  3551. /**
  3552. * We need to resolve the friction to figure out if we need a
  3553. * spring but we don't want to do this twice per frame. So here
  3554. * we flag if we updated for this frame and later if we did
  3555. * we can skip doing it again.
  3556. */
  3557. let hasUpdatedFrame = false;
  3558. if (!spring$1 && timeReachedBoundary === undefined) {
  3559. hasUpdatedFrame = true;
  3560. applyFriction(t);
  3561. checkCatchBoundary(t);
  3562. }
  3563. /**
  3564. * If we have a spring and the provided t is beyond the moment the friction
  3565. * animation crossed the min/max boundary, use the spring.
  3566. */
  3567. if (timeReachedBoundary !== undefined && t >= timeReachedBoundary) {
  3568. return spring$1.next(t - timeReachedBoundary);
  3569. }
  3570. else {
  3571. !hasUpdatedFrame && applyFriction(t);
  3572. return state;
  3573. }
  3574. },
  3575. };
  3576. }
  3577. const easeIn = /*@__PURE__*/ cubicBezier(0.42, 0, 1, 1);
  3578. const easeOut = /*@__PURE__*/ cubicBezier(0, 0, 0.58, 1);
  3579. const easeInOut = /*@__PURE__*/ cubicBezier(0.42, 0, 0.58, 1);
  3580. const isEasingArray = (ease) => {
  3581. return Array.isArray(ease) && typeof ease[0] !== "number";
  3582. };
  3583. const easingLookup = {
  3584. linear: noop,
  3585. easeIn,
  3586. easeInOut,
  3587. easeOut,
  3588. circIn,
  3589. circInOut,
  3590. circOut,
  3591. backIn,
  3592. backInOut,
  3593. backOut,
  3594. anticipate,
  3595. };
  3596. const easingDefinitionToFunction = (definition) => {
  3597. if (isBezierDefinition(definition)) {
  3598. // If cubic bezier definition, create bezier curve
  3599. exports.invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`);
  3600. const [x1, y1, x2, y2] = definition;
  3601. return cubicBezier(x1, y1, x2, y2);
  3602. }
  3603. else if (typeof definition === "string") {
  3604. // Else lookup from table
  3605. exports.invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`);
  3606. return easingLookup[definition];
  3607. }
  3608. return definition;
  3609. };
  3610. function createMixers(output, ease, customMixer) {
  3611. const mixers = [];
  3612. const mixerFactory = customMixer || mix;
  3613. const numMixers = output.length - 1;
  3614. for (let i = 0; i < numMixers; i++) {
  3615. let mixer = mixerFactory(output[i], output[i + 1]);
  3616. if (ease) {
  3617. const easingFunction = Array.isArray(ease) ? ease[i] || noop : ease;
  3618. mixer = pipe(easingFunction, mixer);
  3619. }
  3620. mixers.push(mixer);
  3621. }
  3622. return mixers;
  3623. }
  3624. /**
  3625. * Create a function that maps from a numerical input array to a generic output array.
  3626. *
  3627. * Accepts:
  3628. * - Numbers
  3629. * - Colors (hex, hsl, hsla, rgb, rgba)
  3630. * - Complex (combinations of one or more numbers or strings)
  3631. *
  3632. * ```jsx
  3633. * const mixColor = interpolate([0, 1], ['#fff', '#000'])
  3634. *
  3635. * mixColor(0.5) // 'rgba(128, 128, 128, 1)'
  3636. * ```
  3637. *
  3638. * TODO Revist this approach once we've moved to data models for values,
  3639. * probably not needed to pregenerate mixer functions.
  3640. *
  3641. * @public
  3642. */
  3643. function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) {
  3644. const inputLength = input.length;
  3645. exports.invariant(inputLength === output.length, "Both input and output ranges must be the same length");
  3646. /**
  3647. * If we're only provided a single input, we can just make a function
  3648. * that returns the output.
  3649. */
  3650. if (inputLength === 1)
  3651. return () => output[0];
  3652. if (inputLength === 2 && output[0] === output[1])
  3653. return () => output[1];
  3654. const isZeroDeltaRange = input[0] === input[1];
  3655. // If input runs highest -> lowest, reverse both arrays
  3656. if (input[0] > input[inputLength - 1]) {
  3657. input = [...input].reverse();
  3658. output = [...output].reverse();
  3659. }
  3660. const mixers = createMixers(output, ease, mixer);
  3661. const numMixers = mixers.length;
  3662. const interpolator = (v) => {
  3663. if (isZeroDeltaRange && v < input[0])
  3664. return output[0];
  3665. let i = 0;
  3666. if (numMixers > 1) {
  3667. for (; i < input.length - 2; i++) {
  3668. if (v < input[i + 1])
  3669. break;
  3670. }
  3671. }
  3672. const progressInRange = progress(input[i], input[i + 1], v);
  3673. return mixers[i](progressInRange);
  3674. };
  3675. return isClamp
  3676. ? (v) => interpolator(clamp(input[0], input[inputLength - 1], v))
  3677. : interpolator;
  3678. }
  3679. function fillOffset(offset, remaining) {
  3680. const min = offset[offset.length - 1];
  3681. for (let i = 1; i <= remaining; i++) {
  3682. const offsetProgress = progress(0, remaining, i);
  3683. offset.push(mixNumber$1(min, 1, offsetProgress));
  3684. }
  3685. }
  3686. function defaultOffset$1(arr) {
  3687. const offset = [0];
  3688. fillOffset(offset, arr.length - 1);
  3689. return offset;
  3690. }
  3691. function convertOffsetToTimes(offset, duration) {
  3692. return offset.map((o) => o * duration);
  3693. }
  3694. function defaultEasing(values, easing) {
  3695. return values.map(() => easing || easeInOut).splice(0, values.length - 1);
  3696. }
  3697. function keyframes({ duration = 300, keyframes: keyframeValues, times, ease = "easeInOut", }) {
  3698. /**
  3699. * Easing functions can be externally defined as strings. Here we convert them
  3700. * into actual functions.
  3701. */
  3702. const easingFunctions = isEasingArray(ease)
  3703. ? ease.map(easingDefinitionToFunction)
  3704. : easingDefinitionToFunction(ease);
  3705. /**
  3706. * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
  3707. * to reduce GC during animation.
  3708. */
  3709. const state = {
  3710. done: false,
  3711. value: keyframeValues[0],
  3712. };
  3713. /**
  3714. * Create a times array based on the provided 0-1 offsets
  3715. */
  3716. const absoluteTimes = convertOffsetToTimes(
  3717. // Only use the provided offsets if they're the correct length
  3718. // TODO Maybe we should warn here if there's a length mismatch
  3719. times && times.length === keyframeValues.length
  3720. ? times
  3721. : defaultOffset$1(keyframeValues), duration);
  3722. const mapTimeToKeyframe = interpolate(absoluteTimes, keyframeValues, {
  3723. ease: Array.isArray(easingFunctions)
  3724. ? easingFunctions
  3725. : defaultEasing(keyframeValues, easingFunctions),
  3726. });
  3727. return {
  3728. calculatedDuration: duration,
  3729. next: (t) => {
  3730. state.value = mapTimeToKeyframe(t);
  3731. state.done = t >= duration;
  3732. return state;
  3733. },
  3734. };
  3735. }
  3736. const frameloopDriver = (update) => {
  3737. const passTimestamp = ({ timestamp }) => update(timestamp);
  3738. return {
  3739. start: () => frame.update(passTimestamp, true),
  3740. stop: () => cancelFrame(passTimestamp),
  3741. /**
  3742. * If we're processing this frame we can use the
  3743. * framelocked timestamp to keep things in sync.
  3744. */
  3745. now: () => (frameData.isProcessing ? frameData.timestamp : time.now()),
  3746. };
  3747. };
  3748. const generators = {
  3749. decay: inertia,
  3750. inertia,
  3751. tween: keyframes,
  3752. keyframes: keyframes,
  3753. spring,
  3754. };
  3755. const percentToProgress = (percent) => percent / 100;
  3756. /**
  3757. * Animation that runs on the main thread. Designed to be WAAPI-spec in the subset of
  3758. * features we expose publically. Mostly the compatibility is to ensure visual identity
  3759. * between both WAAPI and main thread animations.
  3760. */
  3761. class MainThreadAnimation extends BaseAnimation {
  3762. constructor(options) {
  3763. super(options);
  3764. /**
  3765. * The time at which the animation was paused.
  3766. */
  3767. this.holdTime = null;
  3768. /**
  3769. * The time at which the animation was cancelled.
  3770. */
  3771. this.cancelTime = null;
  3772. /**
  3773. * The current time of the animation.
  3774. */
  3775. this.currentTime = 0;
  3776. /**
  3777. * Playback speed as a factor. 0 would be stopped, -1 reverse and 2 double speed.
  3778. */
  3779. this.playbackSpeed = 1;
  3780. /**
  3781. * The state of the animation to apply when the animation is resolved. This
  3782. * allows calls to the public API to control the animation before it is resolved,
  3783. * without us having to resolve it first.
  3784. */
  3785. this.pendingPlayState = "running";
  3786. /**
  3787. * The time at which the animation was started.
  3788. */
  3789. this.startTime = null;
  3790. this.state = "idle";
  3791. /**
  3792. * This method is bound to the instance to fix a pattern where
  3793. * animation.stop is returned as a reference from a useEffect.
  3794. */
  3795. this.stop = () => {
  3796. this.resolver.cancel();
  3797. this.isStopped = true;
  3798. if (this.state === "idle")
  3799. return;
  3800. this.teardown();
  3801. const { onStop } = this.options;
  3802. onStop && onStop();
  3803. };
  3804. const { name, motionValue, element, keyframes } = this.options;
  3805. const KeyframeResolver$1 = element?.KeyframeResolver || KeyframeResolver;
  3806. const onResolved = (resolvedKeyframes, finalKeyframe) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe);
  3807. this.resolver = new KeyframeResolver$1(keyframes, onResolved, name, motionValue, element);
  3808. this.resolver.scheduleResolve();
  3809. }
  3810. flatten() {
  3811. super.flatten();
  3812. // If we've already resolved the animation, re-initialise it
  3813. if (this._resolved) {
  3814. Object.assign(this._resolved, this.initPlayback(this._resolved.keyframes));
  3815. }
  3816. }
  3817. initPlayback(keyframes$1) {
  3818. const { type = "keyframes", repeat = 0, repeatDelay = 0, repeatType, velocity = 0, } = this.options;
  3819. const generatorFactory = isGenerator(type)
  3820. ? type
  3821. : generators[type] || keyframes;
  3822. /**
  3823. * If our generator doesn't support mixing numbers, we need to replace keyframes with
  3824. * [0, 100] and then make a function that maps that to the actual keyframes.
  3825. *
  3826. * 100 is chosen instead of 1 as it works nicer with spring animations.
  3827. */
  3828. let mapPercentToKeyframes;
  3829. let mirroredGenerator;
  3830. if (generatorFactory !== keyframes) {
  3831. exports.invariant(keyframes$1.length <= 2, `Only two keyframes currently supported with spring and inertia animations. Trying to animate ${keyframes$1}`);
  3832. }
  3833. if (generatorFactory !== keyframes &&
  3834. typeof keyframes$1[0] !== "number") {
  3835. mapPercentToKeyframes = pipe(percentToProgress, mix(keyframes$1[0], keyframes$1[1]));
  3836. keyframes$1 = [0, 100];
  3837. }
  3838. const generator = generatorFactory({ ...this.options, keyframes: keyframes$1 });
  3839. /**
  3840. * If we have a mirror repeat type we need to create a second generator that outputs the
  3841. * mirrored (not reversed) animation and later ping pong between the two generators.
  3842. */
  3843. if (repeatType === "mirror") {
  3844. mirroredGenerator = generatorFactory({
  3845. ...this.options,
  3846. keyframes: [...keyframes$1].reverse(),
  3847. velocity: -velocity,
  3848. });
  3849. }
  3850. /**
  3851. * If duration is undefined and we have repeat options,
  3852. * we need to calculate a duration from the generator.
  3853. *
  3854. * We set it to the generator itself to cache the duration.
  3855. * Any timeline resolver will need to have already precalculated
  3856. * the duration by this step.
  3857. */
  3858. if (generator.calculatedDuration === null) {
  3859. generator.calculatedDuration = calcGeneratorDuration(generator);
  3860. }
  3861. const { calculatedDuration } = generator;
  3862. const resolvedDuration = calculatedDuration + repeatDelay;
  3863. const totalDuration = resolvedDuration * (repeat + 1) - repeatDelay;
  3864. return {
  3865. generator,
  3866. mirroredGenerator,
  3867. mapPercentToKeyframes,
  3868. calculatedDuration,
  3869. resolvedDuration,
  3870. totalDuration,
  3871. };
  3872. }
  3873. onPostResolved() {
  3874. const { autoplay = true } = this.options;
  3875. this.play();
  3876. if (this.pendingPlayState === "paused" || !autoplay) {
  3877. this.pause();
  3878. }
  3879. else {
  3880. this.state = this.pendingPlayState;
  3881. }
  3882. }
  3883. tick(timestamp, sample = false) {
  3884. const { resolved } = this;
  3885. // If the animations has failed to resolve, return the final keyframe.
  3886. if (!resolved) {
  3887. const { keyframes } = this.options;
  3888. return { done: true, value: keyframes[keyframes.length - 1] };
  3889. }
  3890. const { finalKeyframe, generator, mirroredGenerator, mapPercentToKeyframes, keyframes, calculatedDuration, totalDuration, resolvedDuration, } = resolved;
  3891. if (this.startTime === null)
  3892. return generator.next(0);
  3893. const { delay, repeat, repeatType, repeatDelay, onUpdate } = this.options;
  3894. /**
  3895. * requestAnimationFrame timestamps can come through as lower than
  3896. * the startTime as set by performance.now(). Here we prevent this,
  3897. * though in the future it could be possible to make setting startTime
  3898. * a pending operation that gets resolved here.
  3899. */
  3900. if (this.speed > 0) {
  3901. this.startTime = Math.min(this.startTime, timestamp);
  3902. }
  3903. else if (this.speed < 0) {
  3904. this.startTime = Math.min(timestamp - totalDuration / this.speed, this.startTime);
  3905. }
  3906. // Update currentTime
  3907. if (sample) {
  3908. this.currentTime = timestamp;
  3909. }
  3910. else if (this.holdTime !== null) {
  3911. this.currentTime = this.holdTime;
  3912. }
  3913. else {
  3914. // Rounding the time because floating point arithmetic is not always accurate, e.g. 3000.367 - 1000.367 =
  3915. // 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for
  3916. // example.
  3917. this.currentTime =
  3918. Math.round(timestamp - this.startTime) * this.speed;
  3919. }
  3920. // Rebase on delay
  3921. const timeWithoutDelay = this.currentTime - delay * (this.speed >= 0 ? 1 : -1);
  3922. const isInDelayPhase = this.speed >= 0
  3923. ? timeWithoutDelay < 0
  3924. : timeWithoutDelay > totalDuration;
  3925. this.currentTime = Math.max(timeWithoutDelay, 0);
  3926. // If this animation has finished, set the current time to the total duration.
  3927. if (this.state === "finished" && this.holdTime === null) {
  3928. this.currentTime = totalDuration;
  3929. }
  3930. let elapsed = this.currentTime;
  3931. let frameGenerator = generator;
  3932. if (repeat) {
  3933. /**
  3934. * Get the current progress (0-1) of the animation. If t is >
  3935. * than duration we'll get values like 2.5 (midway through the
  3936. * third iteration)
  3937. */
  3938. const progress = Math.min(this.currentTime, totalDuration) / resolvedDuration;
  3939. /**
  3940. * Get the current iteration (0 indexed). For instance the floor of
  3941. * 2.5 is 2.
  3942. */
  3943. let currentIteration = Math.floor(progress);
  3944. /**
  3945. * Get the current progress of the iteration by taking the remainder
  3946. * so 2.5 is 0.5 through iteration 2
  3947. */
  3948. let iterationProgress = progress % 1.0;
  3949. /**
  3950. * If iteration progress is 1 we count that as the end
  3951. * of the previous iteration.
  3952. */
  3953. if (!iterationProgress && progress >= 1) {
  3954. iterationProgress = 1;
  3955. }
  3956. iterationProgress === 1 && currentIteration--;
  3957. currentIteration = Math.min(currentIteration, repeat + 1);
  3958. /**
  3959. * Reverse progress if we're not running in "normal" direction
  3960. */
  3961. const isOddIteration = Boolean(currentIteration % 2);
  3962. if (isOddIteration) {
  3963. if (repeatType === "reverse") {
  3964. iterationProgress = 1 - iterationProgress;
  3965. if (repeatDelay) {
  3966. iterationProgress -= repeatDelay / resolvedDuration;
  3967. }
  3968. }
  3969. else if (repeatType === "mirror") {
  3970. frameGenerator = mirroredGenerator;
  3971. }
  3972. }
  3973. elapsed = clamp(0, 1, iterationProgress) * resolvedDuration;
  3974. }
  3975. /**
  3976. * If we're in negative time, set state as the initial keyframe.
  3977. * This prevents delay: x, duration: 0 animations from finishing
  3978. * instantly.
  3979. */
  3980. const state = isInDelayPhase
  3981. ? { done: false, value: keyframes[0] }
  3982. : frameGenerator.next(elapsed);
  3983. if (mapPercentToKeyframes) {
  3984. state.value = mapPercentToKeyframes(state.value);
  3985. }
  3986. let { done } = state;
  3987. if (!isInDelayPhase && calculatedDuration !== null) {
  3988. done =
  3989. this.speed >= 0
  3990. ? this.currentTime >= totalDuration
  3991. : this.currentTime <= 0;
  3992. }
  3993. const isAnimationFinished = this.holdTime === null &&
  3994. (this.state === "finished" || (this.state === "running" && done));
  3995. if (isAnimationFinished && finalKeyframe !== undefined) {
  3996. state.value = getFinalKeyframe(keyframes, this.options, finalKeyframe);
  3997. }
  3998. if (onUpdate) {
  3999. onUpdate(state.value);
  4000. }
  4001. if (isAnimationFinished) {
  4002. this.finish();
  4003. }
  4004. return state;
  4005. }
  4006. get duration() {
  4007. const { resolved } = this;
  4008. return resolved ? millisecondsToSeconds(resolved.calculatedDuration) : 0;
  4009. }
  4010. get time() {
  4011. return millisecondsToSeconds(this.currentTime);
  4012. }
  4013. set time(newTime) {
  4014. newTime = secondsToMilliseconds(newTime);
  4015. this.currentTime = newTime;
  4016. if (this.holdTime !== null || this.speed === 0) {
  4017. this.holdTime = newTime;
  4018. }
  4019. else if (this.driver) {
  4020. this.startTime = this.driver.now() - newTime / this.speed;
  4021. }
  4022. }
  4023. get speed() {
  4024. return this.playbackSpeed;
  4025. }
  4026. set speed(newSpeed) {
  4027. const hasChanged = this.playbackSpeed !== newSpeed;
  4028. this.playbackSpeed = newSpeed;
  4029. if (hasChanged) {
  4030. this.time = millisecondsToSeconds(this.currentTime);
  4031. }
  4032. }
  4033. play() {
  4034. if (!this.resolver.isScheduled) {
  4035. this.resolver.resume();
  4036. }
  4037. if (!this._resolved) {
  4038. this.pendingPlayState = "running";
  4039. return;
  4040. }
  4041. if (this.isStopped)
  4042. return;
  4043. const { driver = frameloopDriver, onPlay, startTime } = this.options;
  4044. if (!this.driver) {
  4045. this.driver = driver((timestamp) => this.tick(timestamp));
  4046. }
  4047. onPlay && onPlay();
  4048. const now = this.driver.now();
  4049. if (this.holdTime !== null) {
  4050. this.startTime = now - this.holdTime;
  4051. }
  4052. else if (!this.startTime) {
  4053. this.startTime = startTime ?? this.calcStartTime();
  4054. }
  4055. else if (this.state === "finished") {
  4056. this.startTime = now;
  4057. }
  4058. if (this.state === "finished") {
  4059. this.updateFinishedPromise();
  4060. }
  4061. this.cancelTime = this.startTime;
  4062. this.holdTime = null;
  4063. /**
  4064. * Set playState to running only after we've used it in
  4065. * the previous logic.
  4066. */
  4067. this.state = "running";
  4068. this.driver.start();
  4069. }
  4070. pause() {
  4071. if (!this._resolved) {
  4072. this.pendingPlayState = "paused";
  4073. return;
  4074. }
  4075. this.state = "paused";
  4076. this.holdTime = this.currentTime ?? 0;
  4077. }
  4078. complete() {
  4079. if (this.state !== "running") {
  4080. this.play();
  4081. }
  4082. this.pendingPlayState = this.state = "finished";
  4083. this.holdTime = null;
  4084. }
  4085. finish() {
  4086. this.teardown();
  4087. this.state = "finished";
  4088. const { onComplete } = this.options;
  4089. onComplete && onComplete();
  4090. }
  4091. cancel() {
  4092. if (this.cancelTime !== null) {
  4093. this.tick(this.cancelTime);
  4094. }
  4095. this.teardown();
  4096. this.updateFinishedPromise();
  4097. }
  4098. teardown() {
  4099. this.state = "idle";
  4100. this.stopDriver();
  4101. this.resolveFinishedPromise();
  4102. this.updateFinishedPromise();
  4103. this.startTime = this.cancelTime = null;
  4104. this.resolver.cancel();
  4105. }
  4106. stopDriver() {
  4107. if (!this.driver)
  4108. return;
  4109. this.driver.stop();
  4110. this.driver = undefined;
  4111. }
  4112. sample(time) {
  4113. this.startTime = 0;
  4114. return this.tick(time, true);
  4115. }
  4116. get finished() {
  4117. return this.currentFinishedPromise;
  4118. }
  4119. }
  4120. // Legacy interface
  4121. function animateValue(options) {
  4122. return new MainThreadAnimation(options);
  4123. }
  4124. /**
  4125. * A list of values that can be hardware-accelerated.
  4126. */
  4127. const acceleratedValues = new Set([
  4128. "opacity",
  4129. "clipPath",
  4130. "filter",
  4131. "transform",
  4132. // TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved
  4133. // or until we implement support for linear() easing.
  4134. // "background-color"
  4135. ]);
  4136. const supportsWaapi = /*@__PURE__*/ memo(() => Object.hasOwnProperty.call(Element.prototype, "animate"));
  4137. /**
  4138. * 10ms is chosen here as it strikes a balance between smooth
  4139. * results (more than one keyframe per frame at 60fps) and
  4140. * keyframe quantity.
  4141. */
  4142. const sampleDelta = 10; //ms
  4143. /**
  4144. * Implement a practical max duration for keyframe generation
  4145. * to prevent infinite loops
  4146. */
  4147. const maxDuration = 20000;
  4148. /**
  4149. * Check if an animation can run natively via WAAPI or requires pregenerated keyframes.
  4150. * WAAPI doesn't support spring or function easings so we run these as JS animation before
  4151. * handing off.
  4152. */
  4153. function requiresPregeneratedKeyframes(options) {
  4154. return (isGenerator(options.type) ||
  4155. options.type === "spring" ||
  4156. !isWaapiSupportedEasing(options.ease));
  4157. }
  4158. function pregenerateKeyframes(keyframes, options) {
  4159. /**
  4160. * Create a main-thread animation to pregenerate keyframes.
  4161. * We sample this at regular intervals to generate keyframes that we then
  4162. * linearly interpolate between.
  4163. */
  4164. const sampleAnimation = new MainThreadAnimation({
  4165. ...options,
  4166. keyframes,
  4167. repeat: 0,
  4168. delay: 0,
  4169. isGenerator: true,
  4170. });
  4171. let state = { done: false, value: keyframes[0] };
  4172. const pregeneratedKeyframes = [];
  4173. /**
  4174. * Bail after 20 seconds of pre-generated keyframes as it's likely
  4175. * we're heading for an infinite loop.
  4176. */
  4177. let t = 0;
  4178. while (!state.done && t < maxDuration) {
  4179. state = sampleAnimation.sample(t);
  4180. pregeneratedKeyframes.push(state.value);
  4181. t += sampleDelta;
  4182. }
  4183. return {
  4184. times: undefined,
  4185. keyframes: pregeneratedKeyframes,
  4186. duration: t - sampleDelta,
  4187. ease: "linear",
  4188. };
  4189. }
  4190. const unsupportedEasingFunctions = {
  4191. anticipate,
  4192. backInOut,
  4193. circInOut,
  4194. };
  4195. function isUnsupportedEase(key) {
  4196. return key in unsupportedEasingFunctions;
  4197. }
  4198. class AcceleratedAnimation extends BaseAnimation {
  4199. constructor(options) {
  4200. super(options);
  4201. const { name, motionValue, element, keyframes } = this.options;
  4202. this.resolver = new DOMKeyframesResolver(keyframes, (resolvedKeyframes, finalKeyframe) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe), name, motionValue, element);
  4203. this.resolver.scheduleResolve();
  4204. }
  4205. initPlayback(keyframes, finalKeyframe) {
  4206. let { duration = 300, times, ease, type, motionValue, name, startTime, } = this.options;
  4207. /**
  4208. * If element has since been unmounted, return false to indicate
  4209. * the animation failed to initialised.
  4210. */
  4211. if (!motionValue.owner || !motionValue.owner.current) {
  4212. return false;
  4213. }
  4214. /**
  4215. * If the user has provided an easing function name that isn't supported
  4216. * by WAAPI (like "anticipate"), we need to provide the corressponding
  4217. * function. This will later get converted to a linear() easing function.
  4218. */
  4219. if (typeof ease === "string" &&
  4220. supportsLinearEasing() &&
  4221. isUnsupportedEase(ease)) {
  4222. ease = unsupportedEasingFunctions[ease];
  4223. }
  4224. /**
  4225. * If this animation needs pre-generated keyframes then generate.
  4226. */
  4227. if (requiresPregeneratedKeyframes(this.options)) {
  4228. const { onComplete, onUpdate, motionValue, element, ...options } = this.options;
  4229. const pregeneratedAnimation = pregenerateKeyframes(keyframes, options);
  4230. keyframes = pregeneratedAnimation.keyframes;
  4231. // If this is a very short animation, ensure we have
  4232. // at least two keyframes to animate between as older browsers
  4233. // can't animate between a single keyframe.
  4234. if (keyframes.length === 1) {
  4235. keyframes[1] = keyframes[0];
  4236. }
  4237. duration = pregeneratedAnimation.duration;
  4238. times = pregeneratedAnimation.times;
  4239. ease = pregeneratedAnimation.ease;
  4240. type = "keyframes";
  4241. }
  4242. const animation = startWaapiAnimation(motionValue.owner.current, name, keyframes, { ...this.options, duration, times, ease });
  4243. // Override the browser calculated startTime with one synchronised to other JS
  4244. // and WAAPI animations starting this event loop.
  4245. animation.startTime = startTime ?? this.calcStartTime();
  4246. if (this.pendingTimeline) {
  4247. attachTimeline(animation, this.pendingTimeline);
  4248. this.pendingTimeline = undefined;
  4249. }
  4250. else {
  4251. /**
  4252. * Prefer the `onfinish` prop as it's more widely supported than
  4253. * the `finished` promise.
  4254. *
  4255. * Here, we synchronously set the provided MotionValue to the end
  4256. * keyframe. If we didn't, when the WAAPI animation is finished it would
  4257. * be removed from the element which would then revert to its old styles.
  4258. */
  4259. animation.onfinish = () => {
  4260. const { onComplete } = this.options;
  4261. motionValue.set(getFinalKeyframe(keyframes, this.options, finalKeyframe));
  4262. onComplete && onComplete();
  4263. this.cancel();
  4264. this.resolveFinishedPromise();
  4265. };
  4266. }
  4267. return {
  4268. animation,
  4269. duration,
  4270. times,
  4271. type,
  4272. ease,
  4273. keyframes: keyframes,
  4274. };
  4275. }
  4276. get duration() {
  4277. const { resolved } = this;
  4278. if (!resolved)
  4279. return 0;
  4280. const { duration } = resolved;
  4281. return millisecondsToSeconds(duration);
  4282. }
  4283. get time() {
  4284. const { resolved } = this;
  4285. if (!resolved)
  4286. return 0;
  4287. const { animation } = resolved;
  4288. return millisecondsToSeconds(animation.currentTime || 0);
  4289. }
  4290. set time(newTime) {
  4291. const { resolved } = this;
  4292. if (!resolved)
  4293. return;
  4294. const { animation } = resolved;
  4295. animation.currentTime = secondsToMilliseconds(newTime);
  4296. }
  4297. get speed() {
  4298. const { resolved } = this;
  4299. if (!resolved)
  4300. return 1;
  4301. const { animation } = resolved;
  4302. return animation.playbackRate;
  4303. }
  4304. get finished() {
  4305. return this.resolved.animation.finished;
  4306. }
  4307. set speed(newSpeed) {
  4308. const { resolved } = this;
  4309. if (!resolved)
  4310. return;
  4311. const { animation } = resolved;
  4312. animation.playbackRate = newSpeed;
  4313. }
  4314. get state() {
  4315. const { resolved } = this;
  4316. if (!resolved)
  4317. return "idle";
  4318. const { animation } = resolved;
  4319. return animation.playState;
  4320. }
  4321. get startTime() {
  4322. const { resolved } = this;
  4323. if (!resolved)
  4324. return null;
  4325. const { animation } = resolved;
  4326. // Coerce to number as TypeScript incorrectly types this
  4327. // as CSSNumberish
  4328. return animation.startTime;
  4329. }
  4330. /**
  4331. * Replace the default DocumentTimeline with another AnimationTimeline.
  4332. * Currently used for scroll animations.
  4333. */
  4334. attachTimeline(timeline) {
  4335. if (!this._resolved) {
  4336. this.pendingTimeline = timeline;
  4337. }
  4338. else {
  4339. const { resolved } = this;
  4340. if (!resolved)
  4341. return noop;
  4342. const { animation } = resolved;
  4343. attachTimeline(animation, timeline);
  4344. }
  4345. return noop;
  4346. }
  4347. play() {
  4348. if (this.isStopped)
  4349. return;
  4350. const { resolved } = this;
  4351. if (!resolved)
  4352. return;
  4353. const { animation } = resolved;
  4354. if (animation.playState === "finished") {
  4355. this.updateFinishedPromise();
  4356. }
  4357. animation.play();
  4358. }
  4359. pause() {
  4360. const { resolved } = this;
  4361. if (!resolved)
  4362. return;
  4363. const { animation } = resolved;
  4364. animation.pause();
  4365. }
  4366. stop() {
  4367. this.resolver.cancel();
  4368. this.isStopped = true;
  4369. if (this.state === "idle")
  4370. return;
  4371. this.resolveFinishedPromise();
  4372. this.updateFinishedPromise();
  4373. const { resolved } = this;
  4374. if (!resolved)
  4375. return;
  4376. const { animation, keyframes, duration, type, ease, times } = resolved;
  4377. if (animation.playState === "idle" ||
  4378. animation.playState === "finished") {
  4379. return;
  4380. }
  4381. /**
  4382. * WAAPI doesn't natively have any interruption capabilities.
  4383. *
  4384. * Rather than read commited styles back out of the DOM, we can
  4385. * create a renderless JS animation and sample it twice to calculate
  4386. * its current value, "previous" value, and therefore allow
  4387. * Motion to calculate velocity for any subsequent animation.
  4388. */
  4389. if (this.time) {
  4390. const { motionValue, onUpdate, onComplete, element, ...options } = this.options;
  4391. const sampleAnimation = new MainThreadAnimation({
  4392. ...options,
  4393. keyframes,
  4394. duration,
  4395. type,
  4396. ease,
  4397. times,
  4398. isGenerator: true,
  4399. });
  4400. const sampleTime = secondsToMilliseconds(this.time);
  4401. motionValue.setWithVelocity(sampleAnimation.sample(sampleTime - sampleDelta).value, sampleAnimation.sample(sampleTime).value, sampleDelta);
  4402. }
  4403. const { onStop } = this.options;
  4404. onStop && onStop();
  4405. this.cancel();
  4406. }
  4407. complete() {
  4408. const { resolved } = this;
  4409. if (!resolved)
  4410. return;
  4411. resolved.animation.finish();
  4412. }
  4413. cancel() {
  4414. const { resolved } = this;
  4415. if (!resolved)
  4416. return;
  4417. resolved.animation.cancel();
  4418. }
  4419. static supports(options) {
  4420. const { motionValue, name, repeatDelay, repeatType, damping, type } = options;
  4421. if (!motionValue ||
  4422. !motionValue.owner ||
  4423. !(motionValue.owner.current instanceof HTMLElement)) {
  4424. return false;
  4425. }
  4426. const { onUpdate, transformTemplate } = motionValue.owner.getProps();
  4427. return (supportsWaapi() &&
  4428. name &&
  4429. acceleratedValues.has(name) &&
  4430. (name !== "transform" || !transformTemplate) &&
  4431. /**
  4432. * If we're outputting values to onUpdate then we can't use WAAPI as there's
  4433. * no way to read the value from WAAPI every frame.
  4434. */
  4435. !onUpdate &&
  4436. !repeatDelay &&
  4437. repeatType !== "mirror" &&
  4438. damping !== 0 &&
  4439. type !== "inertia");
  4440. }
  4441. }
  4442. const underDampedSpring = {
  4443. type: "spring",
  4444. stiffness: 500,
  4445. damping: 25,
  4446. restSpeed: 10,
  4447. };
  4448. const criticallyDampedSpring = (target) => ({
  4449. type: "spring",
  4450. stiffness: 550,
  4451. damping: target === 0 ? 2 * Math.sqrt(550) : 30,
  4452. restSpeed: 10,
  4453. });
  4454. const keyframesTransition = {
  4455. type: "keyframes",
  4456. duration: 0.8,
  4457. };
  4458. /**
  4459. * Default easing curve is a slightly shallower version of
  4460. * the default browser easing curve.
  4461. */
  4462. const ease = {
  4463. type: "keyframes",
  4464. ease: [0.25, 0.1, 0.35, 1],
  4465. duration: 0.3,
  4466. };
  4467. const getDefaultTransition = (valueKey, { keyframes }) => {
  4468. if (keyframes.length > 2) {
  4469. return keyframesTransition;
  4470. }
  4471. else if (transformProps.has(valueKey)) {
  4472. return valueKey.startsWith("scale")
  4473. ? criticallyDampedSpring(keyframes[1])
  4474. : underDampedSpring;
  4475. }
  4476. return ease;
  4477. };
  4478. /**
  4479. * Decide whether a transition is defined on a given Transition.
  4480. * This filters out orchestration options and returns true
  4481. * if any options are left.
  4482. */
  4483. function isTransitionDefined({ when, delay: _delay, delayChildren, staggerChildren, staggerDirection, repeat, repeatType, repeatDelay, from, elapsed, ...transition }) {
  4484. return !!Object.keys(transition).length;
  4485. }
  4486. const animateMotionValue = (name, value, target, transition = {}, element, isHandoff) => (onComplete) => {
  4487. const valueTransition = getValueTransition$1(transition, name) || {};
  4488. /**
  4489. * Most transition values are currently completely overwritten by value-specific
  4490. * transitions. In the future it'd be nicer to blend these transitions. But for now
  4491. * delay actually does inherit from the root transition if not value-specific.
  4492. */
  4493. const delay = valueTransition.delay || transition.delay || 0;
  4494. /**
  4495. * Elapsed isn't a public transition option but can be passed through from
  4496. * optimized appear effects in milliseconds.
  4497. */
  4498. let { elapsed = 0 } = transition;
  4499. elapsed = elapsed - secondsToMilliseconds(delay);
  4500. let options = {
  4501. keyframes: Array.isArray(target) ? target : [null, target],
  4502. ease: "easeOut",
  4503. velocity: value.getVelocity(),
  4504. ...valueTransition,
  4505. delay: -elapsed,
  4506. onUpdate: (v) => {
  4507. value.set(v);
  4508. valueTransition.onUpdate && valueTransition.onUpdate(v);
  4509. },
  4510. onComplete: () => {
  4511. onComplete();
  4512. valueTransition.onComplete && valueTransition.onComplete();
  4513. },
  4514. name,
  4515. motionValue: value,
  4516. element: isHandoff ? undefined : element,
  4517. };
  4518. /**
  4519. * If there's no transition defined for this value, we can generate
  4520. * unique transition settings for this value.
  4521. */
  4522. if (!isTransitionDefined(valueTransition)) {
  4523. options = {
  4524. ...options,
  4525. ...getDefaultTransition(name, options),
  4526. };
  4527. }
  4528. /**
  4529. * Both WAAPI and our internal animation functions use durations
  4530. * as defined by milliseconds, while our external API defines them
  4531. * as seconds.
  4532. */
  4533. if (options.duration) {
  4534. options.duration = secondsToMilliseconds(options.duration);
  4535. }
  4536. if (options.repeatDelay) {
  4537. options.repeatDelay = secondsToMilliseconds(options.repeatDelay);
  4538. }
  4539. if (options.from !== undefined) {
  4540. options.keyframes[0] = options.from;
  4541. }
  4542. let shouldSkip = false;
  4543. if (options.type === false ||
  4544. (options.duration === 0 && !options.repeatDelay)) {
  4545. options.duration = 0;
  4546. if (options.delay === 0) {
  4547. shouldSkip = true;
  4548. }
  4549. }
  4550. if (instantAnimationState.current ||
  4551. MotionGlobalConfig.skipAnimations) {
  4552. shouldSkip = true;
  4553. options.duration = 0;
  4554. options.delay = 0;
  4555. }
  4556. /**
  4557. * If the transition type or easing has been explicitly set by the user
  4558. * then we don't want to allow flattening the animation.
  4559. */
  4560. options.allowFlatten = !valueTransition.type && !valueTransition.ease;
  4561. /**
  4562. * If we can or must skip creating the animation, and apply only
  4563. * the final keyframe, do so. We also check once keyframes are resolved but
  4564. * this early check prevents the need to create an animation at all.
  4565. */
  4566. if (shouldSkip && !isHandoff && value.get() !== undefined) {
  4567. const finalKeyframe = getFinalKeyframe(options.keyframes, valueTransition);
  4568. if (finalKeyframe !== undefined) {
  4569. frame.update(() => {
  4570. options.onUpdate(finalKeyframe);
  4571. options.onComplete();
  4572. });
  4573. // We still want to return some animation controls here rather
  4574. // than returning undefined
  4575. return new GroupAnimationWithThen([]);
  4576. }
  4577. }
  4578. /**
  4579. * Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via
  4580. * WAAPI. Therefore, this animation must be JS to ensure it runs "under" the
  4581. * optimised animation.
  4582. */
  4583. if (!isHandoff && AcceleratedAnimation.supports(options)) {
  4584. return new AcceleratedAnimation(options);
  4585. }
  4586. else {
  4587. return new MainThreadAnimation(options);
  4588. }
  4589. };
  4590. function animateSingleValue(value, keyframes, options) {
  4591. const motionValue$1 = isMotionValue(value) ? value : motionValue(value);
  4592. motionValue$1.start(animateMotionValue("", motionValue$1, keyframes, options));
  4593. return motionValue$1.animation;
  4594. }
  4595. /**
  4596. * Convert camelCase to dash-case properties.
  4597. */
  4598. const camelToDash = (str) => str.replace(/([a-z])([A-Z])/gu, "$1-$2").toLowerCase();
  4599. const optimizedAppearDataId = "framerAppearId";
  4600. const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId);
  4601. function getOptimisedAppearId(visualElement) {
  4602. return visualElement.props[optimizedAppearDataAttribute];
  4603. }
  4604. function isSVGElement(element) {
  4605. return element instanceof SVGElement && element.tagName !== "svg";
  4606. }
  4607. const compareByDepth = (a, b) => a.depth - b.depth;
  4608. class FlatTree {
  4609. constructor() {
  4610. this.children = [];
  4611. this.isDirty = false;
  4612. }
  4613. add(child) {
  4614. addUniqueItem(this.children, child);
  4615. this.isDirty = true;
  4616. }
  4617. remove(child) {
  4618. removeItem(this.children, child);
  4619. this.isDirty = true;
  4620. }
  4621. forEach(callback) {
  4622. this.isDirty && this.children.sort(compareByDepth);
  4623. this.isDirty = false;
  4624. this.children.forEach(callback);
  4625. }
  4626. }
  4627. /**
  4628. * Timeout defined in ms
  4629. */
  4630. function delay(callback, timeout) {
  4631. const start = time.now();
  4632. const checkElapsed = ({ timestamp }) => {
  4633. const elapsed = timestamp - start;
  4634. if (elapsed >= timeout) {
  4635. cancelFrame(checkElapsed);
  4636. callback(elapsed - timeout);
  4637. }
  4638. };
  4639. frame.read(checkElapsed, true);
  4640. return () => cancelFrame(checkElapsed);
  4641. }
  4642. const isKeyframesTarget = (v) => {
  4643. return Array.isArray(v);
  4644. };
  4645. const isCustomValue = (v) => {
  4646. return Boolean(v && typeof v === "object" && v.mix && v.toValue);
  4647. };
  4648. const resolveFinalValueInKeyframes = (v) => {
  4649. // TODO maybe throw if v.length - 1 is placeholder token?
  4650. return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v;
  4651. };
  4652. /**
  4653. * If the provided value is a MotionValue, this returns the actual value, otherwise just the value itself
  4654. *
  4655. * TODO: Remove and move to library
  4656. */
  4657. function resolveMotionValue(value) {
  4658. const unwrappedValue = isMotionValue(value) ? value.get() : value;
  4659. return isCustomValue(unwrappedValue)
  4660. ? unwrappedValue.toValue()
  4661. : unwrappedValue;
  4662. }
  4663. const borders = ["TopLeft", "TopRight", "BottomLeft", "BottomRight"];
  4664. const numBorders = borders.length;
  4665. const asNumber$1 = (value) => typeof value === "string" ? parseFloat(value) : value;
  4666. const isPx = (value) => typeof value === "number" || px.test(value);
  4667. function mixValues(target, follow, lead, progress, shouldCrossfadeOpacity, isOnlyMember) {
  4668. if (shouldCrossfadeOpacity) {
  4669. target.opacity = mixNumber$1(0, lead.opacity ?? 1, easeCrossfadeIn(progress));
  4670. target.opacityExit = mixNumber$1(follow.opacity ?? 1, 0, easeCrossfadeOut(progress));
  4671. }
  4672. else if (isOnlyMember) {
  4673. target.opacity = mixNumber$1(follow.opacity ?? 1, lead.opacity ?? 1, progress);
  4674. }
  4675. /**
  4676. * Mix border radius
  4677. */
  4678. for (let i = 0; i < numBorders; i++) {
  4679. const borderLabel = `border${borders[i]}Radius`;
  4680. let followRadius = getRadius(follow, borderLabel);
  4681. let leadRadius = getRadius(lead, borderLabel);
  4682. if (followRadius === undefined && leadRadius === undefined)
  4683. continue;
  4684. followRadius || (followRadius = 0);
  4685. leadRadius || (leadRadius = 0);
  4686. const canMix = followRadius === 0 ||
  4687. leadRadius === 0 ||
  4688. isPx(followRadius) === isPx(leadRadius);
  4689. if (canMix) {
  4690. target[borderLabel] = Math.max(mixNumber$1(asNumber$1(followRadius), asNumber$1(leadRadius), progress), 0);
  4691. if (percent.test(leadRadius) || percent.test(followRadius)) {
  4692. target[borderLabel] += "%";
  4693. }
  4694. }
  4695. else {
  4696. target[borderLabel] = leadRadius;
  4697. }
  4698. }
  4699. /**
  4700. * Mix rotation
  4701. */
  4702. if (follow.rotate || lead.rotate) {
  4703. target.rotate = mixNumber$1(follow.rotate || 0, lead.rotate || 0, progress);
  4704. }
  4705. }
  4706. function getRadius(values, radiusName) {
  4707. return values[radiusName] !== undefined
  4708. ? values[radiusName]
  4709. : values.borderRadius;
  4710. }
  4711. // /**
  4712. // * We only want to mix the background color if there's a follow element
  4713. // * that we're not crossfading opacity between. For instance with switch
  4714. // * AnimateSharedLayout animations, this helps the illusion of a continuous
  4715. // * element being animated but also cuts down on the number of paints triggered
  4716. // * for elements where opacity is doing that work for us.
  4717. // */
  4718. // if (
  4719. // !hasFollowElement &&
  4720. // latestLeadValues.backgroundColor &&
  4721. // latestFollowValues.backgroundColor
  4722. // ) {
  4723. // /**
  4724. // * This isn't ideal performance-wise as mixColor is creating a new function every frame.
  4725. // * We could probably create a mixer that runs at the start of the animation but
  4726. // * the idea behind the crossfader is that it runs dynamically between two potentially
  4727. // * changing targets (ie opacity or borderRadius may be animating independently via variants)
  4728. // */
  4729. // leadState.backgroundColor = followState.backgroundColor = mixColor(
  4730. // latestFollowValues.backgroundColor as string,
  4731. // latestLeadValues.backgroundColor as string
  4732. // )(p)
  4733. // }
  4734. const easeCrossfadeIn = /*@__PURE__*/ compress(0, 0.5, circOut);
  4735. const easeCrossfadeOut = /*@__PURE__*/ compress(0.5, 0.95, noop);
  4736. function compress(min, max, easing) {
  4737. return (p) => {
  4738. // Could replace ifs with clamp
  4739. if (p < min)
  4740. return 0;
  4741. if (p > max)
  4742. return 1;
  4743. return easing(progress(min, max, p));
  4744. };
  4745. }
  4746. /**
  4747. * Reset an axis to the provided origin box.
  4748. *
  4749. * This is a mutative operation.
  4750. */
  4751. function copyAxisInto(axis, originAxis) {
  4752. axis.min = originAxis.min;
  4753. axis.max = originAxis.max;
  4754. }
  4755. /**
  4756. * Reset a box to the provided origin box.
  4757. *
  4758. * This is a mutative operation.
  4759. */
  4760. function copyBoxInto(box, originBox) {
  4761. copyAxisInto(box.x, originBox.x);
  4762. copyAxisInto(box.y, originBox.y);
  4763. }
  4764. /**
  4765. * Reset a delta to the provided origin box.
  4766. *
  4767. * This is a mutative operation.
  4768. */
  4769. function copyAxisDeltaInto(delta, originDelta) {
  4770. delta.translate = originDelta.translate;
  4771. delta.scale = originDelta.scale;
  4772. delta.originPoint = originDelta.originPoint;
  4773. delta.origin = originDelta.origin;
  4774. }
  4775. function isIdentityScale(scale) {
  4776. return scale === undefined || scale === 1;
  4777. }
  4778. function hasScale({ scale, scaleX, scaleY }) {
  4779. return (!isIdentityScale(scale) ||
  4780. !isIdentityScale(scaleX) ||
  4781. !isIdentityScale(scaleY));
  4782. }
  4783. function hasTransform(values) {
  4784. return (hasScale(values) ||
  4785. has2DTranslate(values) ||
  4786. values.z ||
  4787. values.rotate ||
  4788. values.rotateX ||
  4789. values.rotateY ||
  4790. values.skewX ||
  4791. values.skewY);
  4792. }
  4793. function has2DTranslate(values) {
  4794. return is2DTranslate(values.x) || is2DTranslate(values.y);
  4795. }
  4796. function is2DTranslate(value) {
  4797. return value && value !== "0%";
  4798. }
  4799. /**
  4800. * Scales a point based on a factor and an originPoint
  4801. */
  4802. function scalePoint(point, scale, originPoint) {
  4803. const distanceFromOrigin = point - originPoint;
  4804. const scaled = scale * distanceFromOrigin;
  4805. return originPoint + scaled;
  4806. }
  4807. /**
  4808. * Applies a translate/scale delta to a point
  4809. */
  4810. function applyPointDelta(point, translate, scale, originPoint, boxScale) {
  4811. if (boxScale !== undefined) {
  4812. point = scalePoint(point, boxScale, originPoint);
  4813. }
  4814. return scalePoint(point, scale, originPoint) + translate;
  4815. }
  4816. /**
  4817. * Applies a translate/scale delta to an axis
  4818. */
  4819. function applyAxisDelta(axis, translate = 0, scale = 1, originPoint, boxScale) {
  4820. axis.min = applyPointDelta(axis.min, translate, scale, originPoint, boxScale);
  4821. axis.max = applyPointDelta(axis.max, translate, scale, originPoint, boxScale);
  4822. }
  4823. /**
  4824. * Applies a translate/scale delta to a box
  4825. */
  4826. function applyBoxDelta(box, { x, y }) {
  4827. applyAxisDelta(box.x, x.translate, x.scale, x.originPoint);
  4828. applyAxisDelta(box.y, y.translate, y.scale, y.originPoint);
  4829. }
  4830. const TREE_SCALE_SNAP_MIN = 0.999999999999;
  4831. const TREE_SCALE_SNAP_MAX = 1.0000000000001;
  4832. /**
  4833. * Apply a tree of deltas to a box. We do this to calculate the effect of all the transforms
  4834. * in a tree upon our box before then calculating how to project it into our desired viewport-relative box
  4835. *
  4836. * This is the final nested loop within updateLayoutDelta for future refactoring
  4837. */
  4838. function applyTreeDeltas(box, treeScale, treePath, isSharedTransition = false) {
  4839. const treeLength = treePath.length;
  4840. if (!treeLength)
  4841. return;
  4842. // Reset the treeScale
  4843. treeScale.x = treeScale.y = 1;
  4844. let node;
  4845. let delta;
  4846. for (let i = 0; i < treeLength; i++) {
  4847. node = treePath[i];
  4848. delta = node.projectionDelta;
  4849. /**
  4850. * TODO: Prefer to remove this, but currently we have motion components with
  4851. * display: contents in Framer.
  4852. */
  4853. const { visualElement } = node.options;
  4854. if (visualElement &&
  4855. visualElement.props.style &&
  4856. visualElement.props.style.display === "contents") {
  4857. continue;
  4858. }
  4859. if (isSharedTransition &&
  4860. node.options.layoutScroll &&
  4861. node.scroll &&
  4862. node !== node.root) {
  4863. transformBox(box, {
  4864. x: -node.scroll.offset.x,
  4865. y: -node.scroll.offset.y,
  4866. });
  4867. }
  4868. if (delta) {
  4869. // Incoporate each ancestor's scale into a culmulative treeScale for this component
  4870. treeScale.x *= delta.x.scale;
  4871. treeScale.y *= delta.y.scale;
  4872. // Apply each ancestor's calculated delta into this component's recorded layout box
  4873. applyBoxDelta(box, delta);
  4874. }
  4875. if (isSharedTransition && hasTransform(node.latestValues)) {
  4876. transformBox(box, node.latestValues);
  4877. }
  4878. }
  4879. /**
  4880. * Snap tree scale back to 1 if it's within a non-perceivable threshold.
  4881. * This will help reduce useless scales getting rendered.
  4882. */
  4883. if (treeScale.x < TREE_SCALE_SNAP_MAX &&
  4884. treeScale.x > TREE_SCALE_SNAP_MIN) {
  4885. treeScale.x = 1.0;
  4886. }
  4887. if (treeScale.y < TREE_SCALE_SNAP_MAX &&
  4888. treeScale.y > TREE_SCALE_SNAP_MIN) {
  4889. treeScale.y = 1.0;
  4890. }
  4891. }
  4892. function translateAxis(axis, distance) {
  4893. axis.min = axis.min + distance;
  4894. axis.max = axis.max + distance;
  4895. }
  4896. /**
  4897. * Apply a transform to an axis from the latest resolved motion values.
  4898. * This function basically acts as a bridge between a flat motion value map
  4899. * and applyAxisDelta
  4900. */
  4901. function transformAxis(axis, axisTranslate, axisScale, boxScale, axisOrigin = 0.5) {
  4902. const originPoint = mixNumber$1(axis.min, axis.max, axisOrigin);
  4903. // Apply the axis delta to the final axis
  4904. applyAxisDelta(axis, axisTranslate, axisScale, originPoint, boxScale);
  4905. }
  4906. /**
  4907. * Apply a transform to a box from the latest resolved motion values.
  4908. */
  4909. function transformBox(box, transform) {
  4910. transformAxis(box.x, transform.x, transform.scaleX, transform.scale, transform.originX);
  4911. transformAxis(box.y, transform.y, transform.scaleY, transform.scale, transform.originY);
  4912. }
  4913. /**
  4914. * Remove a delta from a point. This is essentially the steps of applyPointDelta in reverse
  4915. */
  4916. function removePointDelta(point, translate, scale, originPoint, boxScale) {
  4917. point -= translate;
  4918. point = scalePoint(point, 1 / scale, originPoint);
  4919. if (boxScale !== undefined) {
  4920. point = scalePoint(point, 1 / boxScale, originPoint);
  4921. }
  4922. return point;
  4923. }
  4924. /**
  4925. * Remove a delta from an axis. This is essentially the steps of applyAxisDelta in reverse
  4926. */
  4927. function removeAxisDelta(axis, translate = 0, scale = 1, origin = 0.5, boxScale, originAxis = axis, sourceAxis = axis) {
  4928. if (percent.test(translate)) {
  4929. translate = parseFloat(translate);
  4930. const relativeProgress = mixNumber$1(sourceAxis.min, sourceAxis.max, translate / 100);
  4931. translate = relativeProgress - sourceAxis.min;
  4932. }
  4933. if (typeof translate !== "number")
  4934. return;
  4935. let originPoint = mixNumber$1(originAxis.min, originAxis.max, origin);
  4936. if (axis === originAxis)
  4937. originPoint -= translate;
  4938. axis.min = removePointDelta(axis.min, translate, scale, originPoint, boxScale);
  4939. axis.max = removePointDelta(axis.max, translate, scale, originPoint, boxScale);
  4940. }
  4941. /**
  4942. * Remove a transforms from an axis. This is essentially the steps of applyAxisTransforms in reverse
  4943. * and acts as a bridge between motion values and removeAxisDelta
  4944. */
  4945. function removeAxisTransforms(axis, transforms, [key, scaleKey, originKey], origin, sourceAxis) {
  4946. removeAxisDelta(axis, transforms[key], transforms[scaleKey], transforms[originKey], transforms.scale, origin, sourceAxis);
  4947. }
  4948. /**
  4949. * The names of the motion values we want to apply as translation, scale and origin.
  4950. */
  4951. const xKeys = ["x", "scaleX", "originX"];
  4952. const yKeys = ["y", "scaleY", "originY"];
  4953. /**
  4954. * Remove a transforms from an box. This is essentially the steps of applyAxisBox in reverse
  4955. * and acts as a bridge between motion values and removeAxisDelta
  4956. */
  4957. function removeBoxTransforms(box, transforms, originBox, sourceBox) {
  4958. removeAxisTransforms(box.x, transforms, xKeys, originBox ? originBox.x : undefined, sourceBox ? sourceBox.x : undefined);
  4959. removeAxisTransforms(box.y, transforms, yKeys, originBox ? originBox.y : undefined, sourceBox ? sourceBox.y : undefined);
  4960. }
  4961. const createAxisDelta = () => ({
  4962. translate: 0,
  4963. scale: 1,
  4964. origin: 0,
  4965. originPoint: 0,
  4966. });
  4967. const createDelta = () => ({
  4968. x: createAxisDelta(),
  4969. y: createAxisDelta(),
  4970. });
  4971. const createAxis = () => ({ min: 0, max: 0 });
  4972. const createBox = () => ({
  4973. x: createAxis(),
  4974. y: createAxis(),
  4975. });
  4976. function isAxisDeltaZero(delta) {
  4977. return delta.translate === 0 && delta.scale === 1;
  4978. }
  4979. function isDeltaZero(delta) {
  4980. return isAxisDeltaZero(delta.x) && isAxisDeltaZero(delta.y);
  4981. }
  4982. function axisEquals(a, b) {
  4983. return a.min === b.min && a.max === b.max;
  4984. }
  4985. function boxEquals(a, b) {
  4986. return axisEquals(a.x, b.x) && axisEquals(a.y, b.y);
  4987. }
  4988. function axisEqualsRounded(a, b) {
  4989. return (Math.round(a.min) === Math.round(b.min) &&
  4990. Math.round(a.max) === Math.round(b.max));
  4991. }
  4992. function boxEqualsRounded(a, b) {
  4993. return axisEqualsRounded(a.x, b.x) && axisEqualsRounded(a.y, b.y);
  4994. }
  4995. function aspectRatio(box) {
  4996. return calcLength(box.x) / calcLength(box.y);
  4997. }
  4998. function axisDeltaEquals(a, b) {
  4999. return (a.translate === b.translate &&
  5000. a.scale === b.scale &&
  5001. a.originPoint === b.originPoint);
  5002. }
  5003. class NodeStack {
  5004. constructor() {
  5005. this.members = [];
  5006. }
  5007. add(node) {
  5008. addUniqueItem(this.members, node);
  5009. node.scheduleRender();
  5010. }
  5011. remove(node) {
  5012. removeItem(this.members, node);
  5013. if (node === this.prevLead) {
  5014. this.prevLead = undefined;
  5015. }
  5016. if (node === this.lead) {
  5017. const prevLead = this.members[this.members.length - 1];
  5018. if (prevLead) {
  5019. this.promote(prevLead);
  5020. }
  5021. }
  5022. }
  5023. relegate(node) {
  5024. const indexOfNode = this.members.findIndex((member) => node === member);
  5025. if (indexOfNode === 0)
  5026. return false;
  5027. /**
  5028. * Find the next projection node that is present
  5029. */
  5030. let prevLead;
  5031. for (let i = indexOfNode; i >= 0; i--) {
  5032. const member = this.members[i];
  5033. if (member.isPresent !== false) {
  5034. prevLead = member;
  5035. break;
  5036. }
  5037. }
  5038. if (prevLead) {
  5039. this.promote(prevLead);
  5040. return true;
  5041. }
  5042. else {
  5043. return false;
  5044. }
  5045. }
  5046. promote(node, preserveFollowOpacity) {
  5047. const prevLead = this.lead;
  5048. if (node === prevLead)
  5049. return;
  5050. this.prevLead = prevLead;
  5051. this.lead = node;
  5052. node.show();
  5053. if (prevLead) {
  5054. prevLead.instance && prevLead.scheduleRender();
  5055. node.scheduleRender();
  5056. node.resumeFrom = prevLead;
  5057. if (preserveFollowOpacity) {
  5058. node.resumeFrom.preserveOpacity = true;
  5059. }
  5060. if (prevLead.snapshot) {
  5061. node.snapshot = prevLead.snapshot;
  5062. node.snapshot.latestValues =
  5063. prevLead.animationValues || prevLead.latestValues;
  5064. }
  5065. if (node.root && node.root.isUpdating) {
  5066. node.isLayoutDirty = true;
  5067. }
  5068. const { crossfade } = node.options;
  5069. if (crossfade === false) {
  5070. prevLead.hide();
  5071. }
  5072. /**
  5073. * TODO:
  5074. * - Test border radius when previous node was deleted
  5075. * - boxShadow mixing
  5076. * - Shared between element A in scrolled container and element B (scroll stays the same or changes)
  5077. * - Shared between element A in transformed container and element B (transform stays the same or changes)
  5078. * - Shared between element A in scrolled page and element B (scroll stays the same or changes)
  5079. * ---
  5080. * - Crossfade opacity of root nodes
  5081. * - layoutId changes after animation
  5082. * - layoutId changes mid animation
  5083. */
  5084. }
  5085. }
  5086. exitAnimationComplete() {
  5087. this.members.forEach((node) => {
  5088. const { options, resumingFrom } = node;
  5089. options.onExitComplete && options.onExitComplete();
  5090. if (resumingFrom) {
  5091. resumingFrom.options.onExitComplete &&
  5092. resumingFrom.options.onExitComplete();
  5093. }
  5094. });
  5095. }
  5096. scheduleRender() {
  5097. this.members.forEach((node) => {
  5098. node.instance && node.scheduleRender(false);
  5099. });
  5100. }
  5101. /**
  5102. * Clear any leads that have been removed this render to prevent them from being
  5103. * used in future animations and to prevent memory leaks
  5104. */
  5105. removeLeadSnapshot() {
  5106. if (this.lead && this.lead.snapshot) {
  5107. this.lead.snapshot = undefined;
  5108. }
  5109. }
  5110. }
  5111. const scaleCorrectors = {};
  5112. function addScaleCorrector(correctors) {
  5113. for (const key in correctors) {
  5114. scaleCorrectors[key] = correctors[key];
  5115. if (isCSSVariableName(key)) {
  5116. scaleCorrectors[key].isCSSVariable = true;
  5117. }
  5118. }
  5119. }
  5120. function buildProjectionTransform(delta, treeScale, latestTransform) {
  5121. let transform = "";
  5122. /**
  5123. * The translations we use to calculate are always relative to the viewport coordinate space.
  5124. * But when we apply scales, we also scale the coordinate space of an element and its children.
  5125. * For instance if we have a treeScale (the culmination of all parent scales) of 0.5 and we need
  5126. * to move an element 100 pixels, we actually need to move it 200 in within that scaled space.
  5127. */
  5128. const xTranslate = delta.x.translate / treeScale.x;
  5129. const yTranslate = delta.y.translate / treeScale.y;
  5130. const zTranslate = latestTransform?.z || 0;
  5131. if (xTranslate || yTranslate || zTranslate) {
  5132. transform = `translate3d(${xTranslate}px, ${yTranslate}px, ${zTranslate}px) `;
  5133. }
  5134. /**
  5135. * Apply scale correction for the tree transform.
  5136. * This will apply scale to the screen-orientated axes.
  5137. */
  5138. if (treeScale.x !== 1 || treeScale.y !== 1) {
  5139. transform += `scale(${1 / treeScale.x}, ${1 / treeScale.y}) `;
  5140. }
  5141. if (latestTransform) {
  5142. const { transformPerspective, rotate, rotateX, rotateY, skewX, skewY } = latestTransform;
  5143. if (transformPerspective)
  5144. transform = `perspective(${transformPerspective}px) ${transform}`;
  5145. if (rotate)
  5146. transform += `rotate(${rotate}deg) `;
  5147. if (rotateX)
  5148. transform += `rotateX(${rotateX}deg) `;
  5149. if (rotateY)
  5150. transform += `rotateY(${rotateY}deg) `;
  5151. if (skewX)
  5152. transform += `skewX(${skewX}deg) `;
  5153. if (skewY)
  5154. transform += `skewY(${skewY}deg) `;
  5155. }
  5156. /**
  5157. * Apply scale to match the size of the element to the size we want it.
  5158. * This will apply scale to the element-orientated axes.
  5159. */
  5160. const elementScaleX = delta.x.scale * treeScale.x;
  5161. const elementScaleY = delta.y.scale * treeScale.y;
  5162. if (elementScaleX !== 1 || elementScaleY !== 1) {
  5163. transform += `scale(${elementScaleX}, ${elementScaleY})`;
  5164. }
  5165. return transform || "none";
  5166. }
  5167. function eachAxis(callback) {
  5168. return [callback("x"), callback("y")];
  5169. }
  5170. /**
  5171. * This should only ever be modified on the client otherwise it'll
  5172. * persist through server requests. If we need instanced states we
  5173. * could lazy-init via root.
  5174. */
  5175. const globalProjectionState = {
  5176. /**
  5177. * Global flag as to whether the tree has animated since the last time
  5178. * we resized the window
  5179. */
  5180. hasAnimatedSinceResize: true,
  5181. /**
  5182. * We set this to true once, on the first update. Any nodes added to the tree beyond that
  5183. * update will be given a `data-projection-id` attribute.
  5184. */
  5185. hasEverUpdated: false,
  5186. };
  5187. const transformAxes = ["", "X", "Y", "Z"];
  5188. const hiddenVisibility = { visibility: "hidden" };
  5189. /**
  5190. * We use 1000 as the animation target as 0-1000 maps better to pixels than 0-1
  5191. * which has a noticeable difference in spring animations
  5192. */
  5193. const animationTarget = 1000;
  5194. let id$2 = 0;
  5195. function resetDistortingTransform(key, visualElement, values, sharedAnimationValues) {
  5196. const { latestValues } = visualElement;
  5197. // Record the distorting transform and then temporarily set it to 0
  5198. if (latestValues[key]) {
  5199. values[key] = latestValues[key];
  5200. visualElement.setStaticValue(key, 0);
  5201. if (sharedAnimationValues) {
  5202. sharedAnimationValues[key] = 0;
  5203. }
  5204. }
  5205. }
  5206. function cancelTreeOptimisedTransformAnimations(projectionNode) {
  5207. projectionNode.hasCheckedOptimisedAppear = true;
  5208. if (projectionNode.root === projectionNode)
  5209. return;
  5210. const { visualElement } = projectionNode.options;
  5211. if (!visualElement)
  5212. return;
  5213. const appearId = getOptimisedAppearId(visualElement);
  5214. if (window.MotionHasOptimisedAnimation(appearId, "transform")) {
  5215. const { layout, layoutId } = projectionNode.options;
  5216. window.MotionCancelOptimisedAnimation(appearId, "transform", frame, !(layout || layoutId));
  5217. }
  5218. const { parent } = projectionNode;
  5219. if (parent && !parent.hasCheckedOptimisedAppear) {
  5220. cancelTreeOptimisedTransformAnimations(parent);
  5221. }
  5222. }
  5223. function createProjectionNode$1({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) {
  5224. return class ProjectionNode {
  5225. constructor(latestValues = {}, parent = defaultParent?.()) {
  5226. /**
  5227. * A unique ID generated for every projection node.
  5228. */
  5229. this.id = id$2++;
  5230. /**
  5231. * An id that represents a unique session instigated by startUpdate.
  5232. */
  5233. this.animationId = 0;
  5234. /**
  5235. * A Set containing all this component's children. This is used to iterate
  5236. * through the children.
  5237. *
  5238. * TODO: This could be faster to iterate as a flat array stored on the root node.
  5239. */
  5240. this.children = new Set();
  5241. /**
  5242. * Options for the node. We use this to configure what kind of layout animations
  5243. * we should perform (if any).
  5244. */
  5245. this.options = {};
  5246. /**
  5247. * We use this to detect when its safe to shut down part of a projection tree.
  5248. * We have to keep projecting children for scale correction and relative projection
  5249. * until all their parents stop performing layout animations.
  5250. */
  5251. this.isTreeAnimating = false;
  5252. this.isAnimationBlocked = false;
  5253. /**
  5254. * Flag to true if we think this layout has been changed. We can't always know this,
  5255. * currently we set it to true every time a component renders, or if it has a layoutDependency
  5256. * if that has changed between renders. Additionally, components can be grouped by LayoutGroup
  5257. * and if one node is dirtied, they all are.
  5258. */
  5259. this.isLayoutDirty = false;
  5260. /**
  5261. * Flag to true if we think the projection calculations for this node needs
  5262. * recalculating as a result of an updated transform or layout animation.
  5263. */
  5264. this.isProjectionDirty = false;
  5265. /**
  5266. * Flag to true if the layout *or* transform has changed. This then gets propagated
  5267. * throughout the projection tree, forcing any element below to recalculate on the next frame.
  5268. */
  5269. this.isSharedProjectionDirty = false;
  5270. /**
  5271. * Flag transform dirty. This gets propagated throughout the whole tree but is only
  5272. * respected by shared nodes.
  5273. */
  5274. this.isTransformDirty = false;
  5275. /**
  5276. * Block layout updates for instant layout transitions throughout the tree.
  5277. */
  5278. this.updateManuallyBlocked = false;
  5279. this.updateBlockedByResize = false;
  5280. /**
  5281. * Set to true between the start of the first `willUpdate` call and the end of the `didUpdate`
  5282. * call.
  5283. */
  5284. this.isUpdating = false;
  5285. /**
  5286. * If this is an SVG element we currently disable projection transforms
  5287. */
  5288. this.isSVG = false;
  5289. /**
  5290. * Flag to true (during promotion) if a node doing an instant layout transition needs to reset
  5291. * its projection styles.
  5292. */
  5293. this.needsReset = false;
  5294. /**
  5295. * Flags whether this node should have its transform reset prior to measuring.
  5296. */
  5297. this.shouldResetTransform = false;
  5298. /**
  5299. * Store whether this node has been checked for optimised appear animations. As
  5300. * effects fire bottom-up, and we want to look up the tree for appear animations,
  5301. * this makes sure we only check each path once, stopping at nodes that
  5302. * have already been checked.
  5303. */
  5304. this.hasCheckedOptimisedAppear = false;
  5305. /**
  5306. * An object representing the calculated contextual/accumulated/tree scale.
  5307. * This will be used to scale calculcated projection transforms, as these are
  5308. * calculated in screen-space but need to be scaled for elements to layoutly
  5309. * make it to their calculated destinations.
  5310. *
  5311. * TODO: Lazy-init
  5312. */
  5313. this.treeScale = { x: 1, y: 1 };
  5314. /**
  5315. *
  5316. */
  5317. this.eventHandlers = new Map();
  5318. this.hasTreeAnimated = false;
  5319. // Note: Currently only running on root node
  5320. this.updateScheduled = false;
  5321. this.scheduleUpdate = () => this.update();
  5322. this.projectionUpdateScheduled = false;
  5323. this.checkUpdateFailed = () => {
  5324. if (this.isUpdating) {
  5325. this.isUpdating = false;
  5326. this.clearAllSnapshots();
  5327. }
  5328. };
  5329. /**
  5330. * This is a multi-step process as shared nodes might be of different depths. Nodes
  5331. * are sorted by depth order, so we need to resolve the entire tree before moving to
  5332. * the next step.
  5333. */
  5334. this.updateProjection = () => {
  5335. this.projectionUpdateScheduled = false;
  5336. this.nodes.forEach(propagateDirtyNodes);
  5337. this.nodes.forEach(resolveTargetDelta);
  5338. this.nodes.forEach(calcProjection);
  5339. this.nodes.forEach(cleanDirtyNodes);
  5340. };
  5341. /**
  5342. * Frame calculations
  5343. */
  5344. this.resolvedRelativeTargetAt = 0.0;
  5345. this.hasProjected = false;
  5346. this.isVisible = true;
  5347. this.animationProgress = 0;
  5348. /**
  5349. * Shared layout
  5350. */
  5351. // TODO Only running on root node
  5352. this.sharedNodes = new Map();
  5353. this.latestValues = latestValues;
  5354. this.root = parent ? parent.root || parent : this;
  5355. this.path = parent ? [...parent.path, parent] : [];
  5356. this.parent = parent;
  5357. this.depth = parent ? parent.depth + 1 : 0;
  5358. for (let i = 0; i < this.path.length; i++) {
  5359. this.path[i].shouldResetTransform = true;
  5360. }
  5361. if (this.root === this)
  5362. this.nodes = new FlatTree();
  5363. }
  5364. addEventListener(name, handler) {
  5365. if (!this.eventHandlers.has(name)) {
  5366. this.eventHandlers.set(name, new SubscriptionManager());
  5367. }
  5368. return this.eventHandlers.get(name).add(handler);
  5369. }
  5370. notifyListeners(name, ...args) {
  5371. const subscriptionManager = this.eventHandlers.get(name);
  5372. subscriptionManager && subscriptionManager.notify(...args);
  5373. }
  5374. hasListeners(name) {
  5375. return this.eventHandlers.has(name);
  5376. }
  5377. /**
  5378. * Lifecycles
  5379. */
  5380. mount(instance, isLayoutDirty = this.root.hasTreeAnimated) {
  5381. if (this.instance)
  5382. return;
  5383. this.isSVG = isSVGElement(instance);
  5384. this.instance = instance;
  5385. const { layoutId, layout, visualElement } = this.options;
  5386. if (visualElement && !visualElement.current) {
  5387. visualElement.mount(instance);
  5388. }
  5389. this.root.nodes.add(this);
  5390. this.parent && this.parent.children.add(this);
  5391. if (isLayoutDirty && (layout || layoutId)) {
  5392. this.isLayoutDirty = true;
  5393. }
  5394. if (attachResizeListener) {
  5395. let cancelDelay;
  5396. const resizeUnblockUpdate = () => (this.root.updateBlockedByResize = false);
  5397. attachResizeListener(instance, () => {
  5398. this.root.updateBlockedByResize = true;
  5399. cancelDelay && cancelDelay();
  5400. cancelDelay = delay(resizeUnblockUpdate, 250);
  5401. if (globalProjectionState.hasAnimatedSinceResize) {
  5402. globalProjectionState.hasAnimatedSinceResize = false;
  5403. this.nodes.forEach(finishAnimation);
  5404. }
  5405. });
  5406. }
  5407. if (layoutId) {
  5408. this.root.registerSharedNode(layoutId, this);
  5409. }
  5410. // Only register the handler if it requires layout animation
  5411. if (this.options.animate !== false &&
  5412. visualElement &&
  5413. (layoutId || layout)) {
  5414. this.addEventListener("didUpdate", ({ delta, hasLayoutChanged, hasRelativeLayoutChanged, layout: newLayout, }) => {
  5415. if (this.isTreeAnimationBlocked()) {
  5416. this.target = undefined;
  5417. this.relativeTarget = undefined;
  5418. return;
  5419. }
  5420. // TODO: Check here if an animation exists
  5421. const layoutTransition = this.options.transition ||
  5422. visualElement.getDefaultTransition() ||
  5423. defaultLayoutTransition;
  5424. const { onLayoutAnimationStart, onLayoutAnimationComplete, } = visualElement.getProps();
  5425. /**
  5426. * The target layout of the element might stay the same,
  5427. * but its position relative to its parent has changed.
  5428. */
  5429. const hasTargetChanged = !this.targetLayout ||
  5430. !boxEqualsRounded(this.targetLayout, newLayout);
  5431. /*
  5432. * Note: Disabled to fix relative animations always triggering new
  5433. * layout animations. If this causes further issues, we can try
  5434. * a different approach to detecting relative target changes.
  5435. */
  5436. // || hasRelativeLayoutChanged
  5437. /**
  5438. * If the layout hasn't seemed to have changed, it might be that the
  5439. * element is visually in the same place in the document but its position
  5440. * relative to its parent has indeed changed. So here we check for that.
  5441. */
  5442. const hasOnlyRelativeTargetChanged = !hasLayoutChanged && hasRelativeLayoutChanged;
  5443. if (this.options.layoutRoot ||
  5444. this.resumeFrom ||
  5445. hasOnlyRelativeTargetChanged ||
  5446. (hasLayoutChanged &&
  5447. (hasTargetChanged || !this.currentAnimation))) {
  5448. if (this.resumeFrom) {
  5449. this.resumingFrom = this.resumeFrom;
  5450. this.resumingFrom.resumingFrom = undefined;
  5451. }
  5452. this.setAnimationOrigin(delta, hasOnlyRelativeTargetChanged);
  5453. const animationOptions = {
  5454. ...getValueTransition$1(layoutTransition, "layout"),
  5455. onPlay: onLayoutAnimationStart,
  5456. onComplete: onLayoutAnimationComplete,
  5457. };
  5458. if (visualElement.shouldReduceMotion ||
  5459. this.options.layoutRoot) {
  5460. animationOptions.delay = 0;
  5461. animationOptions.type = false;
  5462. }
  5463. this.startAnimation(animationOptions);
  5464. }
  5465. else {
  5466. /**
  5467. * If the layout hasn't changed and we have an animation that hasn't started yet,
  5468. * finish it immediately. Otherwise it will be animating from a location
  5469. * that was probably never commited to screen and look like a jumpy box.
  5470. */
  5471. if (!hasLayoutChanged) {
  5472. finishAnimation(this);
  5473. }
  5474. if (this.isLead() && this.options.onExitComplete) {
  5475. this.options.onExitComplete();
  5476. }
  5477. }
  5478. this.targetLayout = newLayout;
  5479. });
  5480. }
  5481. }
  5482. unmount() {
  5483. this.options.layoutId && this.willUpdate();
  5484. this.root.nodes.remove(this);
  5485. const stack = this.getStack();
  5486. stack && stack.remove(this);
  5487. this.parent && this.parent.children.delete(this);
  5488. this.instance = undefined;
  5489. cancelFrame(this.updateProjection);
  5490. }
  5491. // only on the root
  5492. blockUpdate() {
  5493. this.updateManuallyBlocked = true;
  5494. }
  5495. unblockUpdate() {
  5496. this.updateManuallyBlocked = false;
  5497. }
  5498. isUpdateBlocked() {
  5499. return this.updateManuallyBlocked || this.updateBlockedByResize;
  5500. }
  5501. isTreeAnimationBlocked() {
  5502. return (this.isAnimationBlocked ||
  5503. (this.parent && this.parent.isTreeAnimationBlocked()) ||
  5504. false);
  5505. }
  5506. // Note: currently only running on root node
  5507. startUpdate() {
  5508. if (this.isUpdateBlocked())
  5509. return;
  5510. this.isUpdating = true;
  5511. this.nodes && this.nodes.forEach(resetSkewAndRotation);
  5512. this.animationId++;
  5513. }
  5514. getTransformTemplate() {
  5515. const { visualElement } = this.options;
  5516. return visualElement && visualElement.getProps().transformTemplate;
  5517. }
  5518. willUpdate(shouldNotifyListeners = true) {
  5519. this.root.hasTreeAnimated = true;
  5520. if (this.root.isUpdateBlocked()) {
  5521. this.options.onExitComplete && this.options.onExitComplete();
  5522. return;
  5523. }
  5524. /**
  5525. * If we're running optimised appear animations then these must be
  5526. * cancelled before measuring the DOM. This is so we can measure
  5527. * the true layout of the element rather than the WAAPI animation
  5528. * which will be unaffected by the resetSkewAndRotate step.
  5529. *
  5530. * Note: This is a DOM write. Worst case scenario is this is sandwiched
  5531. * between other snapshot reads which will cause unnecessary style recalculations.
  5532. * This has to happen here though, as we don't yet know which nodes will need
  5533. * snapshots in startUpdate(), but we only want to cancel optimised animations
  5534. * if a layout animation measurement is actually going to be affected by them.
  5535. */
  5536. if (window.MotionCancelOptimisedAnimation &&
  5537. !this.hasCheckedOptimisedAppear) {
  5538. cancelTreeOptimisedTransformAnimations(this);
  5539. }
  5540. !this.root.isUpdating && this.root.startUpdate();
  5541. if (this.isLayoutDirty)
  5542. return;
  5543. this.isLayoutDirty = true;
  5544. for (let i = 0; i < this.path.length; i++) {
  5545. const node = this.path[i];
  5546. node.shouldResetTransform = true;
  5547. node.updateScroll("snapshot");
  5548. if (node.options.layoutRoot) {
  5549. node.willUpdate(false);
  5550. }
  5551. }
  5552. const { layoutId, layout } = this.options;
  5553. if (layoutId === undefined && !layout)
  5554. return;
  5555. const transformTemplate = this.getTransformTemplate();
  5556. this.prevTransformTemplateValue = transformTemplate
  5557. ? transformTemplate(this.latestValues, "")
  5558. : undefined;
  5559. this.updateSnapshot();
  5560. shouldNotifyListeners && this.notifyListeners("willUpdate");
  5561. }
  5562. update() {
  5563. this.updateScheduled = false;
  5564. const updateWasBlocked = this.isUpdateBlocked();
  5565. // When doing an instant transition, we skip the layout update,
  5566. // but should still clean up the measurements so that the next
  5567. // snapshot could be taken correctly.
  5568. if (updateWasBlocked) {
  5569. this.unblockUpdate();
  5570. this.clearAllSnapshots();
  5571. this.nodes.forEach(clearMeasurements);
  5572. return;
  5573. }
  5574. if (!this.isUpdating) {
  5575. this.nodes.forEach(clearIsLayoutDirty);
  5576. }
  5577. this.isUpdating = false;
  5578. /**
  5579. * Write
  5580. */
  5581. this.nodes.forEach(resetTransformStyle);
  5582. /**
  5583. * Read ==================
  5584. */
  5585. // Update layout measurements of updated children
  5586. this.nodes.forEach(updateLayout);
  5587. /**
  5588. * Write
  5589. */
  5590. // Notify listeners that the layout is updated
  5591. this.nodes.forEach(notifyLayoutUpdate);
  5592. this.clearAllSnapshots();
  5593. /**
  5594. * Manually flush any pending updates. Ideally
  5595. * we could leave this to the following requestAnimationFrame but this seems
  5596. * to leave a flash of incorrectly styled content.
  5597. */
  5598. const now = time.now();
  5599. frameData.delta = clamp(0, 1000 / 60, now - frameData.timestamp);
  5600. frameData.timestamp = now;
  5601. frameData.isProcessing = true;
  5602. frameSteps.update.process(frameData);
  5603. frameSteps.preRender.process(frameData);
  5604. frameSteps.render.process(frameData);
  5605. frameData.isProcessing = false;
  5606. }
  5607. didUpdate() {
  5608. if (!this.updateScheduled) {
  5609. this.updateScheduled = true;
  5610. microtask.read(this.scheduleUpdate);
  5611. }
  5612. }
  5613. clearAllSnapshots() {
  5614. this.nodes.forEach(clearSnapshot);
  5615. this.sharedNodes.forEach(removeLeadSnapshots);
  5616. }
  5617. scheduleUpdateProjection() {
  5618. if (!this.projectionUpdateScheduled) {
  5619. this.projectionUpdateScheduled = true;
  5620. frame.preRender(this.updateProjection, false, true);
  5621. }
  5622. }
  5623. scheduleCheckAfterUnmount() {
  5624. /**
  5625. * If the unmounting node is in a layoutGroup and did trigger a willUpdate,
  5626. * we manually call didUpdate to give a chance to the siblings to animate.
  5627. * Otherwise, cleanup all snapshots to prevents future nodes from reusing them.
  5628. */
  5629. frame.postRender(() => {
  5630. if (this.isLayoutDirty) {
  5631. this.root.didUpdate();
  5632. }
  5633. else {
  5634. this.root.checkUpdateFailed();
  5635. }
  5636. });
  5637. }
  5638. /**
  5639. * Update measurements
  5640. */
  5641. updateSnapshot() {
  5642. if (this.snapshot || !this.instance)
  5643. return;
  5644. this.snapshot = this.measure();
  5645. if (this.snapshot &&
  5646. !calcLength(this.snapshot.measuredBox.x) &&
  5647. !calcLength(this.snapshot.measuredBox.y)) {
  5648. this.snapshot = undefined;
  5649. }
  5650. }
  5651. updateLayout() {
  5652. if (!this.instance)
  5653. return;
  5654. // TODO: Incorporate into a forwarded scroll offset
  5655. this.updateScroll();
  5656. if (!(this.options.alwaysMeasureLayout && this.isLead()) &&
  5657. !this.isLayoutDirty) {
  5658. return;
  5659. }
  5660. /**
  5661. * When a node is mounted, it simply resumes from the prevLead's
  5662. * snapshot instead of taking a new one, but the ancestors scroll
  5663. * might have updated while the prevLead is unmounted. We need to
  5664. * update the scroll again to make sure the layout we measure is
  5665. * up to date.
  5666. */
  5667. if (this.resumeFrom && !this.resumeFrom.instance) {
  5668. for (let i = 0; i < this.path.length; i++) {
  5669. const node = this.path[i];
  5670. node.updateScroll();
  5671. }
  5672. }
  5673. const prevLayout = this.layout;
  5674. this.layout = this.measure(false);
  5675. this.layoutCorrected = createBox();
  5676. this.isLayoutDirty = false;
  5677. this.projectionDelta = undefined;
  5678. this.notifyListeners("measure", this.layout.layoutBox);
  5679. const { visualElement } = this.options;
  5680. visualElement &&
  5681. visualElement.notify("LayoutMeasure", this.layout.layoutBox, prevLayout ? prevLayout.layoutBox : undefined);
  5682. }
  5683. updateScroll(phase = "measure") {
  5684. let needsMeasurement = Boolean(this.options.layoutScroll && this.instance);
  5685. if (this.scroll &&
  5686. this.scroll.animationId === this.root.animationId &&
  5687. this.scroll.phase === phase) {
  5688. needsMeasurement = false;
  5689. }
  5690. if (needsMeasurement) {
  5691. const isRoot = checkIsScrollRoot(this.instance);
  5692. this.scroll = {
  5693. animationId: this.root.animationId,
  5694. phase,
  5695. isRoot,
  5696. offset: measureScroll(this.instance),
  5697. wasRoot: this.scroll ? this.scroll.isRoot : isRoot,
  5698. };
  5699. }
  5700. }
  5701. resetTransform() {
  5702. if (!resetTransform)
  5703. return;
  5704. const isResetRequested = this.isLayoutDirty ||
  5705. this.shouldResetTransform ||
  5706. this.options.alwaysMeasureLayout;
  5707. const hasProjection = this.projectionDelta && !isDeltaZero(this.projectionDelta);
  5708. const transformTemplate = this.getTransformTemplate();
  5709. const transformTemplateValue = transformTemplate
  5710. ? transformTemplate(this.latestValues, "")
  5711. : undefined;
  5712. const transformTemplateHasChanged = transformTemplateValue !== this.prevTransformTemplateValue;
  5713. if (isResetRequested &&
  5714. (hasProjection ||
  5715. hasTransform(this.latestValues) ||
  5716. transformTemplateHasChanged)) {
  5717. resetTransform(this.instance, transformTemplateValue);
  5718. this.shouldResetTransform = false;
  5719. this.scheduleRender();
  5720. }
  5721. }
  5722. measure(removeTransform = true) {
  5723. const pageBox = this.measurePageBox();
  5724. let layoutBox = this.removeElementScroll(pageBox);
  5725. /**
  5726. * Measurements taken during the pre-render stage
  5727. * still have transforms applied so we remove them
  5728. * via calculation.
  5729. */
  5730. if (removeTransform) {
  5731. layoutBox = this.removeTransform(layoutBox);
  5732. }
  5733. roundBox(layoutBox);
  5734. return {
  5735. animationId: this.root.animationId,
  5736. measuredBox: pageBox,
  5737. layoutBox,
  5738. latestValues: {},
  5739. source: this.id,
  5740. };
  5741. }
  5742. measurePageBox() {
  5743. const { visualElement } = this.options;
  5744. if (!visualElement)
  5745. return createBox();
  5746. const box = visualElement.measureViewportBox();
  5747. const wasInScrollRoot = this.scroll?.wasRoot || this.path.some(checkNodeWasScrollRoot);
  5748. if (!wasInScrollRoot) {
  5749. // Remove viewport scroll to give page-relative coordinates
  5750. const { scroll } = this.root;
  5751. if (scroll) {
  5752. translateAxis(box.x, scroll.offset.x);
  5753. translateAxis(box.y, scroll.offset.y);
  5754. }
  5755. }
  5756. return box;
  5757. }
  5758. removeElementScroll(box) {
  5759. const boxWithoutScroll = createBox();
  5760. copyBoxInto(boxWithoutScroll, box);
  5761. if (this.scroll?.wasRoot) {
  5762. return boxWithoutScroll;
  5763. }
  5764. /**
  5765. * Performance TODO: Keep a cumulative scroll offset down the tree
  5766. * rather than loop back up the path.
  5767. */
  5768. for (let i = 0; i < this.path.length; i++) {
  5769. const node = this.path[i];
  5770. const { scroll, options } = node;
  5771. if (node !== this.root && scroll && options.layoutScroll) {
  5772. /**
  5773. * If this is a new scroll root, we want to remove all previous scrolls
  5774. * from the viewport box.
  5775. */
  5776. if (scroll.wasRoot) {
  5777. copyBoxInto(boxWithoutScroll, box);
  5778. }
  5779. translateAxis(boxWithoutScroll.x, scroll.offset.x);
  5780. translateAxis(boxWithoutScroll.y, scroll.offset.y);
  5781. }
  5782. }
  5783. return boxWithoutScroll;
  5784. }
  5785. applyTransform(box, transformOnly = false) {
  5786. const withTransforms = createBox();
  5787. copyBoxInto(withTransforms, box);
  5788. for (let i = 0; i < this.path.length; i++) {
  5789. const node = this.path[i];
  5790. if (!transformOnly &&
  5791. node.options.layoutScroll &&
  5792. node.scroll &&
  5793. node !== node.root) {
  5794. transformBox(withTransforms, {
  5795. x: -node.scroll.offset.x,
  5796. y: -node.scroll.offset.y,
  5797. });
  5798. }
  5799. if (!hasTransform(node.latestValues))
  5800. continue;
  5801. transformBox(withTransforms, node.latestValues);
  5802. }
  5803. if (hasTransform(this.latestValues)) {
  5804. transformBox(withTransforms, this.latestValues);
  5805. }
  5806. return withTransforms;
  5807. }
  5808. removeTransform(box) {
  5809. const boxWithoutTransform = createBox();
  5810. copyBoxInto(boxWithoutTransform, box);
  5811. for (let i = 0; i < this.path.length; i++) {
  5812. const node = this.path[i];
  5813. if (!node.instance)
  5814. continue;
  5815. if (!hasTransform(node.latestValues))
  5816. continue;
  5817. hasScale(node.latestValues) && node.updateSnapshot();
  5818. const sourceBox = createBox();
  5819. const nodeBox = node.measurePageBox();
  5820. copyBoxInto(sourceBox, nodeBox);
  5821. removeBoxTransforms(boxWithoutTransform, node.latestValues, node.snapshot ? node.snapshot.layoutBox : undefined, sourceBox);
  5822. }
  5823. if (hasTransform(this.latestValues)) {
  5824. removeBoxTransforms(boxWithoutTransform, this.latestValues);
  5825. }
  5826. return boxWithoutTransform;
  5827. }
  5828. setTargetDelta(delta) {
  5829. this.targetDelta = delta;
  5830. this.root.scheduleUpdateProjection();
  5831. this.isProjectionDirty = true;
  5832. }
  5833. setOptions(options) {
  5834. this.options = {
  5835. ...this.options,
  5836. ...options,
  5837. crossfade: options.crossfade !== undefined ? options.crossfade : true,
  5838. };
  5839. }
  5840. clearMeasurements() {
  5841. this.scroll = undefined;
  5842. this.layout = undefined;
  5843. this.snapshot = undefined;
  5844. this.prevTransformTemplateValue = undefined;
  5845. this.targetDelta = undefined;
  5846. this.target = undefined;
  5847. this.isLayoutDirty = false;
  5848. }
  5849. forceRelativeParentToResolveTarget() {
  5850. if (!this.relativeParent)
  5851. return;
  5852. /**
  5853. * If the parent target isn't up-to-date, force it to update.
  5854. * This is an unfortunate de-optimisation as it means any updating relative
  5855. * projection will cause all the relative parents to recalculate back
  5856. * up the tree.
  5857. */
  5858. if (this.relativeParent.resolvedRelativeTargetAt !==
  5859. frameData.timestamp) {
  5860. this.relativeParent.resolveTargetDelta(true);
  5861. }
  5862. }
  5863. resolveTargetDelta(forceRecalculation = false) {
  5864. /**
  5865. * Once the dirty status of nodes has been spread through the tree, we also
  5866. * need to check if we have a shared node of a different depth that has itself
  5867. * been dirtied.
  5868. */
  5869. const lead = this.getLead();
  5870. this.isProjectionDirty || (this.isProjectionDirty = lead.isProjectionDirty);
  5871. this.isTransformDirty || (this.isTransformDirty = lead.isTransformDirty);
  5872. this.isSharedProjectionDirty || (this.isSharedProjectionDirty = lead.isSharedProjectionDirty);
  5873. const isShared = Boolean(this.resumingFrom) || this !== lead;
  5874. /**
  5875. * We don't use transform for this step of processing so we don't
  5876. * need to check whether any nodes have changed transform.
  5877. */
  5878. const canSkip = !(forceRecalculation ||
  5879. (isShared && this.isSharedProjectionDirty) ||
  5880. this.isProjectionDirty ||
  5881. this.parent?.isProjectionDirty ||
  5882. this.attemptToResolveRelativeTarget ||
  5883. this.root.updateBlockedByResize);
  5884. if (canSkip)
  5885. return;
  5886. const { layout, layoutId } = this.options;
  5887. /**
  5888. * If we have no layout, we can't perform projection, so early return
  5889. */
  5890. if (!this.layout || !(layout || layoutId))
  5891. return;
  5892. this.resolvedRelativeTargetAt = frameData.timestamp;
  5893. /**
  5894. * If we don't have a targetDelta but do have a layout, we can attempt to resolve
  5895. * a relativeParent. This will allow a component to perform scale correction
  5896. * even if no animation has started.
  5897. */
  5898. if (!this.targetDelta && !this.relativeTarget) {
  5899. const relativeParent = this.getClosestProjectingParent();
  5900. if (relativeParent &&
  5901. relativeParent.layout &&
  5902. this.animationProgress !== 1) {
  5903. this.relativeParent = relativeParent;
  5904. this.forceRelativeParentToResolveTarget();
  5905. this.relativeTarget = createBox();
  5906. this.relativeTargetOrigin = createBox();
  5907. calcRelativePosition(this.relativeTargetOrigin, this.layout.layoutBox, relativeParent.layout.layoutBox);
  5908. copyBoxInto(this.relativeTarget, this.relativeTargetOrigin);
  5909. }
  5910. else {
  5911. this.relativeParent = this.relativeTarget = undefined;
  5912. }
  5913. }
  5914. /**
  5915. * If we have no relative target or no target delta our target isn't valid
  5916. * for this frame.
  5917. */
  5918. if (!this.relativeTarget && !this.targetDelta)
  5919. return;
  5920. /**
  5921. * Lazy-init target data structure
  5922. */
  5923. if (!this.target) {
  5924. this.target = createBox();
  5925. this.targetWithTransforms = createBox();
  5926. }
  5927. /**
  5928. * If we've got a relative box for this component, resolve it into a target relative to the parent.
  5929. */
  5930. if (this.relativeTarget &&
  5931. this.relativeTargetOrigin &&
  5932. this.relativeParent &&
  5933. this.relativeParent.target) {
  5934. this.forceRelativeParentToResolveTarget();
  5935. calcRelativeBox(this.target, this.relativeTarget, this.relativeParent.target);
  5936. /**
  5937. * If we've only got a targetDelta, resolve it into a target
  5938. */
  5939. }
  5940. else if (this.targetDelta) {
  5941. if (Boolean(this.resumingFrom)) {
  5942. // TODO: This is creating a new object every frame
  5943. this.target = this.applyTransform(this.layout.layoutBox);
  5944. }
  5945. else {
  5946. copyBoxInto(this.target, this.layout.layoutBox);
  5947. }
  5948. applyBoxDelta(this.target, this.targetDelta);
  5949. }
  5950. else {
  5951. /**
  5952. * If no target, use own layout as target
  5953. */
  5954. copyBoxInto(this.target, this.layout.layoutBox);
  5955. }
  5956. /**
  5957. * If we've been told to attempt to resolve a relative target, do so.
  5958. */
  5959. if (this.attemptToResolveRelativeTarget) {
  5960. this.attemptToResolveRelativeTarget = false;
  5961. const relativeParent = this.getClosestProjectingParent();
  5962. if (relativeParent &&
  5963. Boolean(relativeParent.resumingFrom) ===
  5964. Boolean(this.resumingFrom) &&
  5965. !relativeParent.options.layoutScroll &&
  5966. relativeParent.target &&
  5967. this.animationProgress !== 1) {
  5968. this.relativeParent = relativeParent;
  5969. this.forceRelativeParentToResolveTarget();
  5970. this.relativeTarget = createBox();
  5971. this.relativeTargetOrigin = createBox();
  5972. calcRelativePosition(this.relativeTargetOrigin, this.target, relativeParent.target);
  5973. copyBoxInto(this.relativeTarget, this.relativeTargetOrigin);
  5974. }
  5975. else {
  5976. this.relativeParent = this.relativeTarget = undefined;
  5977. }
  5978. }
  5979. }
  5980. getClosestProjectingParent() {
  5981. if (!this.parent ||
  5982. hasScale(this.parent.latestValues) ||
  5983. has2DTranslate(this.parent.latestValues)) {
  5984. return undefined;
  5985. }
  5986. if (this.parent.isProjecting()) {
  5987. return this.parent;
  5988. }
  5989. else {
  5990. return this.parent.getClosestProjectingParent();
  5991. }
  5992. }
  5993. isProjecting() {
  5994. return Boolean((this.relativeTarget ||
  5995. this.targetDelta ||
  5996. this.options.layoutRoot) &&
  5997. this.layout);
  5998. }
  5999. calcProjection() {
  6000. const lead = this.getLead();
  6001. const isShared = Boolean(this.resumingFrom) || this !== lead;
  6002. let canSkip = true;
  6003. /**
  6004. * If this is a normal layout animation and neither this node nor its nearest projecting
  6005. * is dirty then we can't skip.
  6006. */
  6007. if (this.isProjectionDirty || this.parent?.isProjectionDirty) {
  6008. canSkip = false;
  6009. }
  6010. /**
  6011. * If this is a shared layout animation and this node's shared projection is dirty then
  6012. * we can't skip.
  6013. */
  6014. if (isShared &&
  6015. (this.isSharedProjectionDirty || this.isTransformDirty)) {
  6016. canSkip = false;
  6017. }
  6018. /**
  6019. * If we have resolved the target this frame we must recalculate the
  6020. * projection to ensure it visually represents the internal calculations.
  6021. */
  6022. if (this.resolvedRelativeTargetAt === frameData.timestamp) {
  6023. canSkip = false;
  6024. }
  6025. if (canSkip)
  6026. return;
  6027. const { layout, layoutId } = this.options;
  6028. /**
  6029. * If this section of the tree isn't animating we can
  6030. * delete our target sources for the following frame.
  6031. */
  6032. this.isTreeAnimating = Boolean((this.parent && this.parent.isTreeAnimating) ||
  6033. this.currentAnimation ||
  6034. this.pendingAnimation);
  6035. if (!this.isTreeAnimating) {
  6036. this.targetDelta = this.relativeTarget = undefined;
  6037. }
  6038. if (!this.layout || !(layout || layoutId))
  6039. return;
  6040. /**
  6041. * Reset the corrected box with the latest values from box, as we're then going
  6042. * to perform mutative operations on it.
  6043. */
  6044. copyBoxInto(this.layoutCorrected, this.layout.layoutBox);
  6045. /**
  6046. * Record previous tree scales before updating.
  6047. */
  6048. const prevTreeScaleX = this.treeScale.x;
  6049. const prevTreeScaleY = this.treeScale.y;
  6050. /**
  6051. * Apply all the parent deltas to this box to produce the corrected box. This
  6052. * is the layout box, as it will appear on screen as a result of the transforms of its parents.
  6053. */
  6054. applyTreeDeltas(this.layoutCorrected, this.treeScale, this.path, isShared);
  6055. /**
  6056. * If this layer needs to perform scale correction but doesn't have a target,
  6057. * use the layout as the target.
  6058. */
  6059. if (lead.layout &&
  6060. !lead.target &&
  6061. (this.treeScale.x !== 1 || this.treeScale.y !== 1)) {
  6062. lead.target = lead.layout.layoutBox;
  6063. lead.targetWithTransforms = createBox();
  6064. }
  6065. const { target } = lead;
  6066. if (!target) {
  6067. /**
  6068. * If we don't have a target to project into, but we were previously
  6069. * projecting, we want to remove the stored transform and schedule
  6070. * a render to ensure the elements reflect the removed transform.
  6071. */
  6072. if (this.prevProjectionDelta) {
  6073. this.createProjectionDeltas();
  6074. this.scheduleRender();
  6075. }
  6076. return;
  6077. }
  6078. if (!this.projectionDelta || !this.prevProjectionDelta) {
  6079. this.createProjectionDeltas();
  6080. }
  6081. else {
  6082. copyAxisDeltaInto(this.prevProjectionDelta.x, this.projectionDelta.x);
  6083. copyAxisDeltaInto(this.prevProjectionDelta.y, this.projectionDelta.y);
  6084. }
  6085. /**
  6086. * Update the delta between the corrected box and the target box before user-set transforms were applied.
  6087. * This will allow us to calculate the corrected borderRadius and boxShadow to compensate
  6088. * for our layout reprojection, but still allow them to be scaled correctly by the user.
  6089. * It might be that to simplify this we may want to accept that user-set scale is also corrected
  6090. * and we wouldn't have to keep and calc both deltas, OR we could support a user setting
  6091. * to allow people to choose whether these styles are corrected based on just the
  6092. * layout reprojection or the final bounding box.
  6093. */
  6094. calcBoxDelta(this.projectionDelta, this.layoutCorrected, target, this.latestValues);
  6095. if (this.treeScale.x !== prevTreeScaleX ||
  6096. this.treeScale.y !== prevTreeScaleY ||
  6097. !axisDeltaEquals(this.projectionDelta.x, this.prevProjectionDelta.x) ||
  6098. !axisDeltaEquals(this.projectionDelta.y, this.prevProjectionDelta.y)) {
  6099. this.hasProjected = true;
  6100. this.scheduleRender();
  6101. this.notifyListeners("projectionUpdate", target);
  6102. }
  6103. }
  6104. hide() {
  6105. this.isVisible = false;
  6106. // TODO: Schedule render
  6107. }
  6108. show() {
  6109. this.isVisible = true;
  6110. // TODO: Schedule render
  6111. }
  6112. scheduleRender(notifyAll = true) {
  6113. this.options.visualElement?.scheduleRender();
  6114. if (notifyAll) {
  6115. const stack = this.getStack();
  6116. stack && stack.scheduleRender();
  6117. }
  6118. if (this.resumingFrom && !this.resumingFrom.instance) {
  6119. this.resumingFrom = undefined;
  6120. }
  6121. }
  6122. createProjectionDeltas() {
  6123. this.prevProjectionDelta = createDelta();
  6124. this.projectionDelta = createDelta();
  6125. this.projectionDeltaWithTransform = createDelta();
  6126. }
  6127. setAnimationOrigin(delta, hasOnlyRelativeTargetChanged = false) {
  6128. const snapshot = this.snapshot;
  6129. const snapshotLatestValues = snapshot
  6130. ? snapshot.latestValues
  6131. : {};
  6132. const mixedValues = { ...this.latestValues };
  6133. const targetDelta = createDelta();
  6134. if (!this.relativeParent ||
  6135. !this.relativeParent.options.layoutRoot) {
  6136. this.relativeTarget = this.relativeTargetOrigin = undefined;
  6137. }
  6138. this.attemptToResolveRelativeTarget = !hasOnlyRelativeTargetChanged;
  6139. const relativeLayout = createBox();
  6140. const snapshotSource = snapshot ? snapshot.source : undefined;
  6141. const layoutSource = this.layout ? this.layout.source : undefined;
  6142. const isSharedLayoutAnimation = snapshotSource !== layoutSource;
  6143. const stack = this.getStack();
  6144. const isOnlyMember = !stack || stack.members.length <= 1;
  6145. const shouldCrossfadeOpacity = Boolean(isSharedLayoutAnimation &&
  6146. !isOnlyMember &&
  6147. this.options.crossfade === true &&
  6148. !this.path.some(hasOpacityCrossfade));
  6149. this.animationProgress = 0;
  6150. let prevRelativeTarget;
  6151. this.mixTargetDelta = (latest) => {
  6152. const progress = latest / 1000;
  6153. mixAxisDelta(targetDelta.x, delta.x, progress);
  6154. mixAxisDelta(targetDelta.y, delta.y, progress);
  6155. this.setTargetDelta(targetDelta);
  6156. if (this.relativeTarget &&
  6157. this.relativeTargetOrigin &&
  6158. this.layout &&
  6159. this.relativeParent &&
  6160. this.relativeParent.layout) {
  6161. calcRelativePosition(relativeLayout, this.layout.layoutBox, this.relativeParent.layout.layoutBox);
  6162. mixBox(this.relativeTarget, this.relativeTargetOrigin, relativeLayout, progress);
  6163. /**
  6164. * If this is an unchanged relative target we can consider the
  6165. * projection not dirty.
  6166. */
  6167. if (prevRelativeTarget &&
  6168. boxEquals(this.relativeTarget, prevRelativeTarget)) {
  6169. this.isProjectionDirty = false;
  6170. }
  6171. if (!prevRelativeTarget)
  6172. prevRelativeTarget = createBox();
  6173. copyBoxInto(prevRelativeTarget, this.relativeTarget);
  6174. }
  6175. if (isSharedLayoutAnimation) {
  6176. this.animationValues = mixedValues;
  6177. mixValues(mixedValues, snapshotLatestValues, this.latestValues, progress, shouldCrossfadeOpacity, isOnlyMember);
  6178. }
  6179. this.root.scheduleUpdateProjection();
  6180. this.scheduleRender();
  6181. this.animationProgress = progress;
  6182. };
  6183. this.mixTargetDelta(this.options.layoutRoot ? 1000 : 0);
  6184. }
  6185. startAnimation(options) {
  6186. this.notifyListeners("animationStart");
  6187. this.currentAnimation && this.currentAnimation.stop();
  6188. if (this.resumingFrom && this.resumingFrom.currentAnimation) {
  6189. this.resumingFrom.currentAnimation.stop();
  6190. }
  6191. if (this.pendingAnimation) {
  6192. cancelFrame(this.pendingAnimation);
  6193. this.pendingAnimation = undefined;
  6194. }
  6195. /**
  6196. * Start the animation in the next frame to have a frame with progress 0,
  6197. * where the target is the same as when the animation started, so we can
  6198. * calculate the relative positions correctly for instant transitions.
  6199. */
  6200. this.pendingAnimation = frame.update(() => {
  6201. globalProjectionState.hasAnimatedSinceResize = true;
  6202. this.currentAnimation = animateSingleValue(0, animationTarget, {
  6203. ...options,
  6204. onUpdate: (latest) => {
  6205. this.mixTargetDelta(latest);
  6206. options.onUpdate && options.onUpdate(latest);
  6207. },
  6208. onStop: () => {
  6209. },
  6210. onComplete: () => {
  6211. options.onComplete && options.onComplete();
  6212. this.completeAnimation();
  6213. },
  6214. });
  6215. if (this.resumingFrom) {
  6216. this.resumingFrom.currentAnimation = this.currentAnimation;
  6217. }
  6218. this.pendingAnimation = undefined;
  6219. });
  6220. }
  6221. completeAnimation() {
  6222. if (this.resumingFrom) {
  6223. this.resumingFrom.currentAnimation = undefined;
  6224. this.resumingFrom.preserveOpacity = undefined;
  6225. }
  6226. const stack = this.getStack();
  6227. stack && stack.exitAnimationComplete();
  6228. this.resumingFrom =
  6229. this.currentAnimation =
  6230. this.animationValues =
  6231. undefined;
  6232. this.notifyListeners("animationComplete");
  6233. }
  6234. finishAnimation() {
  6235. if (this.currentAnimation) {
  6236. this.mixTargetDelta && this.mixTargetDelta(animationTarget);
  6237. this.currentAnimation.stop();
  6238. }
  6239. this.completeAnimation();
  6240. }
  6241. applyTransformsToTarget() {
  6242. const lead = this.getLead();
  6243. let { targetWithTransforms, target, layout, latestValues } = lead;
  6244. if (!targetWithTransforms || !target || !layout)
  6245. return;
  6246. /**
  6247. * If we're only animating position, and this element isn't the lead element,
  6248. * then instead of projecting into the lead box we instead want to calculate
  6249. * a new target that aligns the two boxes but maintains the layout shape.
  6250. */
  6251. if (this !== lead &&
  6252. this.layout &&
  6253. layout &&
  6254. shouldAnimatePositionOnly(this.options.animationType, this.layout.layoutBox, layout.layoutBox)) {
  6255. target = this.target || createBox();
  6256. const xLength = calcLength(this.layout.layoutBox.x);
  6257. target.x.min = lead.target.x.min;
  6258. target.x.max = target.x.min + xLength;
  6259. const yLength = calcLength(this.layout.layoutBox.y);
  6260. target.y.min = lead.target.y.min;
  6261. target.y.max = target.y.min + yLength;
  6262. }
  6263. copyBoxInto(targetWithTransforms, target);
  6264. /**
  6265. * Apply the latest user-set transforms to the targetBox to produce the targetBoxFinal.
  6266. * This is the final box that we will then project into by calculating a transform delta and
  6267. * applying it to the corrected box.
  6268. */
  6269. transformBox(targetWithTransforms, latestValues);
  6270. /**
  6271. * Update the delta between the corrected box and the final target box, after
  6272. * user-set transforms are applied to it. This will be used by the renderer to
  6273. * create a transform style that will reproject the element from its layout layout
  6274. * into the desired bounding box.
  6275. */
  6276. calcBoxDelta(this.projectionDeltaWithTransform, this.layoutCorrected, targetWithTransforms, latestValues);
  6277. }
  6278. registerSharedNode(layoutId, node) {
  6279. if (!this.sharedNodes.has(layoutId)) {
  6280. this.sharedNodes.set(layoutId, new NodeStack());
  6281. }
  6282. const stack = this.sharedNodes.get(layoutId);
  6283. stack.add(node);
  6284. const config = node.options.initialPromotionConfig;
  6285. node.promote({
  6286. transition: config ? config.transition : undefined,
  6287. preserveFollowOpacity: config && config.shouldPreserveFollowOpacity
  6288. ? config.shouldPreserveFollowOpacity(node)
  6289. : undefined,
  6290. });
  6291. }
  6292. isLead() {
  6293. const stack = this.getStack();
  6294. return stack ? stack.lead === this : true;
  6295. }
  6296. getLead() {
  6297. const { layoutId } = this.options;
  6298. return layoutId ? this.getStack()?.lead || this : this;
  6299. }
  6300. getPrevLead() {
  6301. const { layoutId } = this.options;
  6302. return layoutId ? this.getStack()?.prevLead : undefined;
  6303. }
  6304. getStack() {
  6305. const { layoutId } = this.options;
  6306. if (layoutId)
  6307. return this.root.sharedNodes.get(layoutId);
  6308. }
  6309. promote({ needsReset, transition, preserveFollowOpacity, } = {}) {
  6310. const stack = this.getStack();
  6311. if (stack)
  6312. stack.promote(this, preserveFollowOpacity);
  6313. if (needsReset) {
  6314. this.projectionDelta = undefined;
  6315. this.needsReset = true;
  6316. }
  6317. if (transition)
  6318. this.setOptions({ transition });
  6319. }
  6320. relegate() {
  6321. const stack = this.getStack();
  6322. if (stack) {
  6323. return stack.relegate(this);
  6324. }
  6325. else {
  6326. return false;
  6327. }
  6328. }
  6329. resetSkewAndRotation() {
  6330. const { visualElement } = this.options;
  6331. if (!visualElement)
  6332. return;
  6333. // If there's no detected skew or rotation values, we can early return without a forced render.
  6334. let hasDistortingTransform = false;
  6335. /**
  6336. * An unrolled check for rotation values. Most elements don't have any rotation and
  6337. * skipping the nested loop and new object creation is 50% faster.
  6338. */
  6339. const { latestValues } = visualElement;
  6340. if (latestValues.z ||
  6341. latestValues.rotate ||
  6342. latestValues.rotateX ||
  6343. latestValues.rotateY ||
  6344. latestValues.rotateZ ||
  6345. latestValues.skewX ||
  6346. latestValues.skewY) {
  6347. hasDistortingTransform = true;
  6348. }
  6349. // If there's no distorting values, we don't need to do any more.
  6350. if (!hasDistortingTransform)
  6351. return;
  6352. const resetValues = {};
  6353. if (latestValues.z) {
  6354. resetDistortingTransform("z", visualElement, resetValues, this.animationValues);
  6355. }
  6356. // Check the skew and rotate value of all axes and reset to 0
  6357. for (let i = 0; i < transformAxes.length; i++) {
  6358. resetDistortingTransform(`rotate${transformAxes[i]}`, visualElement, resetValues, this.animationValues);
  6359. resetDistortingTransform(`skew${transformAxes[i]}`, visualElement, resetValues, this.animationValues);
  6360. }
  6361. // Force a render of this element to apply the transform with all skews and rotations
  6362. // set to 0.
  6363. visualElement.render();
  6364. // Put back all the values we reset
  6365. for (const key in resetValues) {
  6366. visualElement.setStaticValue(key, resetValues[key]);
  6367. if (this.animationValues) {
  6368. this.animationValues[key] = resetValues[key];
  6369. }
  6370. }
  6371. // Schedule a render for the next frame. This ensures we won't visually
  6372. // see the element with the reset rotate value applied.
  6373. visualElement.scheduleRender();
  6374. }
  6375. getProjectionStyles(styleProp) {
  6376. if (!this.instance || this.isSVG)
  6377. return undefined;
  6378. if (!this.isVisible) {
  6379. return hiddenVisibility;
  6380. }
  6381. const styles = {
  6382. visibility: "",
  6383. };
  6384. const transformTemplate = this.getTransformTemplate();
  6385. if (this.needsReset) {
  6386. this.needsReset = false;
  6387. styles.opacity = "";
  6388. styles.pointerEvents =
  6389. resolveMotionValue(styleProp?.pointerEvents) || "";
  6390. styles.transform = transformTemplate
  6391. ? transformTemplate(this.latestValues, "")
  6392. : "none";
  6393. return styles;
  6394. }
  6395. const lead = this.getLead();
  6396. if (!this.projectionDelta || !this.layout || !lead.target) {
  6397. const emptyStyles = {};
  6398. if (this.options.layoutId) {
  6399. emptyStyles.opacity =
  6400. this.latestValues.opacity !== undefined
  6401. ? this.latestValues.opacity
  6402. : 1;
  6403. emptyStyles.pointerEvents =
  6404. resolveMotionValue(styleProp?.pointerEvents) || "";
  6405. }
  6406. if (this.hasProjected && !hasTransform(this.latestValues)) {
  6407. emptyStyles.transform = transformTemplate
  6408. ? transformTemplate({}, "")
  6409. : "none";
  6410. this.hasProjected = false;
  6411. }
  6412. return emptyStyles;
  6413. }
  6414. const valuesToRender = lead.animationValues || lead.latestValues;
  6415. this.applyTransformsToTarget();
  6416. styles.transform = buildProjectionTransform(this.projectionDeltaWithTransform, this.treeScale, valuesToRender);
  6417. if (transformTemplate) {
  6418. styles.transform = transformTemplate(valuesToRender, styles.transform);
  6419. }
  6420. const { x, y } = this.projectionDelta;
  6421. styles.transformOrigin = `${x.origin * 100}% ${y.origin * 100}% 0`;
  6422. if (lead.animationValues) {
  6423. /**
  6424. * If the lead component is animating, assign this either the entering/leaving
  6425. * opacity
  6426. */
  6427. styles.opacity =
  6428. lead === this
  6429. ? valuesToRender.opacity ??
  6430. this.latestValues.opacity ??
  6431. 1
  6432. : this.preserveOpacity
  6433. ? this.latestValues.opacity
  6434. : valuesToRender.opacityExit;
  6435. }
  6436. else {
  6437. /**
  6438. * Or we're not animating at all, set the lead component to its layout
  6439. * opacity and other components to hidden.
  6440. */
  6441. styles.opacity =
  6442. lead === this
  6443. ? valuesToRender.opacity !== undefined
  6444. ? valuesToRender.opacity
  6445. : ""
  6446. : valuesToRender.opacityExit !== undefined
  6447. ? valuesToRender.opacityExit
  6448. : 0;
  6449. }
  6450. /**
  6451. * Apply scale correction
  6452. */
  6453. for (const key in scaleCorrectors) {
  6454. if (valuesToRender[key] === undefined)
  6455. continue;
  6456. const { correct, applyTo, isCSSVariable } = scaleCorrectors[key];
  6457. /**
  6458. * Only apply scale correction to the value if we have an
  6459. * active projection transform. Otherwise these values become
  6460. * vulnerable to distortion if the element changes size without
  6461. * a corresponding layout animation.
  6462. */
  6463. const corrected = styles.transform === "none"
  6464. ? valuesToRender[key]
  6465. : correct(valuesToRender[key], lead);
  6466. if (applyTo) {
  6467. const num = applyTo.length;
  6468. for (let i = 0; i < num; i++) {
  6469. styles[applyTo[i]] = corrected;
  6470. }
  6471. }
  6472. else {
  6473. // If this is a CSS variable, set it directly on the instance.
  6474. // Replacing this function from creating styles to setting them
  6475. // would be a good place to remove per frame object creation
  6476. if (isCSSVariable) {
  6477. this.options.visualElement.renderState.vars[key] = corrected;
  6478. }
  6479. else {
  6480. styles[key] = corrected;
  6481. }
  6482. }
  6483. }
  6484. /**
  6485. * Disable pointer events on follow components. This is to ensure
  6486. * that if a follow component covers a lead component it doesn't block
  6487. * pointer events on the lead.
  6488. */
  6489. if (this.options.layoutId) {
  6490. styles.pointerEvents =
  6491. lead === this
  6492. ? resolveMotionValue(styleProp?.pointerEvents) || ""
  6493. : "none";
  6494. }
  6495. return styles;
  6496. }
  6497. clearSnapshot() {
  6498. this.resumeFrom = this.snapshot = undefined;
  6499. }
  6500. // Only run on root
  6501. resetTree() {
  6502. this.root.nodes.forEach((node) => node.currentAnimation?.stop());
  6503. this.root.nodes.forEach(clearMeasurements);
  6504. this.root.sharedNodes.clear();
  6505. }
  6506. };
  6507. }
  6508. function updateLayout(node) {
  6509. node.updateLayout();
  6510. }
  6511. function notifyLayoutUpdate(node) {
  6512. const snapshot = node.resumeFrom?.snapshot || node.snapshot;
  6513. if (node.isLead() &&
  6514. node.layout &&
  6515. snapshot &&
  6516. node.hasListeners("didUpdate")) {
  6517. const { layoutBox: layout, measuredBox: measuredLayout } = node.layout;
  6518. const { animationType } = node.options;
  6519. const isShared = snapshot.source !== node.layout.source;
  6520. // TODO Maybe we want to also resize the layout snapshot so we don't trigger
  6521. // animations for instance if layout="size" and an element has only changed position
  6522. if (animationType === "size") {
  6523. eachAxis((axis) => {
  6524. const axisSnapshot = isShared
  6525. ? snapshot.measuredBox[axis]
  6526. : snapshot.layoutBox[axis];
  6527. const length = calcLength(axisSnapshot);
  6528. axisSnapshot.min = layout[axis].min;
  6529. axisSnapshot.max = axisSnapshot.min + length;
  6530. });
  6531. }
  6532. else if (shouldAnimatePositionOnly(animationType, snapshot.layoutBox, layout)) {
  6533. eachAxis((axis) => {
  6534. const axisSnapshot = isShared
  6535. ? snapshot.measuredBox[axis]
  6536. : snapshot.layoutBox[axis];
  6537. const length = calcLength(layout[axis]);
  6538. axisSnapshot.max = axisSnapshot.min + length;
  6539. /**
  6540. * Ensure relative target gets resized and rerendererd
  6541. */
  6542. if (node.relativeTarget && !node.currentAnimation) {
  6543. node.isProjectionDirty = true;
  6544. node.relativeTarget[axis].max =
  6545. node.relativeTarget[axis].min + length;
  6546. }
  6547. });
  6548. }
  6549. const layoutDelta = createDelta();
  6550. calcBoxDelta(layoutDelta, layout, snapshot.layoutBox);
  6551. const visualDelta = createDelta();
  6552. if (isShared) {
  6553. calcBoxDelta(visualDelta, node.applyTransform(measuredLayout, true), snapshot.measuredBox);
  6554. }
  6555. else {
  6556. calcBoxDelta(visualDelta, layout, snapshot.layoutBox);
  6557. }
  6558. const hasLayoutChanged = !isDeltaZero(layoutDelta);
  6559. let hasRelativeLayoutChanged = false;
  6560. if (!node.resumeFrom) {
  6561. const relativeParent = node.getClosestProjectingParent();
  6562. /**
  6563. * If the relativeParent is itself resuming from a different element then
  6564. * the relative snapshot is not relavent
  6565. */
  6566. if (relativeParent && !relativeParent.resumeFrom) {
  6567. const { snapshot: parentSnapshot, layout: parentLayout } = relativeParent;
  6568. if (parentSnapshot && parentLayout) {
  6569. const relativeSnapshot = createBox();
  6570. calcRelativePosition(relativeSnapshot, snapshot.layoutBox, parentSnapshot.layoutBox);
  6571. const relativeLayout = createBox();
  6572. calcRelativePosition(relativeLayout, layout, parentLayout.layoutBox);
  6573. if (!boxEqualsRounded(relativeSnapshot, relativeLayout)) {
  6574. hasRelativeLayoutChanged = true;
  6575. }
  6576. if (relativeParent.options.layoutRoot) {
  6577. node.relativeTarget = relativeLayout;
  6578. node.relativeTargetOrigin = relativeSnapshot;
  6579. node.relativeParent = relativeParent;
  6580. }
  6581. }
  6582. }
  6583. }
  6584. node.notifyListeners("didUpdate", {
  6585. layout,
  6586. snapshot,
  6587. delta: visualDelta,
  6588. layoutDelta,
  6589. hasLayoutChanged,
  6590. hasRelativeLayoutChanged,
  6591. });
  6592. }
  6593. else if (node.isLead()) {
  6594. const { onExitComplete } = node.options;
  6595. onExitComplete && onExitComplete();
  6596. }
  6597. /**
  6598. * Clearing transition
  6599. * TODO: Investigate why this transition is being passed in as {type: false } from Framer
  6600. * and why we need it at all
  6601. */
  6602. node.options.transition = undefined;
  6603. }
  6604. function propagateDirtyNodes(node) {
  6605. if (!node.parent)
  6606. return;
  6607. /**
  6608. * If this node isn't projecting, propagate isProjectionDirty. It will have
  6609. * no performance impact but it will allow the next child that *is* projecting
  6610. * but *isn't* dirty to just check its parent to see if *any* ancestor needs
  6611. * correcting.
  6612. */
  6613. if (!node.isProjecting()) {
  6614. node.isProjectionDirty = node.parent.isProjectionDirty;
  6615. }
  6616. /**
  6617. * Propagate isSharedProjectionDirty and isTransformDirty
  6618. * throughout the whole tree. A future revision can take another look at
  6619. * this but for safety we still recalcualte shared nodes.
  6620. */
  6621. node.isSharedProjectionDirty || (node.isSharedProjectionDirty = Boolean(node.isProjectionDirty ||
  6622. node.parent.isProjectionDirty ||
  6623. node.parent.isSharedProjectionDirty));
  6624. node.isTransformDirty || (node.isTransformDirty = node.parent.isTransformDirty);
  6625. }
  6626. function cleanDirtyNodes(node) {
  6627. node.isProjectionDirty =
  6628. node.isSharedProjectionDirty =
  6629. node.isTransformDirty =
  6630. false;
  6631. }
  6632. function clearSnapshot(node) {
  6633. node.clearSnapshot();
  6634. }
  6635. function clearMeasurements(node) {
  6636. node.clearMeasurements();
  6637. }
  6638. function clearIsLayoutDirty(node) {
  6639. node.isLayoutDirty = false;
  6640. }
  6641. function resetTransformStyle(node) {
  6642. const { visualElement } = node.options;
  6643. if (visualElement && visualElement.getProps().onBeforeLayoutMeasure) {
  6644. visualElement.notify("BeforeLayoutMeasure");
  6645. }
  6646. node.resetTransform();
  6647. }
  6648. function finishAnimation(node) {
  6649. node.finishAnimation();
  6650. node.targetDelta = node.relativeTarget = node.target = undefined;
  6651. node.isProjectionDirty = true;
  6652. }
  6653. function resolveTargetDelta(node) {
  6654. node.resolveTargetDelta();
  6655. }
  6656. function calcProjection(node) {
  6657. node.calcProjection();
  6658. }
  6659. function resetSkewAndRotation(node) {
  6660. node.resetSkewAndRotation();
  6661. }
  6662. function removeLeadSnapshots(stack) {
  6663. stack.removeLeadSnapshot();
  6664. }
  6665. function mixAxisDelta(output, delta, p) {
  6666. output.translate = mixNumber$1(delta.translate, 0, p);
  6667. output.scale = mixNumber$1(delta.scale, 1, p);
  6668. output.origin = delta.origin;
  6669. output.originPoint = delta.originPoint;
  6670. }
  6671. function mixAxis(output, from, to, p) {
  6672. output.min = mixNumber$1(from.min, to.min, p);
  6673. output.max = mixNumber$1(from.max, to.max, p);
  6674. }
  6675. function mixBox(output, from, to, p) {
  6676. mixAxis(output.x, from.x, to.x, p);
  6677. mixAxis(output.y, from.y, to.y, p);
  6678. }
  6679. function hasOpacityCrossfade(node) {
  6680. return (node.animationValues && node.animationValues.opacityExit !== undefined);
  6681. }
  6682. const defaultLayoutTransition = {
  6683. duration: 0.45,
  6684. ease: [0.4, 0, 0.1, 1],
  6685. };
  6686. const userAgentContains = (string) => typeof navigator !== "undefined" &&
  6687. navigator.userAgent &&
  6688. navigator.userAgent.toLowerCase().includes(string);
  6689. /**
  6690. * Measured bounding boxes must be rounded in Safari and
  6691. * left untouched in Chrome, otherwise non-integer layouts within scaled-up elements
  6692. * can appear to jump.
  6693. */
  6694. const roundPoint = userAgentContains("applewebkit/") && !userAgentContains("chrome/")
  6695. ? Math.round
  6696. : noop;
  6697. function roundAxis(axis) {
  6698. // Round to the nearest .5 pixels to support subpixel layouts
  6699. axis.min = roundPoint(axis.min);
  6700. axis.max = roundPoint(axis.max);
  6701. }
  6702. function roundBox(box) {
  6703. roundAxis(box.x);
  6704. roundAxis(box.y);
  6705. }
  6706. function shouldAnimatePositionOnly(animationType, snapshot, layout) {
  6707. return (animationType === "position" ||
  6708. (animationType === "preserve-aspect" &&
  6709. !isNear(aspectRatio(snapshot), aspectRatio(layout), 0.2)));
  6710. }
  6711. function checkNodeWasScrollRoot(node) {
  6712. return node !== node.root && node.scroll?.wasRoot;
  6713. }
  6714. function addDomEvent(target, eventName, handler, options = { passive: true }) {
  6715. target.addEventListener(eventName, handler, options);
  6716. return () => target.removeEventListener(eventName, handler);
  6717. }
  6718. const DocumentProjectionNode = createProjectionNode$1({
  6719. attachResizeListener: (ref, notify) => addDomEvent(ref, "resize", notify),
  6720. measureScroll: () => ({
  6721. x: document.documentElement.scrollLeft || document.body.scrollLeft,
  6722. y: document.documentElement.scrollTop || document.body.scrollTop,
  6723. }),
  6724. checkIsScrollRoot: () => true,
  6725. });
  6726. const rootProjectionNode = {
  6727. current: undefined,
  6728. };
  6729. const HTMLProjectionNode = createProjectionNode$1({
  6730. measureScroll: (instance) => ({
  6731. x: instance.scrollLeft,
  6732. y: instance.scrollTop,
  6733. }),
  6734. defaultParent: () => {
  6735. if (!rootProjectionNode.current) {
  6736. const documentNode = new DocumentProjectionNode({});
  6737. documentNode.mount(window);
  6738. documentNode.setOptions({ layoutScroll: true });
  6739. rootProjectionNode.current = documentNode;
  6740. }
  6741. return rootProjectionNode.current;
  6742. },
  6743. resetTransform: (instance, value) => {
  6744. instance.style.transform = value !== undefined ? value : "none";
  6745. },
  6746. checkIsScrollRoot: (instance) => Boolean(window.getComputedStyle(instance).position === "fixed"),
  6747. });
  6748. function pixelsToPercent(pixels, axis) {
  6749. if (axis.max === axis.min)
  6750. return 0;
  6751. return (pixels / (axis.max - axis.min)) * 100;
  6752. }
  6753. /**
  6754. * We always correct borderRadius as a percentage rather than pixels to reduce paints.
  6755. * For example, if you are projecting a box that is 100px wide with a 10px borderRadius
  6756. * into a box that is 200px wide with a 20px borderRadius, that is actually a 10%
  6757. * borderRadius in both states. If we animate between the two in pixels that will trigger
  6758. * a paint each time. If we animate between the two in percentage we'll avoid a paint.
  6759. */
  6760. const correctBorderRadius = {
  6761. correct: (latest, node) => {
  6762. if (!node.target)
  6763. return latest;
  6764. /**
  6765. * If latest is a string, if it's a percentage we can return immediately as it's
  6766. * going to be stretched appropriately. Otherwise, if it's a pixel, convert it to a number.
  6767. */
  6768. if (typeof latest === "string") {
  6769. if (px.test(latest)) {
  6770. latest = parseFloat(latest);
  6771. }
  6772. else {
  6773. return latest;
  6774. }
  6775. }
  6776. /**
  6777. * If latest is a number, it's a pixel value. We use the current viewportBox to calculate that
  6778. * pixel value as a percentage of each axis
  6779. */
  6780. const x = pixelsToPercent(latest, node.target.x);
  6781. const y = pixelsToPercent(latest, node.target.y);
  6782. return `${x}% ${y}%`;
  6783. },
  6784. };
  6785. const correctBoxShadow = {
  6786. correct: (latest, { treeScale, projectionDelta }) => {
  6787. const original = latest;
  6788. const shadow = complex.parse(latest);
  6789. // TODO: Doesn't support multiple shadows
  6790. if (shadow.length > 5)
  6791. return original;
  6792. const template = complex.createTransformer(latest);
  6793. const offset = typeof shadow[0] !== "number" ? 1 : 0;
  6794. // Calculate the overall context scale
  6795. const xScale = projectionDelta.x.scale * treeScale.x;
  6796. const yScale = projectionDelta.y.scale * treeScale.y;
  6797. shadow[0 + offset] /= xScale;
  6798. shadow[1 + offset] /= yScale;
  6799. /**
  6800. * Ideally we'd correct x and y scales individually, but because blur and
  6801. * spread apply to both we have to take a scale average and apply that instead.
  6802. * We could potentially improve the outcome of this by incorporating the ratio between
  6803. * the two scales.
  6804. */
  6805. const averageScale = mixNumber$1(xScale, yScale, 0.5);
  6806. // Blur
  6807. if (typeof shadow[2 + offset] === "number")
  6808. shadow[2 + offset] /= averageScale;
  6809. // Spread
  6810. if (typeof shadow[3 + offset] === "number")
  6811. shadow[3 + offset] /= averageScale;
  6812. return template(shadow);
  6813. },
  6814. };
  6815. /**
  6816. * Bounding boxes tend to be defined as top, left, right, bottom. For various operations
  6817. * it's easier to consider each axis individually. This function returns a bounding box
  6818. * as a map of single-axis min/max values.
  6819. */
  6820. function convertBoundingBoxToBox({ top, left, right, bottom, }) {
  6821. return {
  6822. x: { min: left, max: right },
  6823. y: { min: top, max: bottom },
  6824. };
  6825. }
  6826. function convertBoxToBoundingBox({ x, y }) {
  6827. return { top: y.min, right: x.max, bottom: y.max, left: x.min };
  6828. }
  6829. /**
  6830. * Applies a TransformPoint function to a bounding box. TransformPoint is usually a function
  6831. * provided by Framer to allow measured points to be corrected for device scaling. This is used
  6832. * when measuring DOM elements and DOM event points.
  6833. */
  6834. function transformBoxPoints(point, transformPoint) {
  6835. if (!transformPoint)
  6836. return point;
  6837. const topLeft = transformPoint({ x: point.left, y: point.top });
  6838. const bottomRight = transformPoint({ x: point.right, y: point.bottom });
  6839. return {
  6840. top: topLeft.y,
  6841. left: topLeft.x,
  6842. bottom: bottomRight.y,
  6843. right: bottomRight.x,
  6844. };
  6845. }
  6846. function measureViewportBox(instance, transformPoint) {
  6847. return convertBoundingBoxToBox(transformBoxPoints(instance.getBoundingClientRect(), transformPoint));
  6848. }
  6849. function measurePageBox(element, rootProjectionNode, transformPagePoint) {
  6850. const viewportBox = measureViewportBox(element, transformPagePoint);
  6851. const { scroll } = rootProjectionNode;
  6852. if (scroll) {
  6853. translateAxis(viewportBox.x, scroll.offset.x);
  6854. translateAxis(viewportBox.y, scroll.offset.y);
  6855. }
  6856. return viewportBox;
  6857. }
  6858. const featureProps = {
  6859. animation: [
  6860. "animate",
  6861. "variants",
  6862. "whileHover",
  6863. "whileTap",
  6864. "exit",
  6865. "whileInView",
  6866. "whileFocus",
  6867. "whileDrag",
  6868. ],
  6869. exit: ["exit"],
  6870. drag: ["drag", "dragControls"],
  6871. focus: ["whileFocus"],
  6872. hover: ["whileHover", "onHoverStart", "onHoverEnd"],
  6873. tap: ["whileTap", "onTap", "onTapStart", "onTapCancel"],
  6874. pan: ["onPan", "onPanStart", "onPanSessionStart", "onPanEnd"],
  6875. inView: ["whileInView", "onViewportEnter", "onViewportLeave"],
  6876. layout: ["layout", "layoutId"],
  6877. };
  6878. const featureDefinitions = {};
  6879. for (const key in featureProps) {
  6880. featureDefinitions[key] = {
  6881. isEnabled: (props) => featureProps[key].some((name) => !!props[name]),
  6882. };
  6883. }
  6884. // Does this device prefer reduced motion? Returns `null` server-side.
  6885. const prefersReducedMotion = { current: null };
  6886. const hasReducedMotionListener = { current: false };
  6887. function initPrefersReducedMotion() {
  6888. hasReducedMotionListener.current = true;
  6889. if (!isBrowser)
  6890. return;
  6891. if (window.matchMedia) {
  6892. const motionMediaQuery = window.matchMedia("(prefers-reduced-motion)");
  6893. const setReducedMotionPreferences = () => (prefersReducedMotion.current = motionMediaQuery.matches);
  6894. motionMediaQuery.addListener(setReducedMotionPreferences);
  6895. setReducedMotionPreferences();
  6896. }
  6897. else {
  6898. prefersReducedMotion.current = false;
  6899. }
  6900. }
  6901. /**
  6902. * A list of all ValueTypes
  6903. */
  6904. const valueTypes = [...dimensionValueTypes, color, complex];
  6905. /**
  6906. * Tests a value against the list of ValueTypes
  6907. */
  6908. const findValueType = (v) => valueTypes.find(testValueType(v));
  6909. const visualElementStore = new WeakMap();
  6910. function isAnimationControls(v) {
  6911. return (v !== null &&
  6912. typeof v === "object" &&
  6913. typeof v.start === "function");
  6914. }
  6915. /**
  6916. * Decides if the supplied variable is variant label
  6917. */
  6918. function isVariantLabel(v) {
  6919. return typeof v === "string" || Array.isArray(v);
  6920. }
  6921. const variantPriorityOrder = [
  6922. "animate",
  6923. "whileInView",
  6924. "whileFocus",
  6925. "whileHover",
  6926. "whileTap",
  6927. "whileDrag",
  6928. "exit",
  6929. ];
  6930. const variantProps = ["initial", ...variantPriorityOrder];
  6931. function isControllingVariants(props) {
  6932. return (isAnimationControls(props.animate) ||
  6933. variantProps.some((name) => isVariantLabel(props[name])));
  6934. }
  6935. function isVariantNode(props) {
  6936. return Boolean(isControllingVariants(props) || props.variants);
  6937. }
  6938. function updateMotionValuesFromProps(element, next, prev) {
  6939. for (const key in next) {
  6940. const nextValue = next[key];
  6941. const prevValue = prev[key];
  6942. if (isMotionValue(nextValue)) {
  6943. /**
  6944. * If this is a motion value found in props or style, we want to add it
  6945. * to our visual element's motion value map.
  6946. */
  6947. element.addValue(key, nextValue);
  6948. /**
  6949. * Check the version of the incoming motion value with this version
  6950. * and warn against mismatches.
  6951. */
  6952. {
  6953. warnOnce(nextValue.version === "12.7.3", `Attempting to mix Motion versions ${nextValue.version} with 12.7.3 may not work as expected.`);
  6954. }
  6955. }
  6956. else if (isMotionValue(prevValue)) {
  6957. /**
  6958. * If we're swapping from a motion value to a static value,
  6959. * create a new motion value from that
  6960. */
  6961. element.addValue(key, motionValue(nextValue, { owner: element }));
  6962. }
  6963. else if (prevValue !== nextValue) {
  6964. /**
  6965. * If this is a flat value that has changed, update the motion value
  6966. * or create one if it doesn't exist. We only want to do this if we're
  6967. * not handling the value with our animation state.
  6968. */
  6969. if (element.hasValue(key)) {
  6970. const existingValue = element.getValue(key);
  6971. if (existingValue.liveStyle === true) {
  6972. existingValue.jump(nextValue);
  6973. }
  6974. else if (!existingValue.hasAnimated) {
  6975. existingValue.set(nextValue);
  6976. }
  6977. }
  6978. else {
  6979. const latestValue = element.getStaticValue(key);
  6980. element.addValue(key, motionValue(latestValue !== undefined ? latestValue : nextValue, { owner: element }));
  6981. }
  6982. }
  6983. }
  6984. // Handle removed values
  6985. for (const key in prev) {
  6986. if (next[key] === undefined)
  6987. element.removeValue(key);
  6988. }
  6989. return next;
  6990. }
  6991. function getValueState(visualElement) {
  6992. const state = [{}, {}];
  6993. visualElement?.values.forEach((value, key) => {
  6994. state[0][key] = value.get();
  6995. state[1][key] = value.getVelocity();
  6996. });
  6997. return state;
  6998. }
  6999. function resolveVariantFromProps(props, definition, custom, visualElement) {
  7000. /**
  7001. * If the variant definition is a function, resolve.
  7002. */
  7003. if (typeof definition === "function") {
  7004. const [current, velocity] = getValueState(visualElement);
  7005. definition = definition(custom !== undefined ? custom : props.custom, current, velocity);
  7006. }
  7007. /**
  7008. * If the variant definition is a variant label, or
  7009. * the function returned a variant label, resolve.
  7010. */
  7011. if (typeof definition === "string") {
  7012. definition = props.variants && props.variants[definition];
  7013. }
  7014. /**
  7015. * At this point we've resolved both functions and variant labels,
  7016. * but the resolved variant label might itself have been a function.
  7017. * If so, resolve. This can only have returned a valid target object.
  7018. */
  7019. if (typeof definition === "function") {
  7020. const [current, velocity] = getValueState(visualElement);
  7021. definition = definition(custom !== undefined ? custom : props.custom, current, velocity);
  7022. }
  7023. return definition;
  7024. }
  7025. const propEventHandlers = [
  7026. "AnimationStart",
  7027. "AnimationComplete",
  7028. "Update",
  7029. "BeforeLayoutMeasure",
  7030. "LayoutMeasure",
  7031. "LayoutAnimationStart",
  7032. "LayoutAnimationComplete",
  7033. ];
  7034. /**
  7035. * A VisualElement is an imperative abstraction around UI elements such as
  7036. * HTMLElement, SVGElement, Three.Object3D etc.
  7037. */
  7038. class VisualElement {
  7039. /**
  7040. * This method takes React props and returns found MotionValues. For example, HTML
  7041. * MotionValues will be found within the style prop, whereas for Three.js within attribute arrays.
  7042. *
  7043. * This isn't an abstract method as it needs calling in the constructor, but it is
  7044. * intended to be one.
  7045. */
  7046. scrapeMotionValuesFromProps(_props, _prevProps, _visualElement) {
  7047. return {};
  7048. }
  7049. constructor({ parent, props, presenceContext, reducedMotionConfig, blockInitialAnimation, visualState, }, options = {}) {
  7050. /**
  7051. * A reference to the current underlying Instance, e.g. a HTMLElement
  7052. * or Three.Mesh etc.
  7053. */
  7054. this.current = null;
  7055. /**
  7056. * A set containing references to this VisualElement's children.
  7057. */
  7058. this.children = new Set();
  7059. /**
  7060. * Determine what role this visual element should take in the variant tree.
  7061. */
  7062. this.isVariantNode = false;
  7063. this.isControllingVariants = false;
  7064. /**
  7065. * Decides whether this VisualElement should animate in reduced motion
  7066. * mode.
  7067. *
  7068. * TODO: This is currently set on every individual VisualElement but feels
  7069. * like it could be set globally.
  7070. */
  7071. this.shouldReduceMotion = null;
  7072. /**
  7073. * A map of all motion values attached to this visual element. Motion
  7074. * values are source of truth for any given animated value. A motion
  7075. * value might be provided externally by the component via props.
  7076. */
  7077. this.values = new Map();
  7078. this.KeyframeResolver = KeyframeResolver;
  7079. /**
  7080. * Cleanup functions for active features (hover/tap/exit etc)
  7081. */
  7082. this.features = {};
  7083. /**
  7084. * A map of every subscription that binds the provided or generated
  7085. * motion values onChange listeners to this visual element.
  7086. */
  7087. this.valueSubscriptions = new Map();
  7088. /**
  7089. * A reference to the previously-provided motion values as returned
  7090. * from scrapeMotionValuesFromProps. We use the keys in here to determine
  7091. * if any motion values need to be removed after props are updated.
  7092. */
  7093. this.prevMotionValues = {};
  7094. /**
  7095. * An object containing a SubscriptionManager for each active event.
  7096. */
  7097. this.events = {};
  7098. /**
  7099. * An object containing an unsubscribe function for each prop event subscription.
  7100. * For example, every "Update" event can have multiple subscribers via
  7101. * VisualElement.on(), but only one of those can be defined via the onUpdate prop.
  7102. */
  7103. this.propEventSubscriptions = {};
  7104. this.notifyUpdate = () => this.notify("Update", this.latestValues);
  7105. this.render = () => {
  7106. if (!this.current)
  7107. return;
  7108. this.triggerBuild();
  7109. this.renderInstance(this.current, this.renderState, this.props.style, this.projection);
  7110. };
  7111. this.renderScheduledAt = 0.0;
  7112. this.scheduleRender = () => {
  7113. const now = time.now();
  7114. if (this.renderScheduledAt < now) {
  7115. this.renderScheduledAt = now;
  7116. frame.render(this.render, false, true);
  7117. }
  7118. };
  7119. const { latestValues, renderState, onUpdate } = visualState;
  7120. this.onUpdate = onUpdate;
  7121. this.latestValues = latestValues;
  7122. this.baseTarget = { ...latestValues };
  7123. this.initialValues = props.initial ? { ...latestValues } : {};
  7124. this.renderState = renderState;
  7125. this.parent = parent;
  7126. this.props = props;
  7127. this.presenceContext = presenceContext;
  7128. this.depth = parent ? parent.depth + 1 : 0;
  7129. this.reducedMotionConfig = reducedMotionConfig;
  7130. this.options = options;
  7131. this.blockInitialAnimation = Boolean(blockInitialAnimation);
  7132. this.isControllingVariants = isControllingVariants(props);
  7133. this.isVariantNode = isVariantNode(props);
  7134. if (this.isVariantNode) {
  7135. this.variantChildren = new Set();
  7136. }
  7137. this.manuallyAnimateOnMount = Boolean(parent && parent.current);
  7138. /**
  7139. * Any motion values that are provided to the element when created
  7140. * aren't yet bound to the element, as this would technically be impure.
  7141. * However, we iterate through the motion values and set them to the
  7142. * initial values for this component.
  7143. *
  7144. * TODO: This is impure and we should look at changing this to run on mount.
  7145. * Doing so will break some tests but this isn't necessarily a breaking change,
  7146. * more a reflection of the test.
  7147. */
  7148. const { willChange, ...initialMotionValues } = this.scrapeMotionValuesFromProps(props, {}, this);
  7149. for (const key in initialMotionValues) {
  7150. const value = initialMotionValues[key];
  7151. if (latestValues[key] !== undefined && isMotionValue(value)) {
  7152. value.set(latestValues[key], false);
  7153. }
  7154. }
  7155. }
  7156. mount(instance) {
  7157. this.current = instance;
  7158. visualElementStore.set(instance, this);
  7159. if (this.projection && !this.projection.instance) {
  7160. this.projection.mount(instance);
  7161. }
  7162. if (this.parent && this.isVariantNode && !this.isControllingVariants) {
  7163. this.removeFromVariantTree = this.parent.addVariantChild(this);
  7164. }
  7165. this.values.forEach((value, key) => this.bindToMotionValue(key, value));
  7166. if (!hasReducedMotionListener.current) {
  7167. initPrefersReducedMotion();
  7168. }
  7169. this.shouldReduceMotion =
  7170. this.reducedMotionConfig === "never"
  7171. ? false
  7172. : this.reducedMotionConfig === "always"
  7173. ? true
  7174. : prefersReducedMotion.current;
  7175. {
  7176. warnOnce(this.shouldReduceMotion !== true, "You have Reduced Motion enabled on your device. Animations may not appear as expected.");
  7177. }
  7178. if (this.parent)
  7179. this.parent.children.add(this);
  7180. this.update(this.props, this.presenceContext);
  7181. }
  7182. unmount() {
  7183. this.projection && this.projection.unmount();
  7184. cancelFrame(this.notifyUpdate);
  7185. cancelFrame(this.render);
  7186. this.valueSubscriptions.forEach((remove) => remove());
  7187. this.valueSubscriptions.clear();
  7188. this.removeFromVariantTree && this.removeFromVariantTree();
  7189. this.parent && this.parent.children.delete(this);
  7190. for (const key in this.events) {
  7191. this.events[key].clear();
  7192. }
  7193. for (const key in this.features) {
  7194. const feature = this.features[key];
  7195. if (feature) {
  7196. feature.unmount();
  7197. feature.isMounted = false;
  7198. }
  7199. }
  7200. this.current = null;
  7201. }
  7202. bindToMotionValue(key, value) {
  7203. if (this.valueSubscriptions.has(key)) {
  7204. this.valueSubscriptions.get(key)();
  7205. }
  7206. const valueIsTransform = transformProps.has(key);
  7207. if (valueIsTransform && this.onBindTransform) {
  7208. this.onBindTransform();
  7209. }
  7210. const removeOnChange = value.on("change", (latestValue) => {
  7211. this.latestValues[key] = latestValue;
  7212. this.props.onUpdate && frame.preRender(this.notifyUpdate);
  7213. if (valueIsTransform && this.projection) {
  7214. this.projection.isTransformDirty = true;
  7215. }
  7216. });
  7217. const removeOnRenderRequest = value.on("renderRequest", this.scheduleRender);
  7218. let removeSyncCheck;
  7219. if (window.MotionCheckAppearSync) {
  7220. removeSyncCheck = window.MotionCheckAppearSync(this, key, value);
  7221. }
  7222. this.valueSubscriptions.set(key, () => {
  7223. removeOnChange();
  7224. removeOnRenderRequest();
  7225. if (removeSyncCheck)
  7226. removeSyncCheck();
  7227. if (value.owner)
  7228. value.stop();
  7229. });
  7230. }
  7231. sortNodePosition(other) {
  7232. /**
  7233. * If these nodes aren't even of the same type we can't compare their depth.
  7234. */
  7235. if (!this.current ||
  7236. !this.sortInstanceNodePosition ||
  7237. this.type !== other.type) {
  7238. return 0;
  7239. }
  7240. return this.sortInstanceNodePosition(this.current, other.current);
  7241. }
  7242. updateFeatures() {
  7243. let key = "animation";
  7244. for (key in featureDefinitions) {
  7245. const featureDefinition = featureDefinitions[key];
  7246. if (!featureDefinition)
  7247. continue;
  7248. const { isEnabled, Feature: FeatureConstructor } = featureDefinition;
  7249. /**
  7250. * If this feature is enabled but not active, make a new instance.
  7251. */
  7252. if (!this.features[key] &&
  7253. FeatureConstructor &&
  7254. isEnabled(this.props)) {
  7255. this.features[key] = new FeatureConstructor(this);
  7256. }
  7257. /**
  7258. * If we have a feature, mount or update it.
  7259. */
  7260. if (this.features[key]) {
  7261. const feature = this.features[key];
  7262. if (feature.isMounted) {
  7263. feature.update();
  7264. }
  7265. else {
  7266. feature.mount();
  7267. feature.isMounted = true;
  7268. }
  7269. }
  7270. }
  7271. }
  7272. triggerBuild() {
  7273. this.build(this.renderState, this.latestValues, this.props);
  7274. }
  7275. /**
  7276. * Measure the current viewport box with or without transforms.
  7277. * Only measures axis-aligned boxes, rotate and skew must be manually
  7278. * removed with a re-render to work.
  7279. */
  7280. measureViewportBox() {
  7281. return this.current
  7282. ? this.measureInstanceViewportBox(this.current, this.props)
  7283. : createBox();
  7284. }
  7285. getStaticValue(key) {
  7286. return this.latestValues[key];
  7287. }
  7288. setStaticValue(key, value) {
  7289. this.latestValues[key] = value;
  7290. }
  7291. /**
  7292. * Update the provided props. Ensure any newly-added motion values are
  7293. * added to our map, old ones removed, and listeners updated.
  7294. */
  7295. update(props, presenceContext) {
  7296. if (props.transformTemplate || this.props.transformTemplate) {
  7297. this.scheduleRender();
  7298. }
  7299. this.prevProps = this.props;
  7300. this.props = props;
  7301. this.prevPresenceContext = this.presenceContext;
  7302. this.presenceContext = presenceContext;
  7303. /**
  7304. * Update prop event handlers ie onAnimationStart, onAnimationComplete
  7305. */
  7306. for (let i = 0; i < propEventHandlers.length; i++) {
  7307. const key = propEventHandlers[i];
  7308. if (this.propEventSubscriptions[key]) {
  7309. this.propEventSubscriptions[key]();
  7310. delete this.propEventSubscriptions[key];
  7311. }
  7312. const listenerName = ("on" + key);
  7313. const listener = props[listenerName];
  7314. if (listener) {
  7315. this.propEventSubscriptions[key] = this.on(key, listener);
  7316. }
  7317. }
  7318. this.prevMotionValues = updateMotionValuesFromProps(this, this.scrapeMotionValuesFromProps(props, this.prevProps, this), this.prevMotionValues);
  7319. if (this.handleChildMotionValue) {
  7320. this.handleChildMotionValue();
  7321. }
  7322. this.onUpdate && this.onUpdate(this);
  7323. }
  7324. getProps() {
  7325. return this.props;
  7326. }
  7327. /**
  7328. * Returns the variant definition with a given name.
  7329. */
  7330. getVariant(name) {
  7331. return this.props.variants ? this.props.variants[name] : undefined;
  7332. }
  7333. /**
  7334. * Returns the defined default transition on this component.
  7335. */
  7336. getDefaultTransition() {
  7337. return this.props.transition;
  7338. }
  7339. getTransformPagePoint() {
  7340. return this.props.transformPagePoint;
  7341. }
  7342. getClosestVariantNode() {
  7343. return this.isVariantNode
  7344. ? this
  7345. : this.parent
  7346. ? this.parent.getClosestVariantNode()
  7347. : undefined;
  7348. }
  7349. /**
  7350. * Add a child visual element to our set of children.
  7351. */
  7352. addVariantChild(child) {
  7353. const closestVariantNode = this.getClosestVariantNode();
  7354. if (closestVariantNode) {
  7355. closestVariantNode.variantChildren &&
  7356. closestVariantNode.variantChildren.add(child);
  7357. return () => closestVariantNode.variantChildren.delete(child);
  7358. }
  7359. }
  7360. /**
  7361. * Add a motion value and bind it to this visual element.
  7362. */
  7363. addValue(key, value) {
  7364. // Remove existing value if it exists
  7365. const existingValue = this.values.get(key);
  7366. if (value !== existingValue) {
  7367. if (existingValue)
  7368. this.removeValue(key);
  7369. this.bindToMotionValue(key, value);
  7370. this.values.set(key, value);
  7371. this.latestValues[key] = value.get();
  7372. }
  7373. }
  7374. /**
  7375. * Remove a motion value and unbind any active subscriptions.
  7376. */
  7377. removeValue(key) {
  7378. this.values.delete(key);
  7379. const unsubscribe = this.valueSubscriptions.get(key);
  7380. if (unsubscribe) {
  7381. unsubscribe();
  7382. this.valueSubscriptions.delete(key);
  7383. }
  7384. delete this.latestValues[key];
  7385. this.removeValueFromRenderState(key, this.renderState);
  7386. }
  7387. /**
  7388. * Check whether we have a motion value for this key
  7389. */
  7390. hasValue(key) {
  7391. return this.values.has(key);
  7392. }
  7393. getValue(key, defaultValue) {
  7394. if (this.props.values && this.props.values[key]) {
  7395. return this.props.values[key];
  7396. }
  7397. let value = this.values.get(key);
  7398. if (value === undefined && defaultValue !== undefined) {
  7399. value = motionValue(defaultValue === null ? undefined : defaultValue, { owner: this });
  7400. this.addValue(key, value);
  7401. }
  7402. return value;
  7403. }
  7404. /**
  7405. * If we're trying to animate to a previously unencountered value,
  7406. * we need to check for it in our state and as a last resort read it
  7407. * directly from the instance (which might have performance implications).
  7408. */
  7409. readValue(key, target) {
  7410. let value = this.latestValues[key] !== undefined || !this.current
  7411. ? this.latestValues[key]
  7412. : this.getBaseTargetFromProps(this.props, key) ??
  7413. this.readValueFromInstance(this.current, key, this.options);
  7414. if (value !== undefined && value !== null) {
  7415. if (typeof value === "string" &&
  7416. (isNumericalString(value) || isZeroValueString(value))) {
  7417. // If this is a number read as a string, ie "0" or "200", convert it to a number
  7418. value = parseFloat(value);
  7419. }
  7420. else if (!findValueType(value) && complex.test(target)) {
  7421. value = getAnimatableNone(key, target);
  7422. }
  7423. this.setBaseTarget(key, isMotionValue(value) ? value.get() : value);
  7424. }
  7425. return isMotionValue(value) ? value.get() : value;
  7426. }
  7427. /**
  7428. * Set the base target to later animate back to. This is currently
  7429. * only hydrated on creation and when we first read a value.
  7430. */
  7431. setBaseTarget(key, value) {
  7432. this.baseTarget[key] = value;
  7433. }
  7434. /**
  7435. * Find the base target for a value thats been removed from all animation
  7436. * props.
  7437. */
  7438. getBaseTarget(key) {
  7439. const { initial } = this.props;
  7440. let valueFromInitial;
  7441. if (typeof initial === "string" || typeof initial === "object") {
  7442. const variant = resolveVariantFromProps(this.props, initial, this.presenceContext?.custom);
  7443. if (variant) {
  7444. valueFromInitial = variant[key];
  7445. }
  7446. }
  7447. /**
  7448. * If this value still exists in the current initial variant, read that.
  7449. */
  7450. if (initial && valueFromInitial !== undefined) {
  7451. return valueFromInitial;
  7452. }
  7453. /**
  7454. * Alternatively, if this VisualElement config has defined a getBaseTarget
  7455. * so we can read the value from an alternative source, try that.
  7456. */
  7457. const target = this.getBaseTargetFromProps(this.props, key);
  7458. if (target !== undefined && !isMotionValue(target))
  7459. return target;
  7460. /**
  7461. * If the value was initially defined on initial, but it doesn't any more,
  7462. * return undefined. Otherwise return the value as initially read from the DOM.
  7463. */
  7464. return this.initialValues[key] !== undefined &&
  7465. valueFromInitial === undefined
  7466. ? undefined
  7467. : this.baseTarget[key];
  7468. }
  7469. on(eventName, callback) {
  7470. if (!this.events[eventName]) {
  7471. this.events[eventName] = new SubscriptionManager();
  7472. }
  7473. return this.events[eventName].add(callback);
  7474. }
  7475. notify(eventName, ...args) {
  7476. if (this.events[eventName]) {
  7477. this.events[eventName].notify(...args);
  7478. }
  7479. }
  7480. }
  7481. class DOMVisualElement extends VisualElement {
  7482. constructor() {
  7483. super(...arguments);
  7484. this.KeyframeResolver = DOMKeyframesResolver;
  7485. }
  7486. sortInstanceNodePosition(a, b) {
  7487. /**
  7488. * compareDocumentPosition returns a bitmask, by using the bitwise &
  7489. * we're returning true if 2 in that bitmask is set to true. 2 is set
  7490. * to true if b preceeds a.
  7491. */
  7492. return a.compareDocumentPosition(b) & 2 ? 1 : -1;
  7493. }
  7494. getBaseTargetFromProps(props, key) {
  7495. return props.style
  7496. ? props.style[key]
  7497. : undefined;
  7498. }
  7499. removeValueFromRenderState(key, { vars, style }) {
  7500. delete vars[key];
  7501. delete style[key];
  7502. }
  7503. handleChildMotionValue() {
  7504. if (this.childSubscription) {
  7505. this.childSubscription();
  7506. delete this.childSubscription;
  7507. }
  7508. const { children } = this.props;
  7509. if (isMotionValue(children)) {
  7510. this.childSubscription = children.on("change", (latest) => {
  7511. if (this.current) {
  7512. this.current.textContent = `${latest}`;
  7513. }
  7514. });
  7515. }
  7516. }
  7517. }
  7518. /**
  7519. * Provided a value and a ValueType, returns the value as that value type.
  7520. */
  7521. const getValueAsType = (value, type) => {
  7522. return type && typeof value === "number"
  7523. ? type.transform(value)
  7524. : value;
  7525. };
  7526. const translateAlias = {
  7527. x: "translateX",
  7528. y: "translateY",
  7529. z: "translateZ",
  7530. transformPerspective: "perspective",
  7531. };
  7532. const numTransforms = transformPropOrder.length;
  7533. /**
  7534. * Build a CSS transform style from individual x/y/scale etc properties.
  7535. *
  7536. * This outputs with a default order of transforms/scales/rotations, this can be customised by
  7537. * providing a transformTemplate function.
  7538. */
  7539. function buildTransform(latestValues, transform, transformTemplate) {
  7540. // The transform string we're going to build into.
  7541. let transformString = "";
  7542. let transformIsDefault = true;
  7543. /**
  7544. * Loop over all possible transforms in order, adding the ones that
  7545. * are present to the transform string.
  7546. */
  7547. for (let i = 0; i < numTransforms; i++) {
  7548. const key = transformPropOrder[i];
  7549. const value = latestValues[key];
  7550. if (value === undefined)
  7551. continue;
  7552. let valueIsDefault = true;
  7553. if (typeof value === "number") {
  7554. valueIsDefault = value === (key.startsWith("scale") ? 1 : 0);
  7555. }
  7556. else {
  7557. valueIsDefault = parseFloat(value) === 0;
  7558. }
  7559. if (!valueIsDefault || transformTemplate) {
  7560. const valueAsType = getValueAsType(value, numberValueTypes[key]);
  7561. if (!valueIsDefault) {
  7562. transformIsDefault = false;
  7563. const transformName = translateAlias[key] || key;
  7564. transformString += `${transformName}(${valueAsType}) `;
  7565. }
  7566. if (transformTemplate) {
  7567. transform[key] = valueAsType;
  7568. }
  7569. }
  7570. }
  7571. transformString = transformString.trim();
  7572. // If we have a custom `transform` template, pass our transform values and
  7573. // generated transformString to that before returning
  7574. if (transformTemplate) {
  7575. transformString = transformTemplate(transform, transformIsDefault ? "" : transformString);
  7576. }
  7577. else if (transformIsDefault) {
  7578. transformString = "none";
  7579. }
  7580. return transformString;
  7581. }
  7582. function buildHTMLStyles(state, latestValues, transformTemplate) {
  7583. const { style, vars, transformOrigin } = state;
  7584. // Track whether we encounter any transform or transformOrigin values.
  7585. let hasTransform = false;
  7586. let hasTransformOrigin = false;
  7587. /**
  7588. * Loop over all our latest animated values and decide whether to handle them
  7589. * as a style or CSS variable.
  7590. *
  7591. * Transforms and transform origins are kept separately for further processing.
  7592. */
  7593. for (const key in latestValues) {
  7594. const value = latestValues[key];
  7595. if (transformProps.has(key)) {
  7596. // If this is a transform, flag to enable further transform processing
  7597. hasTransform = true;
  7598. continue;
  7599. }
  7600. else if (isCSSVariableName(key)) {
  7601. vars[key] = value;
  7602. continue;
  7603. }
  7604. else {
  7605. // Convert the value to its default value type, ie 0 -> "0px"
  7606. const valueAsType = getValueAsType(value, numberValueTypes[key]);
  7607. if (key.startsWith("origin")) {
  7608. // If this is a transform origin, flag and enable further transform-origin processing
  7609. hasTransformOrigin = true;
  7610. transformOrigin[key] =
  7611. valueAsType;
  7612. }
  7613. else {
  7614. style[key] = valueAsType;
  7615. }
  7616. }
  7617. }
  7618. if (!latestValues.transform) {
  7619. if (hasTransform || transformTemplate) {
  7620. style.transform = buildTransform(latestValues, state.transform, transformTemplate);
  7621. }
  7622. else if (style.transform) {
  7623. /**
  7624. * If we have previously created a transform but currently don't have any,
  7625. * reset transform style to none.
  7626. */
  7627. style.transform = "none";
  7628. }
  7629. }
  7630. /**
  7631. * Build a transformOrigin style. Uses the same defaults as the browser for
  7632. * undefined origins.
  7633. */
  7634. if (hasTransformOrigin) {
  7635. const { originX = "50%", originY = "50%", originZ = 0, } = transformOrigin;
  7636. style.transformOrigin = `${originX} ${originY} ${originZ}`;
  7637. }
  7638. }
  7639. function renderHTML(element, { style, vars }, styleProp, projection) {
  7640. Object.assign(element.style, style, projection && projection.getProjectionStyles(styleProp));
  7641. // Loop over any CSS variables and assign those.
  7642. for (const key in vars) {
  7643. element.style.setProperty(key, vars[key]);
  7644. }
  7645. }
  7646. function isForcedMotionValue(key, { layout, layoutId }) {
  7647. return (transformProps.has(key) ||
  7648. key.startsWith("origin") ||
  7649. ((layout || layoutId !== undefined) &&
  7650. (!!scaleCorrectors[key] || key === "opacity")));
  7651. }
  7652. function scrapeMotionValuesFromProps$1(props, prevProps, visualElement) {
  7653. const { style } = props;
  7654. const newValues = {};
  7655. for (const key in style) {
  7656. if (isMotionValue(style[key]) ||
  7657. (prevProps.style &&
  7658. isMotionValue(prevProps.style[key])) ||
  7659. isForcedMotionValue(key, props) ||
  7660. visualElement?.getValue(key)?.liveStyle !== undefined) {
  7661. newValues[key] = style[key];
  7662. }
  7663. }
  7664. return newValues;
  7665. }
  7666. function getComputedStyle$1(element) {
  7667. return window.getComputedStyle(element);
  7668. }
  7669. class HTMLVisualElement extends DOMVisualElement {
  7670. constructor() {
  7671. super(...arguments);
  7672. this.type = "html";
  7673. this.renderInstance = renderHTML;
  7674. }
  7675. readValueFromInstance(instance, key) {
  7676. if (transformProps.has(key)) {
  7677. return readTransformValue(instance, key);
  7678. }
  7679. else {
  7680. const computedStyle = getComputedStyle$1(instance);
  7681. const value = (isCSSVariableName(key)
  7682. ? computedStyle.getPropertyValue(key)
  7683. : computedStyle[key]) || 0;
  7684. return typeof value === "string" ? value.trim() : value;
  7685. }
  7686. }
  7687. measureInstanceViewportBox(instance, { transformPagePoint }) {
  7688. return measureViewportBox(instance, transformPagePoint);
  7689. }
  7690. build(renderState, latestValues, props) {
  7691. buildHTMLStyles(renderState, latestValues, props.transformTemplate);
  7692. }
  7693. scrapeMotionValuesFromProps(props, prevProps, visualElement) {
  7694. return scrapeMotionValuesFromProps$1(props, prevProps, visualElement);
  7695. }
  7696. }
  7697. function useIsMounted() {
  7698. const isMounted = React$1.useRef(false);
  7699. useIsomorphicLayoutEffect(() => {
  7700. isMounted.current = true;
  7701. return () => {
  7702. isMounted.current = false;
  7703. };
  7704. }, []);
  7705. return isMounted;
  7706. }
  7707. function useForceUpdate() {
  7708. const isMounted = useIsMounted();
  7709. const [forcedRenderCount, setForcedRenderCount] = React$1.useState(0);
  7710. const forceRender = React$1.useCallback(() => {
  7711. isMounted.current && setForcedRenderCount(forcedRenderCount + 1);
  7712. }, [forcedRenderCount]);
  7713. /**
  7714. * Defer this to the end of the next animation frame in case there are multiple
  7715. * synchronous calls.
  7716. */
  7717. const deferredForceRender = React$1.useCallback(() => frame.postRender(forceRender), [forceRender]);
  7718. return [deferredForceRender, forcedRenderCount];
  7719. }
  7720. const shouldInheritGroup = (inherit) => inherit === true;
  7721. const shouldInheritId = (inherit) => shouldInheritGroup(inherit === true) || inherit === "id";
  7722. const LayoutGroup = ({ children, id, inherit = true }) => {
  7723. const layoutGroupContext = React$1.useContext(LayoutGroupContext);
  7724. const deprecatedLayoutGroupContext = React$1.useContext(DeprecatedLayoutGroupContext);
  7725. const [forceRender, key] = useForceUpdate();
  7726. const context = React$1.useRef(null);
  7727. const upstreamId = layoutGroupContext.id || deprecatedLayoutGroupContext;
  7728. if (context.current === null) {
  7729. if (shouldInheritId(inherit) && upstreamId) {
  7730. id = id ? upstreamId + "-" + id : upstreamId;
  7731. }
  7732. context.current = {
  7733. id,
  7734. group: shouldInheritGroup(inherit)
  7735. ? layoutGroupContext.group || nodeGroup()
  7736. : nodeGroup(),
  7737. };
  7738. }
  7739. const memoizedContext = React$1.useMemo(() => ({ ...context.current, forceRender }), [key]);
  7740. return (jsx(LayoutGroupContext.Provider, { value: memoizedContext, children: children }));
  7741. };
  7742. const LazyContext = React$1.createContext({ strict: false });
  7743. function loadFeatures(features) {
  7744. for (const key in features) {
  7745. featureDefinitions[key] = {
  7746. ...featureDefinitions[key],
  7747. ...features[key],
  7748. };
  7749. }
  7750. }
  7751. /**
  7752. * Used in conjunction with the `m` component to reduce bundle size.
  7753. *
  7754. * `m` is a version of the `motion` component that only loads functionality
  7755. * critical for the initial render.
  7756. *
  7757. * `LazyMotion` can then be used to either synchronously or asynchronously
  7758. * load animation and gesture support.
  7759. *
  7760. * ```jsx
  7761. * // Synchronous loading
  7762. * import { LazyMotion, m, domAnimation } from "framer-motion"
  7763. *
  7764. * function App() {
  7765. * return (
  7766. * <LazyMotion features={domAnimation}>
  7767. * <m.div animate={{ scale: 2 }} />
  7768. * </LazyMotion>
  7769. * )
  7770. * }
  7771. *
  7772. * // Asynchronous loading
  7773. * import { LazyMotion, m } from "framer-motion"
  7774. *
  7775. * function App() {
  7776. * return (
  7777. * <LazyMotion features={() => import('./path/to/domAnimation')}>
  7778. * <m.div animate={{ scale: 2 }} />
  7779. * </LazyMotion>
  7780. * )
  7781. * }
  7782. * ```
  7783. *
  7784. * @public
  7785. */
  7786. function LazyMotion({ children, features, strict = false }) {
  7787. const [, setIsLoaded] = React$1.useState(!isLazyBundle(features));
  7788. const loadedRenderer = React$1.useRef(undefined);
  7789. /**
  7790. * If this is a synchronous load, load features immediately
  7791. */
  7792. if (!isLazyBundle(features)) {
  7793. const { renderer, ...loadedFeatures } = features;
  7794. loadedRenderer.current = renderer;
  7795. loadFeatures(loadedFeatures);
  7796. }
  7797. React$1.useEffect(() => {
  7798. if (isLazyBundle(features)) {
  7799. features().then(({ renderer, ...loadedFeatures }) => {
  7800. loadFeatures(loadedFeatures);
  7801. loadedRenderer.current = renderer;
  7802. setIsLoaded(true);
  7803. });
  7804. }
  7805. }, []);
  7806. return (jsx(LazyContext.Provider, { value: { renderer: loadedRenderer.current, strict }, children: children }));
  7807. }
  7808. function isLazyBundle(features) {
  7809. return typeof features === "function";
  7810. }
  7811. /**
  7812. * A list of all valid MotionProps.
  7813. *
  7814. * @privateRemarks
  7815. * This doesn't throw if a `MotionProp` name is missing - it should.
  7816. */
  7817. const validMotionProps = new Set([
  7818. "animate",
  7819. "exit",
  7820. "variants",
  7821. "initial",
  7822. "style",
  7823. "values",
  7824. "variants",
  7825. "transition",
  7826. "transformTemplate",
  7827. "custom",
  7828. "inherit",
  7829. "onBeforeLayoutMeasure",
  7830. "onAnimationStart",
  7831. "onAnimationComplete",
  7832. "onUpdate",
  7833. "onDragStart",
  7834. "onDrag",
  7835. "onDragEnd",
  7836. "onMeasureDragConstraints",
  7837. "onDirectionLock",
  7838. "onDragTransitionEnd",
  7839. "_dragX",
  7840. "_dragY",
  7841. "onHoverStart",
  7842. "onHoverEnd",
  7843. "onViewportEnter",
  7844. "onViewportLeave",
  7845. "globalTapTarget",
  7846. "ignoreStrict",
  7847. "viewport",
  7848. ]);
  7849. /**
  7850. * Check whether a prop name is a valid `MotionProp` key.
  7851. *
  7852. * @param key - Name of the property to check
  7853. * @returns `true` is key is a valid `MotionProp`.
  7854. *
  7855. * @public
  7856. */
  7857. function isValidMotionProp(key) {
  7858. return (key.startsWith("while") ||
  7859. (key.startsWith("drag") && key !== "draggable") ||
  7860. key.startsWith("layout") ||
  7861. key.startsWith("onTap") ||
  7862. key.startsWith("onPan") ||
  7863. key.startsWith("onLayout") ||
  7864. validMotionProps.has(key));
  7865. }
  7866. let shouldForward = (key) => !isValidMotionProp(key);
  7867. function loadExternalIsValidProp(isValidProp) {
  7868. if (!isValidProp)
  7869. return;
  7870. // Explicitly filter our events
  7871. shouldForward = (key) => key.startsWith("on") ? !isValidMotionProp(key) : isValidProp(key);
  7872. }
  7873. /**
  7874. * Emotion and Styled Components both allow users to pass through arbitrary props to their components
  7875. * to dynamically generate CSS. They both use the `@emotion/is-prop-valid` package to determine which
  7876. * of these should be passed to the underlying DOM node.
  7877. *
  7878. * However, when styling a Motion component `styled(motion.div)`, both packages pass through *all* props
  7879. * as it's seen as an arbitrary component rather than a DOM node. Motion only allows arbitrary props
  7880. * passed through the `custom` prop so it doesn't *need* the payload or computational overhead of
  7881. * `@emotion/is-prop-valid`, however to fix this problem we need to use it.
  7882. *
  7883. * By making it an optionalDependency we can offer this functionality only in the situations where it's
  7884. * actually required.
  7885. */
  7886. try {
  7887. /**
  7888. * We attempt to import this package but require won't be defined in esm environments, in that case
  7889. * isPropValid will have to be provided via `MotionContext`. In a 6.0.0 this should probably be removed
  7890. * in favour of explicit injection.
  7891. */
  7892. loadExternalIsValidProp(require("@emotion/is-prop-valid").default);
  7893. }
  7894. catch {
  7895. // We don't need to actually do anything here - the fallback is the existing `isPropValid`.
  7896. }
  7897. function filterProps(props, isDom, forwardMotionProps) {
  7898. const filteredProps = {};
  7899. for (const key in props) {
  7900. /**
  7901. * values is considered a valid prop by Emotion, so if it's present
  7902. * this will be rendered out to the DOM unless explicitly filtered.
  7903. *
  7904. * We check the type as it could be used with the `feColorMatrix`
  7905. * element, which we support.
  7906. */
  7907. if (key === "values" && typeof props.values === "object")
  7908. continue;
  7909. if (shouldForward(key) ||
  7910. (forwardMotionProps === true && isValidMotionProp(key)) ||
  7911. (!isDom && !isValidMotionProp(key)) ||
  7912. // If trying to use native HTML drag events, forward drag listeners
  7913. (props["draggable"] &&
  7914. key.startsWith("onDrag"))) {
  7915. filteredProps[key] =
  7916. props[key];
  7917. }
  7918. }
  7919. return filteredProps;
  7920. }
  7921. /**
  7922. * `MotionConfig` is used to set configuration options for all children `motion` components.
  7923. *
  7924. * ```jsx
  7925. * import { motion, MotionConfig } from "framer-motion"
  7926. *
  7927. * export function App() {
  7928. * return (
  7929. * <MotionConfig transition={{ type: "spring" }}>
  7930. * <motion.div animate={{ x: 100 }} />
  7931. * </MotionConfig>
  7932. * )
  7933. * }
  7934. * ```
  7935. *
  7936. * @public
  7937. */
  7938. function MotionConfig({ children, isValidProp, ...config }) {
  7939. isValidProp && loadExternalIsValidProp(isValidProp);
  7940. /**
  7941. * Inherit props from any parent MotionConfig components
  7942. */
  7943. config = { ...React$1.useContext(MotionConfigContext), ...config };
  7944. /**
  7945. * Don't allow isStatic to change between renders as it affects how many hooks
  7946. * motion components fire.
  7947. */
  7948. config.isStatic = useConstant(() => config.isStatic);
  7949. /**
  7950. * Creating a new config context object will re-render every `motion` component
  7951. * every time it renders. So we only want to create a new one sparingly.
  7952. */
  7953. const context = React$1.useMemo(() => config, [
  7954. JSON.stringify(config.transition),
  7955. config.transformPagePoint,
  7956. config.reducedMotion,
  7957. ]);
  7958. return (jsx(MotionConfigContext.Provider, { value: context, children: children }));
  7959. }
  7960. const ReorderContext = React$1.createContext(null);
  7961. function createDOMMotionComponentProxy(componentFactory) {
  7962. if (typeof Proxy === "undefined") {
  7963. return componentFactory;
  7964. }
  7965. /**
  7966. * A cache of generated `motion` components, e.g `motion.div`, `motion.input` etc.
  7967. * Rather than generating them anew every render.
  7968. */
  7969. const componentCache = new Map();
  7970. const deprecatedFactoryFunction = (...args) => {
  7971. {
  7972. warnOnce(false, "motion() is deprecated. Use motion.create() instead.");
  7973. }
  7974. return componentFactory(...args);
  7975. };
  7976. return new Proxy(deprecatedFactoryFunction, {
  7977. /**
  7978. * Called when `motion` is referenced with a prop: `motion.div`, `motion.input` etc.
  7979. * The prop name is passed through as `key` and we can use that to generate a `motion`
  7980. * DOM component with that name.
  7981. */
  7982. get: (_target, key) => {
  7983. if (key === "create")
  7984. return componentFactory;
  7985. /**
  7986. * If this element doesn't exist in the component cache, create it and cache.
  7987. */
  7988. if (!componentCache.has(key)) {
  7989. componentCache.set(key, componentFactory(key));
  7990. }
  7991. return componentCache.get(key);
  7992. },
  7993. });
  7994. }
  7995. function resolveVariant(visualElement, definition, custom) {
  7996. const props = visualElement.getProps();
  7997. return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, visualElement);
  7998. }
  7999. /**
  8000. * Set VisualElement's MotionValue, creating a new MotionValue for it if
  8001. * it doesn't exist.
  8002. */
  8003. function setMotionValue(visualElement, key, value) {
  8004. if (visualElement.hasValue(key)) {
  8005. visualElement.getValue(key).set(value);
  8006. }
  8007. else {
  8008. visualElement.addValue(key, motionValue(value));
  8009. }
  8010. }
  8011. function setTarget(visualElement, definition) {
  8012. const resolved = resolveVariant(visualElement, definition);
  8013. let { transitionEnd = {}, transition = {}, ...target } = resolved || {};
  8014. target = { ...target, ...transitionEnd };
  8015. for (const key in target) {
  8016. const value = resolveFinalValueInKeyframes(target[key]);
  8017. setMotionValue(visualElement, key, value);
  8018. }
  8019. }
  8020. function isWillChangeMotionValue(value) {
  8021. return Boolean(isMotionValue(value) && value.add);
  8022. }
  8023. function addValueToWillChange(visualElement, key) {
  8024. const willChange = visualElement.getValue("willChange");
  8025. /**
  8026. * It could be that a user has set willChange to a regular MotionValue,
  8027. * in which case we can't add the value to it.
  8028. */
  8029. if (isWillChangeMotionValue(willChange)) {
  8030. return willChange.add(key);
  8031. }
  8032. else if (!willChange && MotionGlobalConfig.WillChange) {
  8033. const newWillChange = new MotionGlobalConfig.WillChange("auto");
  8034. visualElement.addValue("willChange", newWillChange);
  8035. newWillChange.add(key);
  8036. }
  8037. }
  8038. /**
  8039. * Decide whether we should block this animation. Previously, we achieved this
  8040. * just by checking whether the key was listed in protectedKeys, but this
  8041. * posed problems if an animation was triggered by afterChildren and protectedKeys
  8042. * had been set to true in the meantime.
  8043. */
  8044. function shouldBlockAnimation({ protectedKeys, needsAnimating }, key) {
  8045. const shouldBlock = protectedKeys.hasOwnProperty(key) && needsAnimating[key] !== true;
  8046. needsAnimating[key] = false;
  8047. return shouldBlock;
  8048. }
  8049. function animateTarget(visualElement, targetAndTransition, { delay = 0, transitionOverride, type } = {}) {
  8050. let { transition = visualElement.getDefaultTransition(), transitionEnd, ...target } = targetAndTransition;
  8051. if (transitionOverride)
  8052. transition = transitionOverride;
  8053. const animations = [];
  8054. const animationTypeState = type &&
  8055. visualElement.animationState &&
  8056. visualElement.animationState.getState()[type];
  8057. for (const key in target) {
  8058. const value = visualElement.getValue(key, visualElement.latestValues[key] ?? null);
  8059. const valueTarget = target[key];
  8060. if (valueTarget === undefined ||
  8061. (animationTypeState &&
  8062. shouldBlockAnimation(animationTypeState, key))) {
  8063. continue;
  8064. }
  8065. const valueTransition = {
  8066. delay,
  8067. ...getValueTransition$1(transition || {}, key),
  8068. };
  8069. /**
  8070. * If this is the first time a value is being animated, check
  8071. * to see if we're handling off from an existing animation.
  8072. */
  8073. let isHandoff = false;
  8074. if (window.MotionHandoffAnimation) {
  8075. const appearId = getOptimisedAppearId(visualElement);
  8076. if (appearId) {
  8077. const startTime = window.MotionHandoffAnimation(appearId, key, frame);
  8078. if (startTime !== null) {
  8079. valueTransition.startTime = startTime;
  8080. isHandoff = true;
  8081. }
  8082. }
  8083. }
  8084. addValueToWillChange(visualElement, key);
  8085. value.start(animateMotionValue(key, value, valueTarget, visualElement.shouldReduceMotion && positionalKeys.has(key)
  8086. ? { type: false }
  8087. : valueTransition, visualElement, isHandoff));
  8088. const animation = value.animation;
  8089. if (animation) {
  8090. animations.push(animation);
  8091. }
  8092. }
  8093. if (transitionEnd) {
  8094. Promise.all(animations).then(() => {
  8095. frame.update(() => {
  8096. transitionEnd && setTarget(visualElement, transitionEnd);
  8097. });
  8098. });
  8099. }
  8100. return animations;
  8101. }
  8102. function animateVariant(visualElement, variant, options = {}) {
  8103. const resolved = resolveVariant(visualElement, variant, options.type === "exit"
  8104. ? visualElement.presenceContext?.custom
  8105. : undefined);
  8106. let { transition = visualElement.getDefaultTransition() || {} } = resolved || {};
  8107. if (options.transitionOverride) {
  8108. transition = options.transitionOverride;
  8109. }
  8110. /**
  8111. * If we have a variant, create a callback that runs it as an animation.
  8112. * Otherwise, we resolve a Promise immediately for a composable no-op.
  8113. */
  8114. const getAnimation = resolved
  8115. ? () => Promise.all(animateTarget(visualElement, resolved, options))
  8116. : () => Promise.resolve();
  8117. /**
  8118. * If we have children, create a callback that runs all their animations.
  8119. * Otherwise, we resolve a Promise immediately for a composable no-op.
  8120. */
  8121. const getChildAnimations = visualElement.variantChildren && visualElement.variantChildren.size
  8122. ? (forwardDelay = 0) => {
  8123. const { delayChildren = 0, staggerChildren, staggerDirection, } = transition;
  8124. return animateChildren(visualElement, variant, delayChildren + forwardDelay, staggerChildren, staggerDirection, options);
  8125. }
  8126. : () => Promise.resolve();
  8127. /**
  8128. * If the transition explicitly defines a "when" option, we need to resolve either
  8129. * this animation or all children animations before playing the other.
  8130. */
  8131. const { when } = transition;
  8132. if (when) {
  8133. const [first, last] = when === "beforeChildren"
  8134. ? [getAnimation, getChildAnimations]
  8135. : [getChildAnimations, getAnimation];
  8136. return first().then(() => last());
  8137. }
  8138. else {
  8139. return Promise.all([getAnimation(), getChildAnimations(options.delay)]);
  8140. }
  8141. }
  8142. function animateChildren(visualElement, variant, delayChildren = 0, staggerChildren = 0, staggerDirection = 1, options) {
  8143. const animations = [];
  8144. const maxStaggerDuration = (visualElement.variantChildren.size - 1) * staggerChildren;
  8145. const generateStaggerDuration = staggerDirection === 1
  8146. ? (i = 0) => i * staggerChildren
  8147. : (i = 0) => maxStaggerDuration - i * staggerChildren;
  8148. Array.from(visualElement.variantChildren)
  8149. .sort(sortByTreeOrder)
  8150. .forEach((child, i) => {
  8151. child.notify("AnimationStart", variant);
  8152. animations.push(animateVariant(child, variant, {
  8153. ...options,
  8154. delay: delayChildren + generateStaggerDuration(i),
  8155. }).then(() => child.notify("AnimationComplete", variant)));
  8156. });
  8157. return Promise.all(animations);
  8158. }
  8159. function sortByTreeOrder(a, b) {
  8160. return a.sortNodePosition(b);
  8161. }
  8162. function animateVisualElement(visualElement, definition, options = {}) {
  8163. visualElement.notify("AnimationStart", definition);
  8164. let animation;
  8165. if (Array.isArray(definition)) {
  8166. const animations = definition.map((variant) => animateVariant(visualElement, variant, options));
  8167. animation = Promise.all(animations);
  8168. }
  8169. else if (typeof definition === "string") {
  8170. animation = animateVariant(visualElement, definition, options);
  8171. }
  8172. else {
  8173. const resolvedDefinition = typeof definition === "function"
  8174. ? resolveVariant(visualElement, definition, options.custom)
  8175. : definition;
  8176. animation = Promise.all(animateTarget(visualElement, resolvedDefinition, options));
  8177. }
  8178. return animation.then(() => {
  8179. visualElement.notify("AnimationComplete", definition);
  8180. });
  8181. }
  8182. function shallowCompare(next, prev) {
  8183. if (!Array.isArray(prev))
  8184. return false;
  8185. const prevLength = prev.length;
  8186. if (prevLength !== next.length)
  8187. return false;
  8188. for (let i = 0; i < prevLength; i++) {
  8189. if (prev[i] !== next[i])
  8190. return false;
  8191. }
  8192. return true;
  8193. }
  8194. const numVariantProps = variantProps.length;
  8195. function getVariantContext(visualElement) {
  8196. if (!visualElement)
  8197. return undefined;
  8198. if (!visualElement.isControllingVariants) {
  8199. const context = visualElement.parent
  8200. ? getVariantContext(visualElement.parent) || {}
  8201. : {};
  8202. if (visualElement.props.initial !== undefined) {
  8203. context.initial = visualElement.props.initial;
  8204. }
  8205. return context;
  8206. }
  8207. const context = {};
  8208. for (let i = 0; i < numVariantProps; i++) {
  8209. const name = variantProps[i];
  8210. const prop = visualElement.props[name];
  8211. if (isVariantLabel(prop) || prop === false) {
  8212. context[name] = prop;
  8213. }
  8214. }
  8215. return context;
  8216. }
  8217. const reversePriorityOrder = [...variantPriorityOrder].reverse();
  8218. const numAnimationTypes = variantPriorityOrder.length;
  8219. function animateList(visualElement) {
  8220. return (animations) => Promise.all(animations.map(({ animation, options }) => animateVisualElement(visualElement, animation, options)));
  8221. }
  8222. function createAnimationState(visualElement) {
  8223. let animate = animateList(visualElement);
  8224. let state = createState();
  8225. let isInitialRender = true;
  8226. /**
  8227. * This function will be used to reduce the animation definitions for
  8228. * each active animation type into an object of resolved values for it.
  8229. */
  8230. const buildResolvedTypeValues = (type) => (acc, definition) => {
  8231. const resolved = resolveVariant(visualElement, definition, type === "exit"
  8232. ? visualElement.presenceContext?.custom
  8233. : undefined);
  8234. if (resolved) {
  8235. const { transition, transitionEnd, ...target } = resolved;
  8236. acc = { ...acc, ...target, ...transitionEnd };
  8237. }
  8238. return acc;
  8239. };
  8240. /**
  8241. * This just allows us to inject mocked animation functions
  8242. * @internal
  8243. */
  8244. function setAnimateFunction(makeAnimator) {
  8245. animate = makeAnimator(visualElement);
  8246. }
  8247. /**
  8248. * When we receive new props, we need to:
  8249. * 1. Create a list of protected keys for each type. This is a directory of
  8250. * value keys that are currently being "handled" by types of a higher priority
  8251. * so that whenever an animation is played of a given type, these values are
  8252. * protected from being animated.
  8253. * 2. Determine if an animation type needs animating.
  8254. * 3. Determine if any values have been removed from a type and figure out
  8255. * what to animate those to.
  8256. */
  8257. function animateChanges(changedActiveType) {
  8258. const { props } = visualElement;
  8259. const context = getVariantContext(visualElement.parent) || {};
  8260. /**
  8261. * A list of animations that we'll build into as we iterate through the animation
  8262. * types. This will get executed at the end of the function.
  8263. */
  8264. const animations = [];
  8265. /**
  8266. * Keep track of which values have been removed. Then, as we hit lower priority
  8267. * animation types, we can check if they contain removed values and animate to that.
  8268. */
  8269. const removedKeys = new Set();
  8270. /**
  8271. * A dictionary of all encountered keys. This is an object to let us build into and
  8272. * copy it without iteration. Each time we hit an animation type we set its protected
  8273. * keys - the keys its not allowed to animate - to the latest version of this object.
  8274. */
  8275. let encounteredKeys = {};
  8276. /**
  8277. * If a variant has been removed at a given index, and this component is controlling
  8278. * variant animations, we want to ensure lower-priority variants are forced to animate.
  8279. */
  8280. let removedVariantIndex = Infinity;
  8281. /**
  8282. * Iterate through all animation types in reverse priority order. For each, we want to
  8283. * detect which values it's handling and whether or not they've changed (and therefore
  8284. * need to be animated). If any values have been removed, we want to detect those in
  8285. * lower priority props and flag for animation.
  8286. */
  8287. for (let i = 0; i < numAnimationTypes; i++) {
  8288. const type = reversePriorityOrder[i];
  8289. const typeState = state[type];
  8290. const prop = props[type] !== undefined
  8291. ? props[type]
  8292. : context[type];
  8293. const propIsVariant = isVariantLabel(prop);
  8294. /**
  8295. * If this type has *just* changed isActive status, set activeDelta
  8296. * to that status. Otherwise set to null.
  8297. */
  8298. const activeDelta = type === changedActiveType ? typeState.isActive : null;
  8299. if (activeDelta === false)
  8300. removedVariantIndex = i;
  8301. /**
  8302. * If this prop is an inherited variant, rather than been set directly on the
  8303. * component itself, we want to make sure we allow the parent to trigger animations.
  8304. *
  8305. * TODO: Can probably change this to a !isControllingVariants check
  8306. */
  8307. let isInherited = prop === context[type] &&
  8308. prop !== props[type] &&
  8309. propIsVariant;
  8310. /**
  8311. *
  8312. */
  8313. if (isInherited &&
  8314. isInitialRender &&
  8315. visualElement.manuallyAnimateOnMount) {
  8316. isInherited = false;
  8317. }
  8318. /**
  8319. * Set all encountered keys so far as the protected keys for this type. This will
  8320. * be any key that has been animated or otherwise handled by active, higher-priortiy types.
  8321. */
  8322. typeState.protectedKeys = { ...encounteredKeys };
  8323. // Check if we can skip analysing this prop early
  8324. if (
  8325. // If it isn't active and hasn't *just* been set as inactive
  8326. (!typeState.isActive && activeDelta === null) ||
  8327. // If we didn't and don't have any defined prop for this animation type
  8328. (!prop && !typeState.prevProp) ||
  8329. // Or if the prop doesn't define an animation
  8330. isAnimationControls(prop) ||
  8331. typeof prop === "boolean") {
  8332. continue;
  8333. }
  8334. /**
  8335. * As we go look through the values defined on this type, if we detect
  8336. * a changed value or a value that was removed in a higher priority, we set
  8337. * this to true and add this prop to the animation list.
  8338. */
  8339. const variantDidChange = checkVariantsDidChange(typeState.prevProp, prop);
  8340. let shouldAnimateType = variantDidChange ||
  8341. // If we're making this variant active, we want to always make it active
  8342. (type === changedActiveType &&
  8343. typeState.isActive &&
  8344. !isInherited &&
  8345. propIsVariant) ||
  8346. // If we removed a higher-priority variant (i is in reverse order)
  8347. (i > removedVariantIndex && propIsVariant);
  8348. let handledRemovedValues = false;
  8349. /**
  8350. * As animations can be set as variant lists, variants or target objects, we
  8351. * coerce everything to an array if it isn't one already
  8352. */
  8353. const definitionList = Array.isArray(prop) ? prop : [prop];
  8354. /**
  8355. * Build an object of all the resolved values. We'll use this in the subsequent
  8356. * animateChanges calls to determine whether a value has changed.
  8357. */
  8358. let resolvedValues = definitionList.reduce(buildResolvedTypeValues(type), {});
  8359. if (activeDelta === false)
  8360. resolvedValues = {};
  8361. /**
  8362. * Now we need to loop through all the keys in the prev prop and this prop,
  8363. * and decide:
  8364. * 1. If the value has changed, and needs animating
  8365. * 2. If it has been removed, and needs adding to the removedKeys set
  8366. * 3. If it has been removed in a higher priority type and needs animating
  8367. * 4. If it hasn't been removed in a higher priority but hasn't changed, and
  8368. * needs adding to the type's protectedKeys list.
  8369. */
  8370. const { prevResolvedValues = {} } = typeState;
  8371. const allKeys = {
  8372. ...prevResolvedValues,
  8373. ...resolvedValues,
  8374. };
  8375. const markToAnimate = (key) => {
  8376. shouldAnimateType = true;
  8377. if (removedKeys.has(key)) {
  8378. handledRemovedValues = true;
  8379. removedKeys.delete(key);
  8380. }
  8381. typeState.needsAnimating[key] = true;
  8382. const motionValue = visualElement.getValue(key);
  8383. if (motionValue)
  8384. motionValue.liveStyle = false;
  8385. };
  8386. for (const key in allKeys) {
  8387. const next = resolvedValues[key];
  8388. const prev = prevResolvedValues[key];
  8389. // If we've already handled this we can just skip ahead
  8390. if (encounteredKeys.hasOwnProperty(key))
  8391. continue;
  8392. /**
  8393. * If the value has changed, we probably want to animate it.
  8394. */
  8395. let valueHasChanged = false;
  8396. if (isKeyframesTarget(next) && isKeyframesTarget(prev)) {
  8397. valueHasChanged = !shallowCompare(next, prev);
  8398. }
  8399. else {
  8400. valueHasChanged = next !== prev;
  8401. }
  8402. if (valueHasChanged) {
  8403. if (next !== undefined && next !== null) {
  8404. // If next is defined and doesn't equal prev, it needs animating
  8405. markToAnimate(key);
  8406. }
  8407. else {
  8408. // If it's undefined, it's been removed.
  8409. removedKeys.add(key);
  8410. }
  8411. }
  8412. else if (next !== undefined && removedKeys.has(key)) {
  8413. /**
  8414. * If next hasn't changed and it isn't undefined, we want to check if it's
  8415. * been removed by a higher priority
  8416. */
  8417. markToAnimate(key);
  8418. }
  8419. else {
  8420. /**
  8421. * If it hasn't changed, we add it to the list of protected values
  8422. * to ensure it doesn't get animated.
  8423. */
  8424. typeState.protectedKeys[key] = true;
  8425. }
  8426. }
  8427. /**
  8428. * Update the typeState so next time animateChanges is called we can compare the
  8429. * latest prop and resolvedValues to these.
  8430. */
  8431. typeState.prevProp = prop;
  8432. typeState.prevResolvedValues = resolvedValues;
  8433. /**
  8434. *
  8435. */
  8436. if (typeState.isActive) {
  8437. encounteredKeys = { ...encounteredKeys, ...resolvedValues };
  8438. }
  8439. if (isInitialRender && visualElement.blockInitialAnimation) {
  8440. shouldAnimateType = false;
  8441. }
  8442. /**
  8443. * If this is an inherited prop we want to skip this animation
  8444. * unless the inherited variants haven't changed on this render.
  8445. */
  8446. const willAnimateViaParent = isInherited && variantDidChange;
  8447. const needsAnimating = !willAnimateViaParent || handledRemovedValues;
  8448. if (shouldAnimateType && needsAnimating) {
  8449. animations.push(...definitionList.map((animation) => ({
  8450. animation: animation,
  8451. options: { type },
  8452. })));
  8453. }
  8454. }
  8455. /**
  8456. * If there are some removed value that haven't been dealt with,
  8457. * we need to create a new animation that falls back either to the value
  8458. * defined in the style prop, or the last read value.
  8459. */
  8460. if (removedKeys.size) {
  8461. const fallbackAnimation = {};
  8462. /**
  8463. * If the initial prop contains a transition we can use that, otherwise
  8464. * allow the animation function to use the visual element's default.
  8465. */
  8466. if (typeof props.initial !== "boolean") {
  8467. const initialTransition = resolveVariant(visualElement, Array.isArray(props.initial)
  8468. ? props.initial[0]
  8469. : props.initial);
  8470. if (initialTransition && initialTransition.transition) {
  8471. fallbackAnimation.transition = initialTransition.transition;
  8472. }
  8473. }
  8474. removedKeys.forEach((key) => {
  8475. const fallbackTarget = visualElement.getBaseTarget(key);
  8476. const motionValue = visualElement.getValue(key);
  8477. if (motionValue)
  8478. motionValue.liveStyle = true;
  8479. // @ts-expect-error - @mattgperry to figure if we should do something here
  8480. fallbackAnimation[key] = fallbackTarget ?? null;
  8481. });
  8482. animations.push({ animation: fallbackAnimation });
  8483. }
  8484. let shouldAnimate = Boolean(animations.length);
  8485. if (isInitialRender &&
  8486. (props.initial === false || props.initial === props.animate) &&
  8487. !visualElement.manuallyAnimateOnMount) {
  8488. shouldAnimate = false;
  8489. }
  8490. isInitialRender = false;
  8491. return shouldAnimate ? animate(animations) : Promise.resolve();
  8492. }
  8493. /**
  8494. * Change whether a certain animation type is active.
  8495. */
  8496. function setActive(type, isActive) {
  8497. // If the active state hasn't changed, we can safely do nothing here
  8498. if (state[type].isActive === isActive)
  8499. return Promise.resolve();
  8500. // Propagate active change to children
  8501. visualElement.variantChildren?.forEach((child) => child.animationState?.setActive(type, isActive));
  8502. state[type].isActive = isActive;
  8503. const animations = animateChanges(type);
  8504. for (const key in state) {
  8505. state[key].protectedKeys = {};
  8506. }
  8507. return animations;
  8508. }
  8509. return {
  8510. animateChanges,
  8511. setActive,
  8512. setAnimateFunction,
  8513. getState: () => state,
  8514. reset: () => {
  8515. state = createState();
  8516. isInitialRender = true;
  8517. },
  8518. };
  8519. }
  8520. function checkVariantsDidChange(prev, next) {
  8521. if (typeof next === "string") {
  8522. return next !== prev;
  8523. }
  8524. else if (Array.isArray(next)) {
  8525. return !shallowCompare(next, prev);
  8526. }
  8527. return false;
  8528. }
  8529. function createTypeState(isActive = false) {
  8530. return {
  8531. isActive,
  8532. protectedKeys: {},
  8533. needsAnimating: {},
  8534. prevResolvedValues: {},
  8535. };
  8536. }
  8537. function createState() {
  8538. return {
  8539. animate: createTypeState(true),
  8540. whileInView: createTypeState(),
  8541. whileHover: createTypeState(),
  8542. whileTap: createTypeState(),
  8543. whileDrag: createTypeState(),
  8544. whileFocus: createTypeState(),
  8545. exit: createTypeState(),
  8546. };
  8547. }
  8548. class Feature {
  8549. constructor(node) {
  8550. this.isMounted = false;
  8551. this.node = node;
  8552. }
  8553. update() { }
  8554. }
  8555. class AnimationFeature extends Feature {
  8556. /**
  8557. * We dynamically generate the AnimationState manager as it contains a reference
  8558. * to the underlying animation library. We only want to load that if we load this,
  8559. * so people can optionally code split it out using the `m` component.
  8560. */
  8561. constructor(node) {
  8562. super(node);
  8563. node.animationState || (node.animationState = createAnimationState(node));
  8564. }
  8565. updateAnimationControlsSubscription() {
  8566. const { animate } = this.node.getProps();
  8567. if (isAnimationControls(animate)) {
  8568. this.unmountControls = animate.subscribe(this.node);
  8569. }
  8570. }
  8571. /**
  8572. * Subscribe any provided AnimationControls to the component's VisualElement
  8573. */
  8574. mount() {
  8575. this.updateAnimationControlsSubscription();
  8576. }
  8577. update() {
  8578. const { animate } = this.node.getProps();
  8579. const { animate: prevAnimate } = this.node.prevProps || {};
  8580. if (animate !== prevAnimate) {
  8581. this.updateAnimationControlsSubscription();
  8582. }
  8583. }
  8584. unmount() {
  8585. this.node.animationState.reset();
  8586. this.unmountControls?.();
  8587. }
  8588. }
  8589. let id$1 = 0;
  8590. class ExitAnimationFeature extends Feature {
  8591. constructor() {
  8592. super(...arguments);
  8593. this.id = id$1++;
  8594. }
  8595. update() {
  8596. if (!this.node.presenceContext)
  8597. return;
  8598. const { isPresent, onExitComplete } = this.node.presenceContext;
  8599. const { isPresent: prevIsPresent } = this.node.prevPresenceContext || {};
  8600. if (!this.node.animationState || isPresent === prevIsPresent) {
  8601. return;
  8602. }
  8603. const exitAnimation = this.node.animationState.setActive("exit", !isPresent);
  8604. if (onExitComplete && !isPresent) {
  8605. exitAnimation.then(() => {
  8606. onExitComplete(this.id);
  8607. });
  8608. }
  8609. }
  8610. mount() {
  8611. const { register, onExitComplete } = this.node.presenceContext || {};
  8612. if (onExitComplete) {
  8613. onExitComplete(this.id);
  8614. }
  8615. if (register) {
  8616. this.unmount = register(this.id);
  8617. }
  8618. }
  8619. unmount() { }
  8620. }
  8621. const animations = {
  8622. animation: {
  8623. Feature: AnimationFeature,
  8624. },
  8625. exit: {
  8626. Feature: ExitAnimationFeature,
  8627. },
  8628. };
  8629. function extractEventInfo(event) {
  8630. return {
  8631. point: {
  8632. x: event.pageX,
  8633. y: event.pageY,
  8634. },
  8635. };
  8636. }
  8637. const addPointerInfo = (handler) => {
  8638. return (event) => isPrimaryPointer(event) && handler(event, extractEventInfo(event));
  8639. };
  8640. function addPointerEvent(target, eventName, handler, options) {
  8641. return addDomEvent(target, eventName, addPointerInfo(handler), options);
  8642. }
  8643. // Fixes https://github.com/motiondivision/motion/issues/2270
  8644. const getContextWindow = ({ current }) => {
  8645. return current ? current.ownerDocument.defaultView : null;
  8646. };
  8647. function isRefObject(ref) {
  8648. return (ref &&
  8649. typeof ref === "object" &&
  8650. Object.prototype.hasOwnProperty.call(ref, "current"));
  8651. }
  8652. const distance = (a, b) => Math.abs(a - b);
  8653. function distance2D(a, b) {
  8654. // Multi-dimensional
  8655. const xDelta = distance(a.x, b.x);
  8656. const yDelta = distance(a.y, b.y);
  8657. return Math.sqrt(xDelta ** 2 + yDelta ** 2);
  8658. }
  8659. /**
  8660. * @internal
  8661. */
  8662. class PanSession {
  8663. constructor(event, handlers, { transformPagePoint, contextWindow, dragSnapToOrigin = false, } = {}) {
  8664. /**
  8665. * @internal
  8666. */
  8667. this.startEvent = null;
  8668. /**
  8669. * @internal
  8670. */
  8671. this.lastMoveEvent = null;
  8672. /**
  8673. * @internal
  8674. */
  8675. this.lastMoveEventInfo = null;
  8676. /**
  8677. * @internal
  8678. */
  8679. this.handlers = {};
  8680. /**
  8681. * @internal
  8682. */
  8683. this.contextWindow = window;
  8684. this.updatePoint = () => {
  8685. if (!(this.lastMoveEvent && this.lastMoveEventInfo))
  8686. return;
  8687. const info = getPanInfo(this.lastMoveEventInfo, this.history);
  8688. const isPanStarted = this.startEvent !== null;
  8689. // Only start panning if the offset is larger than 3 pixels. If we make it
  8690. // any larger than this we'll want to reset the pointer history
  8691. // on the first update to avoid visual snapping to the cursoe.
  8692. const isDistancePastThreshold = distance2D(info.offset, { x: 0, y: 0 }) >= 3;
  8693. if (!isPanStarted && !isDistancePastThreshold)
  8694. return;
  8695. const { point } = info;
  8696. const { timestamp } = frameData;
  8697. this.history.push({ ...point, timestamp });
  8698. const { onStart, onMove } = this.handlers;
  8699. if (!isPanStarted) {
  8700. onStart && onStart(this.lastMoveEvent, info);
  8701. this.startEvent = this.lastMoveEvent;
  8702. }
  8703. onMove && onMove(this.lastMoveEvent, info);
  8704. };
  8705. this.handlePointerMove = (event, info) => {
  8706. this.lastMoveEvent = event;
  8707. this.lastMoveEventInfo = transformPoint(info, this.transformPagePoint);
  8708. // Throttle mouse move event to once per frame
  8709. frame.update(this.updatePoint, true);
  8710. };
  8711. this.handlePointerUp = (event, info) => {
  8712. this.end();
  8713. const { onEnd, onSessionEnd, resumeAnimation } = this.handlers;
  8714. if (this.dragSnapToOrigin)
  8715. resumeAnimation && resumeAnimation();
  8716. if (!(this.lastMoveEvent && this.lastMoveEventInfo))
  8717. return;
  8718. const panInfo = getPanInfo(event.type === "pointercancel"
  8719. ? this.lastMoveEventInfo
  8720. : transformPoint(info, this.transformPagePoint), this.history);
  8721. if (this.startEvent && onEnd) {
  8722. onEnd(event, panInfo);
  8723. }
  8724. onSessionEnd && onSessionEnd(event, panInfo);
  8725. };
  8726. // If we have more than one touch, don't start detecting this gesture
  8727. if (!isPrimaryPointer(event))
  8728. return;
  8729. this.dragSnapToOrigin = dragSnapToOrigin;
  8730. this.handlers = handlers;
  8731. this.transformPagePoint = transformPagePoint;
  8732. this.contextWindow = contextWindow || window;
  8733. const info = extractEventInfo(event);
  8734. const initialInfo = transformPoint(info, this.transformPagePoint);
  8735. const { point } = initialInfo;
  8736. const { timestamp } = frameData;
  8737. this.history = [{ ...point, timestamp }];
  8738. const { onSessionStart } = handlers;
  8739. onSessionStart &&
  8740. onSessionStart(event, getPanInfo(initialInfo, this.history));
  8741. this.removeListeners = pipe(addPointerEvent(this.contextWindow, "pointermove", this.handlePointerMove), addPointerEvent(this.contextWindow, "pointerup", this.handlePointerUp), addPointerEvent(this.contextWindow, "pointercancel", this.handlePointerUp));
  8742. }
  8743. updateHandlers(handlers) {
  8744. this.handlers = handlers;
  8745. }
  8746. end() {
  8747. this.removeListeners && this.removeListeners();
  8748. cancelFrame(this.updatePoint);
  8749. }
  8750. }
  8751. function transformPoint(info, transformPagePoint) {
  8752. return transformPagePoint ? { point: transformPagePoint(info.point) } : info;
  8753. }
  8754. function subtractPoint(a, b) {
  8755. return { x: a.x - b.x, y: a.y - b.y };
  8756. }
  8757. function getPanInfo({ point }, history) {
  8758. return {
  8759. point,
  8760. delta: subtractPoint(point, lastDevicePoint(history)),
  8761. offset: subtractPoint(point, startDevicePoint(history)),
  8762. velocity: getVelocity(history, 0.1),
  8763. };
  8764. }
  8765. function startDevicePoint(history) {
  8766. return history[0];
  8767. }
  8768. function lastDevicePoint(history) {
  8769. return history[history.length - 1];
  8770. }
  8771. function getVelocity(history, timeDelta) {
  8772. if (history.length < 2) {
  8773. return { x: 0, y: 0 };
  8774. }
  8775. let i = history.length - 1;
  8776. let timestampedPoint = null;
  8777. const lastPoint = lastDevicePoint(history);
  8778. while (i >= 0) {
  8779. timestampedPoint = history[i];
  8780. if (lastPoint.timestamp - timestampedPoint.timestamp >
  8781. secondsToMilliseconds(timeDelta)) {
  8782. break;
  8783. }
  8784. i--;
  8785. }
  8786. if (!timestampedPoint) {
  8787. return { x: 0, y: 0 };
  8788. }
  8789. const time = millisecondsToSeconds(lastPoint.timestamp - timestampedPoint.timestamp);
  8790. if (time === 0) {
  8791. return { x: 0, y: 0 };
  8792. }
  8793. const currentVelocity = {
  8794. x: (lastPoint.x - timestampedPoint.x) / time,
  8795. y: (lastPoint.y - timestampedPoint.y) / time,
  8796. };
  8797. if (currentVelocity.x === Infinity) {
  8798. currentVelocity.x = 0;
  8799. }
  8800. if (currentVelocity.y === Infinity) {
  8801. currentVelocity.y = 0;
  8802. }
  8803. return currentVelocity;
  8804. }
  8805. /**
  8806. * Apply constraints to a point. These constraints are both physical along an
  8807. * axis, and an elastic factor that determines how much to constrain the point
  8808. * by if it does lie outside the defined parameters.
  8809. */
  8810. function applyConstraints(point, { min, max }, elastic) {
  8811. if (min !== undefined && point < min) {
  8812. // If we have a min point defined, and this is outside of that, constrain
  8813. point = elastic
  8814. ? mixNumber$1(min, point, elastic.min)
  8815. : Math.max(point, min);
  8816. }
  8817. else if (max !== undefined && point > max) {
  8818. // If we have a max point defined, and this is outside of that, constrain
  8819. point = elastic
  8820. ? mixNumber$1(max, point, elastic.max)
  8821. : Math.min(point, max);
  8822. }
  8823. return point;
  8824. }
  8825. /**
  8826. * Calculate constraints in terms of the viewport when defined relatively to the
  8827. * measured axis. This is measured from the nearest edge, so a max constraint of 200
  8828. * on an axis with a max value of 300 would return a constraint of 500 - axis length
  8829. */
  8830. function calcRelativeAxisConstraints(axis, min, max) {
  8831. return {
  8832. min: min !== undefined ? axis.min + min : undefined,
  8833. max: max !== undefined
  8834. ? axis.max + max - (axis.max - axis.min)
  8835. : undefined,
  8836. };
  8837. }
  8838. /**
  8839. * Calculate constraints in terms of the viewport when
  8840. * defined relatively to the measured bounding box.
  8841. */
  8842. function calcRelativeConstraints(layoutBox, { top, left, bottom, right }) {
  8843. return {
  8844. x: calcRelativeAxisConstraints(layoutBox.x, left, right),
  8845. y: calcRelativeAxisConstraints(layoutBox.y, top, bottom),
  8846. };
  8847. }
  8848. /**
  8849. * Calculate viewport constraints when defined as another viewport-relative axis
  8850. */
  8851. function calcViewportAxisConstraints(layoutAxis, constraintsAxis) {
  8852. let min = constraintsAxis.min - layoutAxis.min;
  8853. let max = constraintsAxis.max - layoutAxis.max;
  8854. // If the constraints axis is actually smaller than the layout axis then we can
  8855. // flip the constraints
  8856. if (constraintsAxis.max - constraintsAxis.min <
  8857. layoutAxis.max - layoutAxis.min) {
  8858. [min, max] = [max, min];
  8859. }
  8860. return { min, max };
  8861. }
  8862. /**
  8863. * Calculate viewport constraints when defined as another viewport-relative box
  8864. */
  8865. function calcViewportConstraints(layoutBox, constraintsBox) {
  8866. return {
  8867. x: calcViewportAxisConstraints(layoutBox.x, constraintsBox.x),
  8868. y: calcViewportAxisConstraints(layoutBox.y, constraintsBox.y),
  8869. };
  8870. }
  8871. /**
  8872. * Calculate a transform origin relative to the source axis, between 0-1, that results
  8873. * in an asthetically pleasing scale/transform needed to project from source to target.
  8874. */
  8875. function calcOrigin$1(source, target) {
  8876. let origin = 0.5;
  8877. const sourceLength = calcLength(source);
  8878. const targetLength = calcLength(target);
  8879. if (targetLength > sourceLength) {
  8880. origin = progress(target.min, target.max - sourceLength, source.min);
  8881. }
  8882. else if (sourceLength > targetLength) {
  8883. origin = progress(source.min, source.max - targetLength, target.min);
  8884. }
  8885. return clamp(0, 1, origin);
  8886. }
  8887. /**
  8888. * Rebase the calculated viewport constraints relative to the layout.min point.
  8889. */
  8890. function rebaseAxisConstraints(layout, constraints) {
  8891. const relativeConstraints = {};
  8892. if (constraints.min !== undefined) {
  8893. relativeConstraints.min = constraints.min - layout.min;
  8894. }
  8895. if (constraints.max !== undefined) {
  8896. relativeConstraints.max = constraints.max - layout.min;
  8897. }
  8898. return relativeConstraints;
  8899. }
  8900. const defaultElastic = 0.35;
  8901. /**
  8902. * Accepts a dragElastic prop and returns resolved elastic values for each axis.
  8903. */
  8904. function resolveDragElastic(dragElastic = defaultElastic) {
  8905. if (dragElastic === false) {
  8906. dragElastic = 0;
  8907. }
  8908. else if (dragElastic === true) {
  8909. dragElastic = defaultElastic;
  8910. }
  8911. return {
  8912. x: resolveAxisElastic(dragElastic, "left", "right"),
  8913. y: resolveAxisElastic(dragElastic, "top", "bottom"),
  8914. };
  8915. }
  8916. function resolveAxisElastic(dragElastic, minLabel, maxLabel) {
  8917. return {
  8918. min: resolvePointElastic(dragElastic, minLabel),
  8919. max: resolvePointElastic(dragElastic, maxLabel),
  8920. };
  8921. }
  8922. function resolvePointElastic(dragElastic, label) {
  8923. return typeof dragElastic === "number"
  8924. ? dragElastic
  8925. : dragElastic[label] || 0;
  8926. }
  8927. const elementDragControls = new WeakMap();
  8928. /**
  8929. *
  8930. */
  8931. // let latestPointerEvent: PointerEvent
  8932. class VisualElementDragControls {
  8933. constructor(visualElement) {
  8934. this.openDragLock = null;
  8935. this.isDragging = false;
  8936. this.currentDirection = null;
  8937. this.originPoint = { x: 0, y: 0 };
  8938. /**
  8939. * The permitted boundaries of travel, in pixels.
  8940. */
  8941. this.constraints = false;
  8942. this.hasMutatedConstraints = false;
  8943. /**
  8944. * The per-axis resolved elastic values.
  8945. */
  8946. this.elastic = createBox();
  8947. this.visualElement = visualElement;
  8948. }
  8949. start(originEvent, { snapToCursor = false } = {}) {
  8950. /**
  8951. * Don't start dragging if this component is exiting
  8952. */
  8953. const { presenceContext } = this.visualElement;
  8954. if (presenceContext && presenceContext.isPresent === false)
  8955. return;
  8956. const onSessionStart = (event) => {
  8957. const { dragSnapToOrigin } = this.getProps();
  8958. // Stop or pause any animations on both axis values immediately. This allows the user to throw and catch
  8959. // the component.
  8960. dragSnapToOrigin ? this.pauseAnimation() : this.stopAnimation();
  8961. if (snapToCursor) {
  8962. this.snapToCursor(extractEventInfo(event).point);
  8963. }
  8964. };
  8965. const onStart = (event, info) => {
  8966. // Attempt to grab the global drag gesture lock - maybe make this part of PanSession
  8967. const { drag, dragPropagation, onDragStart } = this.getProps();
  8968. if (drag && !dragPropagation) {
  8969. if (this.openDragLock)
  8970. this.openDragLock();
  8971. this.openDragLock = setDragLock(drag);
  8972. // If we don 't have the lock, don't start dragging
  8973. if (!this.openDragLock)
  8974. return;
  8975. }
  8976. this.isDragging = true;
  8977. this.currentDirection = null;
  8978. this.resolveConstraints();
  8979. if (this.visualElement.projection) {
  8980. this.visualElement.projection.isAnimationBlocked = true;
  8981. this.visualElement.projection.target = undefined;
  8982. }
  8983. /**
  8984. * Record gesture origin
  8985. */
  8986. eachAxis((axis) => {
  8987. let current = this.getAxisMotionValue(axis).get() || 0;
  8988. /**
  8989. * If the MotionValue is a percentage value convert to px
  8990. */
  8991. if (percent.test(current)) {
  8992. const { projection } = this.visualElement;
  8993. if (projection && projection.layout) {
  8994. const measuredAxis = projection.layout.layoutBox[axis];
  8995. if (measuredAxis) {
  8996. const length = calcLength(measuredAxis);
  8997. current = length * (parseFloat(current) / 100);
  8998. }
  8999. }
  9000. }
  9001. this.originPoint[axis] = current;
  9002. });
  9003. // Fire onDragStart event
  9004. if (onDragStart) {
  9005. frame.postRender(() => onDragStart(event, info));
  9006. }
  9007. addValueToWillChange(this.visualElement, "transform");
  9008. const { animationState } = this.visualElement;
  9009. animationState && animationState.setActive("whileDrag", true);
  9010. };
  9011. const onMove = (event, info) => {
  9012. // latestPointerEvent = event
  9013. const { dragPropagation, dragDirectionLock, onDirectionLock, onDrag, } = this.getProps();
  9014. // If we didn't successfully receive the gesture lock, early return.
  9015. if (!dragPropagation && !this.openDragLock)
  9016. return;
  9017. const { offset } = info;
  9018. // Attempt to detect drag direction if directionLock is true
  9019. if (dragDirectionLock && this.currentDirection === null) {
  9020. this.currentDirection = getCurrentDirection(offset);
  9021. // If we've successfully set a direction, notify listener
  9022. if (this.currentDirection !== null) {
  9023. onDirectionLock && onDirectionLock(this.currentDirection);
  9024. }
  9025. return;
  9026. }
  9027. // Update each point with the latest position
  9028. this.updateAxis("x", info.point, offset);
  9029. this.updateAxis("y", info.point, offset);
  9030. /**
  9031. * Ideally we would leave the renderer to fire naturally at the end of
  9032. * this frame but if the element is about to change layout as the result
  9033. * of a re-render we want to ensure the browser can read the latest
  9034. * bounding box to ensure the pointer and element don't fall out of sync.
  9035. */
  9036. this.visualElement.render();
  9037. /**
  9038. * This must fire after the render call as it might trigger a state
  9039. * change which itself might trigger a layout update.
  9040. */
  9041. onDrag && onDrag(event, info);
  9042. };
  9043. const onSessionEnd = (event, info) => this.stop(event, info);
  9044. const resumeAnimation = () => eachAxis((axis) => this.getAnimationState(axis) === "paused" &&
  9045. this.getAxisMotionValue(axis).animation?.play());
  9046. const { dragSnapToOrigin } = this.getProps();
  9047. this.panSession = new PanSession(originEvent, {
  9048. onSessionStart,
  9049. onStart,
  9050. onMove,
  9051. onSessionEnd,
  9052. resumeAnimation,
  9053. }, {
  9054. transformPagePoint: this.visualElement.getTransformPagePoint(),
  9055. dragSnapToOrigin,
  9056. contextWindow: getContextWindow(this.visualElement),
  9057. });
  9058. }
  9059. stop(event, info) {
  9060. const isDragging = this.isDragging;
  9061. this.cancel();
  9062. if (!isDragging)
  9063. return;
  9064. const { velocity } = info;
  9065. this.startAnimation(velocity);
  9066. const { onDragEnd } = this.getProps();
  9067. if (onDragEnd) {
  9068. frame.postRender(() => onDragEnd(event, info));
  9069. }
  9070. }
  9071. cancel() {
  9072. this.isDragging = false;
  9073. const { projection, animationState } = this.visualElement;
  9074. if (projection) {
  9075. projection.isAnimationBlocked = false;
  9076. }
  9077. this.panSession && this.panSession.end();
  9078. this.panSession = undefined;
  9079. const { dragPropagation } = this.getProps();
  9080. if (!dragPropagation && this.openDragLock) {
  9081. this.openDragLock();
  9082. this.openDragLock = null;
  9083. }
  9084. animationState && animationState.setActive("whileDrag", false);
  9085. }
  9086. updateAxis(axis, _point, offset) {
  9087. const { drag } = this.getProps();
  9088. // If we're not dragging this axis, do an early return.
  9089. if (!offset || !shouldDrag(axis, drag, this.currentDirection))
  9090. return;
  9091. const axisValue = this.getAxisMotionValue(axis);
  9092. let next = this.originPoint[axis] + offset[axis];
  9093. // Apply constraints
  9094. if (this.constraints && this.constraints[axis]) {
  9095. next = applyConstraints(next, this.constraints[axis], this.elastic[axis]);
  9096. }
  9097. axisValue.set(next);
  9098. }
  9099. resolveConstraints() {
  9100. const { dragConstraints, dragElastic } = this.getProps();
  9101. const layout = this.visualElement.projection &&
  9102. !this.visualElement.projection.layout
  9103. ? this.visualElement.projection.measure(false)
  9104. : this.visualElement.projection?.layout;
  9105. const prevConstraints = this.constraints;
  9106. if (dragConstraints && isRefObject(dragConstraints)) {
  9107. if (!this.constraints) {
  9108. this.constraints = this.resolveRefConstraints();
  9109. }
  9110. }
  9111. else {
  9112. if (dragConstraints && layout) {
  9113. this.constraints = calcRelativeConstraints(layout.layoutBox, dragConstraints);
  9114. }
  9115. else {
  9116. this.constraints = false;
  9117. }
  9118. }
  9119. this.elastic = resolveDragElastic(dragElastic);
  9120. /**
  9121. * If we're outputting to external MotionValues, we want to rebase the measured constraints
  9122. * from viewport-relative to component-relative.
  9123. */
  9124. if (prevConstraints !== this.constraints &&
  9125. layout &&
  9126. this.constraints &&
  9127. !this.hasMutatedConstraints) {
  9128. eachAxis((axis) => {
  9129. if (this.constraints !== false &&
  9130. this.getAxisMotionValue(axis)) {
  9131. this.constraints[axis] = rebaseAxisConstraints(layout.layoutBox[axis], this.constraints[axis]);
  9132. }
  9133. });
  9134. }
  9135. }
  9136. resolveRefConstraints() {
  9137. const { dragConstraints: constraints, onMeasureDragConstraints } = this.getProps();
  9138. if (!constraints || !isRefObject(constraints))
  9139. return false;
  9140. const constraintsElement = constraints.current;
  9141. exports.invariant(constraintsElement !== null, "If `dragConstraints` is set as a React ref, that ref must be passed to another component's `ref` prop.");
  9142. const { projection } = this.visualElement;
  9143. // TODO
  9144. if (!projection || !projection.layout)
  9145. return false;
  9146. const constraintsBox = measurePageBox(constraintsElement, projection.root, this.visualElement.getTransformPagePoint());
  9147. let measuredConstraints = calcViewportConstraints(projection.layout.layoutBox, constraintsBox);
  9148. /**
  9149. * If there's an onMeasureDragConstraints listener we call it and
  9150. * if different constraints are returned, set constraints to that
  9151. */
  9152. if (onMeasureDragConstraints) {
  9153. const userConstraints = onMeasureDragConstraints(convertBoxToBoundingBox(measuredConstraints));
  9154. this.hasMutatedConstraints = !!userConstraints;
  9155. if (userConstraints) {
  9156. measuredConstraints = convertBoundingBoxToBox(userConstraints);
  9157. }
  9158. }
  9159. return measuredConstraints;
  9160. }
  9161. startAnimation(velocity) {
  9162. const { drag, dragMomentum, dragElastic, dragTransition, dragSnapToOrigin, onDragTransitionEnd, } = this.getProps();
  9163. const constraints = this.constraints || {};
  9164. const momentumAnimations = eachAxis((axis) => {
  9165. if (!shouldDrag(axis, drag, this.currentDirection)) {
  9166. return;
  9167. }
  9168. let transition = (constraints && constraints[axis]) || {};
  9169. if (dragSnapToOrigin)
  9170. transition = { min: 0, max: 0 };
  9171. /**
  9172. * Overdamp the boundary spring if `dragElastic` is disabled. There's still a frame
  9173. * of spring animations so we should look into adding a disable spring option to `inertia`.
  9174. * We could do something here where we affect the `bounceStiffness` and `bounceDamping`
  9175. * using the value of `dragElastic`.
  9176. */
  9177. const bounceStiffness = dragElastic ? 200 : 1000000;
  9178. const bounceDamping = dragElastic ? 40 : 10000000;
  9179. const inertia = {
  9180. type: "inertia",
  9181. velocity: dragMomentum ? velocity[axis] : 0,
  9182. bounceStiffness,
  9183. bounceDamping,
  9184. timeConstant: 750,
  9185. restDelta: 1,
  9186. restSpeed: 10,
  9187. ...dragTransition,
  9188. ...transition,
  9189. };
  9190. // If we're not animating on an externally-provided `MotionValue` we can use the
  9191. // component's animation controls which will handle interactions with whileHover (etc),
  9192. // otherwise we just have to animate the `MotionValue` itself.
  9193. return this.startAxisValueAnimation(axis, inertia);
  9194. });
  9195. // Run all animations and then resolve the new drag constraints.
  9196. return Promise.all(momentumAnimations).then(onDragTransitionEnd);
  9197. }
  9198. startAxisValueAnimation(axis, transition) {
  9199. const axisValue = this.getAxisMotionValue(axis);
  9200. addValueToWillChange(this.visualElement, axis);
  9201. return axisValue.start(animateMotionValue(axis, axisValue, 0, transition, this.visualElement, false));
  9202. }
  9203. stopAnimation() {
  9204. eachAxis((axis) => this.getAxisMotionValue(axis).stop());
  9205. }
  9206. pauseAnimation() {
  9207. eachAxis((axis) => this.getAxisMotionValue(axis).animation?.pause());
  9208. }
  9209. getAnimationState(axis) {
  9210. return this.getAxisMotionValue(axis).animation?.state;
  9211. }
  9212. /**
  9213. * Drag works differently depending on which props are provided.
  9214. *
  9215. * - If _dragX and _dragY are provided, we output the gesture delta directly to those motion values.
  9216. * - Otherwise, we apply the delta to the x/y motion values.
  9217. */
  9218. getAxisMotionValue(axis) {
  9219. const dragKey = `_drag${axis.toUpperCase()}`;
  9220. const props = this.visualElement.getProps();
  9221. const externalMotionValue = props[dragKey];
  9222. return externalMotionValue
  9223. ? externalMotionValue
  9224. : this.visualElement.getValue(axis, (props.initial
  9225. ? props.initial[axis]
  9226. : undefined) || 0);
  9227. }
  9228. snapToCursor(point) {
  9229. eachAxis((axis) => {
  9230. const { drag } = this.getProps();
  9231. // If we're not dragging this axis, do an early return.
  9232. if (!shouldDrag(axis, drag, this.currentDirection))
  9233. return;
  9234. const { projection } = this.visualElement;
  9235. const axisValue = this.getAxisMotionValue(axis);
  9236. if (projection && projection.layout) {
  9237. const { min, max } = projection.layout.layoutBox[axis];
  9238. axisValue.set(point[axis] - mixNumber$1(min, max, 0.5));
  9239. }
  9240. });
  9241. }
  9242. /**
  9243. * When the viewport resizes we want to check if the measured constraints
  9244. * have changed and, if so, reposition the element within those new constraints
  9245. * relative to where it was before the resize.
  9246. */
  9247. scalePositionWithinConstraints() {
  9248. if (!this.visualElement.current)
  9249. return;
  9250. const { drag, dragConstraints } = this.getProps();
  9251. const { projection } = this.visualElement;
  9252. if (!isRefObject(dragConstraints) || !projection || !this.constraints)
  9253. return;
  9254. /**
  9255. * Stop current animations as there can be visual glitching if we try to do
  9256. * this mid-animation
  9257. */
  9258. this.stopAnimation();
  9259. /**
  9260. * Record the relative position of the dragged element relative to the
  9261. * constraints box and save as a progress value.
  9262. */
  9263. const boxProgress = { x: 0, y: 0 };
  9264. eachAxis((axis) => {
  9265. const axisValue = this.getAxisMotionValue(axis);
  9266. if (axisValue && this.constraints !== false) {
  9267. const latest = axisValue.get();
  9268. boxProgress[axis] = calcOrigin$1({ min: latest, max: latest }, this.constraints[axis]);
  9269. }
  9270. });
  9271. /**
  9272. * Update the layout of this element and resolve the latest drag constraints
  9273. */
  9274. const { transformTemplate } = this.visualElement.getProps();
  9275. this.visualElement.current.style.transform = transformTemplate
  9276. ? transformTemplate({}, "")
  9277. : "none";
  9278. projection.root && projection.root.updateScroll();
  9279. projection.updateLayout();
  9280. this.resolveConstraints();
  9281. /**
  9282. * For each axis, calculate the current progress of the layout axis
  9283. * within the new constraints.
  9284. */
  9285. eachAxis((axis) => {
  9286. if (!shouldDrag(axis, drag, null))
  9287. return;
  9288. /**
  9289. * Calculate a new transform based on the previous box progress
  9290. */
  9291. const axisValue = this.getAxisMotionValue(axis);
  9292. const { min, max } = this.constraints[axis];
  9293. axisValue.set(mixNumber$1(min, max, boxProgress[axis]));
  9294. });
  9295. }
  9296. addListeners() {
  9297. if (!this.visualElement.current)
  9298. return;
  9299. elementDragControls.set(this.visualElement, this);
  9300. const element = this.visualElement.current;
  9301. /**
  9302. * Attach a pointerdown event listener on this DOM element to initiate drag tracking.
  9303. */
  9304. const stopPointerListener = addPointerEvent(element, "pointerdown", (event) => {
  9305. const { drag, dragListener = true } = this.getProps();
  9306. drag && dragListener && this.start(event);
  9307. });
  9308. const measureDragConstraints = () => {
  9309. const { dragConstraints } = this.getProps();
  9310. if (isRefObject(dragConstraints) && dragConstraints.current) {
  9311. this.constraints = this.resolveRefConstraints();
  9312. }
  9313. };
  9314. const { projection } = this.visualElement;
  9315. const stopMeasureLayoutListener = projection.addEventListener("measure", measureDragConstraints);
  9316. if (projection && !projection.layout) {
  9317. projection.root && projection.root.updateScroll();
  9318. projection.updateLayout();
  9319. }
  9320. frame.read(measureDragConstraints);
  9321. /**
  9322. * Attach a window resize listener to scale the draggable target within its defined
  9323. * constraints as the window resizes.
  9324. */
  9325. const stopResizeListener = addDomEvent(window, "resize", () => this.scalePositionWithinConstraints());
  9326. /**
  9327. * If the element's layout changes, calculate the delta and apply that to
  9328. * the drag gesture's origin point.
  9329. */
  9330. const stopLayoutUpdateListener = projection.addEventListener("didUpdate", (({ delta, hasLayoutChanged }) => {
  9331. if (this.isDragging && hasLayoutChanged) {
  9332. eachAxis((axis) => {
  9333. const motionValue = this.getAxisMotionValue(axis);
  9334. if (!motionValue)
  9335. return;
  9336. this.originPoint[axis] += delta[axis].translate;
  9337. motionValue.set(motionValue.get() + delta[axis].translate);
  9338. });
  9339. this.visualElement.render();
  9340. }
  9341. }));
  9342. return () => {
  9343. stopResizeListener();
  9344. stopPointerListener();
  9345. stopMeasureLayoutListener();
  9346. stopLayoutUpdateListener && stopLayoutUpdateListener();
  9347. };
  9348. }
  9349. getProps() {
  9350. const props = this.visualElement.getProps();
  9351. const { drag = false, dragDirectionLock = false, dragPropagation = false, dragConstraints = false, dragElastic = defaultElastic, dragMomentum = true, } = props;
  9352. return {
  9353. ...props,
  9354. drag,
  9355. dragDirectionLock,
  9356. dragPropagation,
  9357. dragConstraints,
  9358. dragElastic,
  9359. dragMomentum,
  9360. };
  9361. }
  9362. }
  9363. function shouldDrag(direction, drag, currentDirection) {
  9364. return ((drag === true || drag === direction) &&
  9365. (currentDirection === null || currentDirection === direction));
  9366. }
  9367. /**
  9368. * Based on an x/y offset determine the current drag direction. If both axis' offsets are lower
  9369. * than the provided threshold, return `null`.
  9370. *
  9371. * @param offset - The x/y offset from origin.
  9372. * @param lockThreshold - (Optional) - the minimum absolute offset before we can determine a drag direction.
  9373. */
  9374. function getCurrentDirection(offset, lockThreshold = 10) {
  9375. let direction = null;
  9376. if (Math.abs(offset.y) > lockThreshold) {
  9377. direction = "y";
  9378. }
  9379. else if (Math.abs(offset.x) > lockThreshold) {
  9380. direction = "x";
  9381. }
  9382. return direction;
  9383. }
  9384. class DragGesture extends Feature {
  9385. constructor(node) {
  9386. super(node);
  9387. this.removeGroupControls = noop;
  9388. this.removeListeners = noop;
  9389. this.controls = new VisualElementDragControls(node);
  9390. }
  9391. mount() {
  9392. // If we've been provided a DragControls for manual control over the drag gesture,
  9393. // subscribe this component to it on mount.
  9394. const { dragControls } = this.node.getProps();
  9395. if (dragControls) {
  9396. this.removeGroupControls = dragControls.subscribe(this.controls);
  9397. }
  9398. this.removeListeners = this.controls.addListeners() || noop;
  9399. }
  9400. unmount() {
  9401. this.removeGroupControls();
  9402. this.removeListeners();
  9403. }
  9404. }
  9405. const asyncHandler = (handler) => (event, info) => {
  9406. if (handler) {
  9407. frame.postRender(() => handler(event, info));
  9408. }
  9409. };
  9410. class PanGesture extends Feature {
  9411. constructor() {
  9412. super(...arguments);
  9413. this.removePointerDownListener = noop;
  9414. }
  9415. onPointerDown(pointerDownEvent) {
  9416. this.session = new PanSession(pointerDownEvent, this.createPanHandlers(), {
  9417. transformPagePoint: this.node.getTransformPagePoint(),
  9418. contextWindow: getContextWindow(this.node),
  9419. });
  9420. }
  9421. createPanHandlers() {
  9422. const { onPanSessionStart, onPanStart, onPan, onPanEnd } = this.node.getProps();
  9423. return {
  9424. onSessionStart: asyncHandler(onPanSessionStart),
  9425. onStart: asyncHandler(onPanStart),
  9426. onMove: onPan,
  9427. onEnd: (event, info) => {
  9428. delete this.session;
  9429. if (onPanEnd) {
  9430. frame.postRender(() => onPanEnd(event, info));
  9431. }
  9432. },
  9433. };
  9434. }
  9435. mount() {
  9436. this.removePointerDownListener = addPointerEvent(this.node.current, "pointerdown", (event) => this.onPointerDown(event));
  9437. }
  9438. update() {
  9439. this.session && this.session.updateHandlers(this.createPanHandlers());
  9440. }
  9441. unmount() {
  9442. this.removePointerDownListener();
  9443. this.session && this.session.end();
  9444. }
  9445. }
  9446. /**
  9447. * Internal, exported only for usage in Framer
  9448. */
  9449. const SwitchLayoutGroupContext = React$1.createContext({});
  9450. class MeasureLayoutWithContext extends React$1.Component {
  9451. /**
  9452. * This only mounts projection nodes for components that
  9453. * need measuring, we might want to do it for all components
  9454. * in order to incorporate transforms
  9455. */
  9456. componentDidMount() {
  9457. const { visualElement, layoutGroup, switchLayoutGroup, layoutId } = this.props;
  9458. const { projection } = visualElement;
  9459. addScaleCorrector(defaultScaleCorrectors);
  9460. if (projection) {
  9461. if (layoutGroup.group)
  9462. layoutGroup.group.add(projection);
  9463. if (switchLayoutGroup && switchLayoutGroup.register && layoutId) {
  9464. switchLayoutGroup.register(projection);
  9465. }
  9466. projection.root.didUpdate();
  9467. projection.addEventListener("animationComplete", () => {
  9468. this.safeToRemove();
  9469. });
  9470. projection.setOptions({
  9471. ...projection.options,
  9472. onExitComplete: () => this.safeToRemove(),
  9473. });
  9474. }
  9475. globalProjectionState.hasEverUpdated = true;
  9476. }
  9477. getSnapshotBeforeUpdate(prevProps) {
  9478. const { layoutDependency, visualElement, drag, isPresent } = this.props;
  9479. const projection = visualElement.projection;
  9480. if (!projection)
  9481. return null;
  9482. /**
  9483. * TODO: We use this data in relegate to determine whether to
  9484. * promote a previous element. There's no guarantee its presence data
  9485. * will have updated by this point - if a bug like this arises it will
  9486. * have to be that we markForRelegation and then find a new lead some other way,
  9487. * perhaps in didUpdate
  9488. */
  9489. projection.isPresent = isPresent;
  9490. if (drag ||
  9491. prevProps.layoutDependency !== layoutDependency ||
  9492. layoutDependency === undefined ||
  9493. prevProps.isPresent !== isPresent) {
  9494. projection.willUpdate();
  9495. }
  9496. else {
  9497. this.safeToRemove();
  9498. }
  9499. if (prevProps.isPresent !== isPresent) {
  9500. if (isPresent) {
  9501. projection.promote();
  9502. }
  9503. else if (!projection.relegate()) {
  9504. /**
  9505. * If there's another stack member taking over from this one,
  9506. * it's in charge of the exit animation and therefore should
  9507. * be in charge of the safe to remove. Otherwise we call it here.
  9508. */
  9509. frame.postRender(() => {
  9510. const stack = projection.getStack();
  9511. if (!stack || !stack.members.length) {
  9512. this.safeToRemove();
  9513. }
  9514. });
  9515. }
  9516. }
  9517. return null;
  9518. }
  9519. componentDidUpdate() {
  9520. const { projection } = this.props.visualElement;
  9521. if (projection) {
  9522. projection.root.didUpdate();
  9523. microtask.postRender(() => {
  9524. if (!projection.currentAnimation && projection.isLead()) {
  9525. this.safeToRemove();
  9526. }
  9527. });
  9528. }
  9529. }
  9530. componentWillUnmount() {
  9531. const { visualElement, layoutGroup, switchLayoutGroup: promoteContext, } = this.props;
  9532. const { projection } = visualElement;
  9533. if (projection) {
  9534. projection.scheduleCheckAfterUnmount();
  9535. if (layoutGroup && layoutGroup.group)
  9536. layoutGroup.group.remove(projection);
  9537. if (promoteContext && promoteContext.deregister)
  9538. promoteContext.deregister(projection);
  9539. }
  9540. }
  9541. safeToRemove() {
  9542. const { safeToRemove } = this.props;
  9543. safeToRemove && safeToRemove();
  9544. }
  9545. render() {
  9546. return null;
  9547. }
  9548. }
  9549. function MeasureLayout(props) {
  9550. const [isPresent, safeToRemove] = usePresence();
  9551. const layoutGroup = React$1.useContext(LayoutGroupContext);
  9552. return (jsx(MeasureLayoutWithContext, { ...props, layoutGroup: layoutGroup, switchLayoutGroup: React$1.useContext(SwitchLayoutGroupContext), isPresent: isPresent, safeToRemove: safeToRemove }));
  9553. }
  9554. const defaultScaleCorrectors = {
  9555. borderRadius: {
  9556. ...correctBorderRadius,
  9557. applyTo: [
  9558. "borderTopLeftRadius",
  9559. "borderTopRightRadius",
  9560. "borderBottomLeftRadius",
  9561. "borderBottomRightRadius",
  9562. ],
  9563. },
  9564. borderTopLeftRadius: correctBorderRadius,
  9565. borderTopRightRadius: correctBorderRadius,
  9566. borderBottomLeftRadius: correctBorderRadius,
  9567. borderBottomRightRadius: correctBorderRadius,
  9568. boxShadow: correctBoxShadow,
  9569. };
  9570. const drag = {
  9571. pan: {
  9572. Feature: PanGesture,
  9573. },
  9574. drag: {
  9575. Feature: DragGesture,
  9576. ProjectionNode: HTMLProjectionNode,
  9577. MeasureLayout,
  9578. },
  9579. };
  9580. function handleHoverEvent(node, event, lifecycle) {
  9581. const { props } = node;
  9582. if (node.animationState && props.whileHover) {
  9583. node.animationState.setActive("whileHover", lifecycle === "Start");
  9584. }
  9585. const eventName = ("onHover" + lifecycle);
  9586. const callback = props[eventName];
  9587. if (callback) {
  9588. frame.postRender(() => callback(event, extractEventInfo(event)));
  9589. }
  9590. }
  9591. class HoverGesture extends Feature {
  9592. mount() {
  9593. const { current } = this.node;
  9594. if (!current)
  9595. return;
  9596. this.unmount = hover(current, (_element, startEvent) => {
  9597. handleHoverEvent(this.node, startEvent, "Start");
  9598. return (endEvent) => handleHoverEvent(this.node, endEvent, "End");
  9599. });
  9600. }
  9601. unmount() { }
  9602. }
  9603. class FocusGesture extends Feature {
  9604. constructor() {
  9605. super(...arguments);
  9606. this.isActive = false;
  9607. }
  9608. onFocus() {
  9609. let isFocusVisible = false;
  9610. /**
  9611. * If this element doesn't match focus-visible then don't
  9612. * apply whileHover. But, if matches throws that focus-visible
  9613. * is not a valid selector then in that browser outline styles will be applied
  9614. * to the element by default and we want to match that behaviour with whileFocus.
  9615. */
  9616. try {
  9617. isFocusVisible = this.node.current.matches(":focus-visible");
  9618. }
  9619. catch (e) {
  9620. isFocusVisible = true;
  9621. }
  9622. if (!isFocusVisible || !this.node.animationState)
  9623. return;
  9624. this.node.animationState.setActive("whileFocus", true);
  9625. this.isActive = true;
  9626. }
  9627. onBlur() {
  9628. if (!this.isActive || !this.node.animationState)
  9629. return;
  9630. this.node.animationState.setActive("whileFocus", false);
  9631. this.isActive = false;
  9632. }
  9633. mount() {
  9634. this.unmount = pipe(addDomEvent(this.node.current, "focus", () => this.onFocus()), addDomEvent(this.node.current, "blur", () => this.onBlur()));
  9635. }
  9636. unmount() { }
  9637. }
  9638. function handlePressEvent(node, event, lifecycle) {
  9639. const { props } = node;
  9640. if (node.current instanceof HTMLButtonElement && node.current.disabled) {
  9641. return;
  9642. }
  9643. if (node.animationState && props.whileTap) {
  9644. node.animationState.setActive("whileTap", lifecycle === "Start");
  9645. }
  9646. const eventName = ("onTap" + (lifecycle === "End" ? "" : lifecycle));
  9647. const callback = props[eventName];
  9648. if (callback) {
  9649. frame.postRender(() => callback(event, extractEventInfo(event)));
  9650. }
  9651. }
  9652. class PressGesture extends Feature {
  9653. mount() {
  9654. const { current } = this.node;
  9655. if (!current)
  9656. return;
  9657. this.unmount = press(current, (_element, startEvent) => {
  9658. handlePressEvent(this.node, startEvent, "Start");
  9659. return (endEvent, { success }) => handlePressEvent(this.node, endEvent, success ? "End" : "Cancel");
  9660. }, { useGlobalTarget: this.node.props.globalTapTarget });
  9661. }
  9662. unmount() { }
  9663. }
  9664. /**
  9665. * Map an IntersectionHandler callback to an element. We only ever make one handler for one
  9666. * element, so even though these handlers might all be triggered by different
  9667. * observers, we can keep them in the same map.
  9668. */
  9669. const observerCallbacks = new WeakMap();
  9670. /**
  9671. * Multiple observers can be created for multiple element/document roots. Each with
  9672. * different settings. So here we store dictionaries of observers to each root,
  9673. * using serialised settings (threshold/margin) as lookup keys.
  9674. */
  9675. const observers = new WeakMap();
  9676. const fireObserverCallback = (entry) => {
  9677. const callback = observerCallbacks.get(entry.target);
  9678. callback && callback(entry);
  9679. };
  9680. const fireAllObserverCallbacks = (entries) => {
  9681. entries.forEach(fireObserverCallback);
  9682. };
  9683. function initIntersectionObserver({ root, ...options }) {
  9684. const lookupRoot = root || document;
  9685. /**
  9686. * If we don't have an observer lookup map for this root, create one.
  9687. */
  9688. if (!observers.has(lookupRoot)) {
  9689. observers.set(lookupRoot, {});
  9690. }
  9691. const rootObservers = observers.get(lookupRoot);
  9692. const key = JSON.stringify(options);
  9693. /**
  9694. * If we don't have an observer for this combination of root and settings,
  9695. * create one.
  9696. */
  9697. if (!rootObservers[key]) {
  9698. rootObservers[key] = new IntersectionObserver(fireAllObserverCallbacks, { root, ...options });
  9699. }
  9700. return rootObservers[key];
  9701. }
  9702. function observeIntersection(element, options, callback) {
  9703. const rootInteresectionObserver = initIntersectionObserver(options);
  9704. observerCallbacks.set(element, callback);
  9705. rootInteresectionObserver.observe(element);
  9706. return () => {
  9707. observerCallbacks.delete(element);
  9708. rootInteresectionObserver.unobserve(element);
  9709. };
  9710. }
  9711. const thresholdNames = {
  9712. some: 0,
  9713. all: 1,
  9714. };
  9715. class InViewFeature extends Feature {
  9716. constructor() {
  9717. super(...arguments);
  9718. this.hasEnteredView = false;
  9719. this.isInView = false;
  9720. }
  9721. startObserver() {
  9722. this.unmount();
  9723. const { viewport = {} } = this.node.getProps();
  9724. const { root, margin: rootMargin, amount = "some", once } = viewport;
  9725. const options = {
  9726. root: root ? root.current : undefined,
  9727. rootMargin,
  9728. threshold: typeof amount === "number" ? amount : thresholdNames[amount],
  9729. };
  9730. const onIntersectionUpdate = (entry) => {
  9731. const { isIntersecting } = entry;
  9732. /**
  9733. * If there's been no change in the viewport state, early return.
  9734. */
  9735. if (this.isInView === isIntersecting)
  9736. return;
  9737. this.isInView = isIntersecting;
  9738. /**
  9739. * Handle hasEnteredView. If this is only meant to run once, and
  9740. * element isn't visible, early return. Otherwise set hasEnteredView to true.
  9741. */
  9742. if (once && !isIntersecting && this.hasEnteredView) {
  9743. return;
  9744. }
  9745. else if (isIntersecting) {
  9746. this.hasEnteredView = true;
  9747. }
  9748. if (this.node.animationState) {
  9749. this.node.animationState.setActive("whileInView", isIntersecting);
  9750. }
  9751. /**
  9752. * Use the latest committed props rather than the ones in scope
  9753. * when this observer is created
  9754. */
  9755. const { onViewportEnter, onViewportLeave } = this.node.getProps();
  9756. const callback = isIntersecting ? onViewportEnter : onViewportLeave;
  9757. callback && callback(entry);
  9758. };
  9759. return observeIntersection(this.node.current, options, onIntersectionUpdate);
  9760. }
  9761. mount() {
  9762. this.startObserver();
  9763. }
  9764. update() {
  9765. if (typeof IntersectionObserver === "undefined")
  9766. return;
  9767. const { props, prevProps } = this.node;
  9768. const hasOptionsChanged = ["amount", "margin", "root"].some(hasViewportOptionChanged(props, prevProps));
  9769. if (hasOptionsChanged) {
  9770. this.startObserver();
  9771. }
  9772. }
  9773. unmount() { }
  9774. }
  9775. function hasViewportOptionChanged({ viewport = {} }, { viewport: prevViewport = {} } = {}) {
  9776. return (name) => viewport[name] !== prevViewport[name];
  9777. }
  9778. const gestureAnimations = {
  9779. inView: {
  9780. Feature: InViewFeature,
  9781. },
  9782. tap: {
  9783. Feature: PressGesture,
  9784. },
  9785. focus: {
  9786. Feature: FocusGesture,
  9787. },
  9788. hover: {
  9789. Feature: HoverGesture,
  9790. },
  9791. };
  9792. const layout = {
  9793. layout: {
  9794. ProjectionNode: HTMLProjectionNode,
  9795. MeasureLayout,
  9796. },
  9797. };
  9798. const MotionContext = /* @__PURE__ */ React$1.createContext({});
  9799. function getCurrentTreeVariants(props, context) {
  9800. if (isControllingVariants(props)) {
  9801. const { initial, animate } = props;
  9802. return {
  9803. initial: initial === false || isVariantLabel(initial)
  9804. ? initial
  9805. : undefined,
  9806. animate: isVariantLabel(animate) ? animate : undefined,
  9807. };
  9808. }
  9809. return props.inherit !== false ? context : {};
  9810. }
  9811. function useCreateMotionContext(props) {
  9812. const { initial, animate } = getCurrentTreeVariants(props, React$1.useContext(MotionContext));
  9813. return React$1.useMemo(() => ({ initial, animate }), [variantLabelsAsDependency(initial), variantLabelsAsDependency(animate)]);
  9814. }
  9815. function variantLabelsAsDependency(prop) {
  9816. return Array.isArray(prop) ? prop.join(" ") : prop;
  9817. }
  9818. const motionComponentSymbol = Symbol.for("motionComponentSymbol");
  9819. /**
  9820. * Creates a ref function that, when called, hydrates the provided
  9821. * external ref and VisualElement.
  9822. */
  9823. function useMotionRef(visualState, visualElement, externalRef) {
  9824. return React$1.useCallback((instance) => {
  9825. if (instance) {
  9826. visualState.onMount && visualState.onMount(instance);
  9827. }
  9828. if (visualElement) {
  9829. if (instance) {
  9830. visualElement.mount(instance);
  9831. }
  9832. else {
  9833. visualElement.unmount();
  9834. }
  9835. }
  9836. if (externalRef) {
  9837. if (typeof externalRef === "function") {
  9838. externalRef(instance);
  9839. }
  9840. else if (isRefObject(externalRef)) {
  9841. externalRef.current = instance;
  9842. }
  9843. }
  9844. },
  9845. /**
  9846. * Only pass a new ref callback to React if we've received a visual element
  9847. * factory. Otherwise we'll be mounting/remounting every time externalRef
  9848. * or other dependencies change.
  9849. */
  9850. [visualElement]);
  9851. }
  9852. function useVisualElement(Component, visualState, props, createVisualElement, ProjectionNodeConstructor) {
  9853. const { visualElement: parent } = React$1.useContext(MotionContext);
  9854. const lazyContext = React$1.useContext(LazyContext);
  9855. const presenceContext = React$1.useContext(PresenceContext);
  9856. const reducedMotionConfig = React$1.useContext(MotionConfigContext).reducedMotion;
  9857. const visualElementRef = React$1.useRef(null);
  9858. /**
  9859. * If we haven't preloaded a renderer, check to see if we have one lazy-loaded
  9860. */
  9861. createVisualElement = createVisualElement || lazyContext.renderer;
  9862. if (!visualElementRef.current && createVisualElement) {
  9863. visualElementRef.current = createVisualElement(Component, {
  9864. visualState,
  9865. parent,
  9866. props,
  9867. presenceContext,
  9868. blockInitialAnimation: presenceContext
  9869. ? presenceContext.initial === false
  9870. : false,
  9871. reducedMotionConfig,
  9872. });
  9873. }
  9874. const visualElement = visualElementRef.current;
  9875. /**
  9876. * Load Motion gesture and animation features. These are rendered as renderless
  9877. * components so each feature can optionally make use of React lifecycle methods.
  9878. */
  9879. const initialLayoutGroupConfig = React$1.useContext(SwitchLayoutGroupContext);
  9880. if (visualElement &&
  9881. !visualElement.projection &&
  9882. ProjectionNodeConstructor &&
  9883. (visualElement.type === "html" || visualElement.type === "svg")) {
  9884. createProjectionNode(visualElementRef.current, props, ProjectionNodeConstructor, initialLayoutGroupConfig);
  9885. }
  9886. const isMounted = React$1.useRef(false);
  9887. React$1.useInsertionEffect(() => {
  9888. /**
  9889. * Check the component has already mounted before calling
  9890. * `update` unnecessarily. This ensures we skip the initial update.
  9891. */
  9892. if (visualElement && isMounted.current) {
  9893. visualElement.update(props, presenceContext);
  9894. }
  9895. });
  9896. /**
  9897. * Cache this value as we want to know whether HandoffAppearAnimations
  9898. * was present on initial render - it will be deleted after this.
  9899. */
  9900. const optimisedAppearId = props[optimizedAppearDataAttribute];
  9901. const wantsHandoff = React$1.useRef(Boolean(optimisedAppearId) &&
  9902. !window.MotionHandoffIsComplete?.(optimisedAppearId) &&
  9903. window.MotionHasOptimisedAnimation?.(optimisedAppearId));
  9904. useIsomorphicLayoutEffect(() => {
  9905. if (!visualElement)
  9906. return;
  9907. isMounted.current = true;
  9908. window.MotionIsMounted = true;
  9909. visualElement.updateFeatures();
  9910. microtask.render(visualElement.render);
  9911. /**
  9912. * Ideally this function would always run in a useEffect.
  9913. *
  9914. * However, if we have optimised appear animations to handoff from,
  9915. * it needs to happen synchronously to ensure there's no flash of
  9916. * incorrect styles in the event of a hydration error.
  9917. *
  9918. * So if we detect a situtation where optimised appear animations
  9919. * are running, we use useLayoutEffect to trigger animations.
  9920. */
  9921. if (wantsHandoff.current && visualElement.animationState) {
  9922. visualElement.animationState.animateChanges();
  9923. }
  9924. });
  9925. React$1.useEffect(() => {
  9926. if (!visualElement)
  9927. return;
  9928. if (!wantsHandoff.current && visualElement.animationState) {
  9929. visualElement.animationState.animateChanges();
  9930. }
  9931. if (wantsHandoff.current) {
  9932. // This ensures all future calls to animateChanges() in this component will run in useEffect
  9933. queueMicrotask(() => {
  9934. window.MotionHandoffMarkAsComplete?.(optimisedAppearId);
  9935. });
  9936. wantsHandoff.current = false;
  9937. }
  9938. });
  9939. return visualElement;
  9940. }
  9941. function createProjectionNode(visualElement, props, ProjectionNodeConstructor, initialPromotionConfig) {
  9942. const { layoutId, layout, drag, dragConstraints, layoutScroll, layoutRoot, layoutCrossfade, } = props;
  9943. visualElement.projection = new ProjectionNodeConstructor(visualElement.latestValues, props["data-framer-portal-id"]
  9944. ? undefined
  9945. : getClosestProjectingNode(visualElement.parent));
  9946. visualElement.projection.setOptions({
  9947. layoutId,
  9948. layout,
  9949. alwaysMeasureLayout: Boolean(drag) || (dragConstraints && isRefObject(dragConstraints)),
  9950. visualElement,
  9951. /**
  9952. * TODO: Update options in an effect. This could be tricky as it'll be too late
  9953. * to update by the time layout animations run.
  9954. * We also need to fix this safeToRemove by linking it up to the one returned by usePresence,
  9955. * ensuring it gets called if there's no potential layout animations.
  9956. *
  9957. */
  9958. animationType: typeof layout === "string" ? layout : "both",
  9959. initialPromotionConfig,
  9960. crossfade: layoutCrossfade,
  9961. layoutScroll,
  9962. layoutRoot,
  9963. });
  9964. }
  9965. function getClosestProjectingNode(visualElement) {
  9966. if (!visualElement)
  9967. return undefined;
  9968. return visualElement.options.allowProjection !== false
  9969. ? visualElement.projection
  9970. : getClosestProjectingNode(visualElement.parent);
  9971. }
  9972. /**
  9973. * Create a `motion` component.
  9974. *
  9975. * This function accepts a Component argument, which can be either a string (ie "div"
  9976. * for `motion.div`), or an actual React component.
  9977. *
  9978. * Alongside this is a config option which provides a way of rendering the provided
  9979. * component "offline", or outside the React render cycle.
  9980. */
  9981. function createRendererMotionComponent({ preloadedFeatures, createVisualElement, useRender, useVisualState, Component, }) {
  9982. preloadedFeatures && loadFeatures(preloadedFeatures);
  9983. function MotionComponent(props, externalRef) {
  9984. /**
  9985. * If we need to measure the element we load this functionality in a
  9986. * separate class component in order to gain access to getSnapshotBeforeUpdate.
  9987. */
  9988. let MeasureLayout;
  9989. const configAndProps = {
  9990. ...React$1.useContext(MotionConfigContext),
  9991. ...props,
  9992. layoutId: useLayoutId(props),
  9993. };
  9994. const { isStatic } = configAndProps;
  9995. const context = useCreateMotionContext(props);
  9996. const visualState = useVisualState(props, isStatic);
  9997. if (!isStatic && isBrowser) {
  9998. useStrictMode(configAndProps, preloadedFeatures);
  9999. const layoutProjection = getProjectionFunctionality(configAndProps);
  10000. MeasureLayout = layoutProjection.MeasureLayout;
  10001. /**
  10002. * Create a VisualElement for this component. A VisualElement provides a common
  10003. * interface to renderer-specific APIs (ie DOM/Three.js etc) as well as
  10004. * providing a way of rendering to these APIs outside of the React render loop
  10005. * for more performant animations and interactions
  10006. */
  10007. context.visualElement = useVisualElement(Component, visualState, configAndProps, createVisualElement, layoutProjection.ProjectionNode);
  10008. }
  10009. /**
  10010. * The mount order and hierarchy is specific to ensure our element ref
  10011. * is hydrated by the time features fire their effects.
  10012. */
  10013. return (jsxs(MotionContext.Provider, { value: context, children: [MeasureLayout && context.visualElement ? (jsx(MeasureLayout, { visualElement: context.visualElement, ...configAndProps })) : null, useRender(Component, props, useMotionRef(visualState, context.visualElement, externalRef), visualState, isStatic, context.visualElement)] }));
  10014. }
  10015. MotionComponent.displayName = `motion.${typeof Component === "string"
  10016. ? Component
  10017. : `create(${Component.displayName ?? Component.name ?? ""})`}`;
  10018. const ForwardRefMotionComponent = React$1.forwardRef(MotionComponent);
  10019. ForwardRefMotionComponent[motionComponentSymbol] = Component;
  10020. return ForwardRefMotionComponent;
  10021. }
  10022. function useLayoutId({ layoutId }) {
  10023. const layoutGroupId = React$1.useContext(LayoutGroupContext).id;
  10024. return layoutGroupId && layoutId !== undefined
  10025. ? layoutGroupId + "-" + layoutId
  10026. : layoutId;
  10027. }
  10028. function useStrictMode(configAndProps, preloadedFeatures) {
  10029. const isStrict = React$1.useContext(LazyContext).strict;
  10030. /**
  10031. * If we're in development mode, check to make sure we're not rendering a motion component
  10032. * as a child of LazyMotion, as this will break the file-size benefits of using it.
  10033. */
  10034. if (preloadedFeatures &&
  10035. isStrict) {
  10036. const strictMessage = "You have rendered a `motion` component within a `LazyMotion` component. This will break tree shaking. Import and render a `m` component instead.";
  10037. configAndProps.ignoreStrict
  10038. ? warning(false, strictMessage)
  10039. : exports.invariant(false, strictMessage);
  10040. }
  10041. }
  10042. function getProjectionFunctionality(props) {
  10043. const { drag, layout } = featureDefinitions;
  10044. if (!drag && !layout)
  10045. return {};
  10046. const combined = { ...drag, ...layout };
  10047. return {
  10048. MeasureLayout: drag?.isEnabled(props) || layout?.isEnabled(props)
  10049. ? combined.MeasureLayout
  10050. : undefined,
  10051. ProjectionNode: combined.ProjectionNode,
  10052. };
  10053. }
  10054. const createHtmlRenderState = () => ({
  10055. style: {},
  10056. transform: {},
  10057. transformOrigin: {},
  10058. vars: {},
  10059. });
  10060. function copyRawValuesOnly(target, source, props) {
  10061. for (const key in source) {
  10062. if (!isMotionValue(source[key]) && !isForcedMotionValue(key, props)) {
  10063. target[key] = source[key];
  10064. }
  10065. }
  10066. }
  10067. function useInitialMotionValues({ transformTemplate }, visualState) {
  10068. return React$1.useMemo(() => {
  10069. const state = createHtmlRenderState();
  10070. buildHTMLStyles(state, visualState, transformTemplate);
  10071. return Object.assign({}, state.vars, state.style);
  10072. }, [visualState]);
  10073. }
  10074. function useStyle(props, visualState) {
  10075. const styleProp = props.style || {};
  10076. const style = {};
  10077. /**
  10078. * Copy non-Motion Values straight into style
  10079. */
  10080. copyRawValuesOnly(style, styleProp, props);
  10081. Object.assign(style, useInitialMotionValues(props, visualState));
  10082. return style;
  10083. }
  10084. function useHTMLProps(props, visualState) {
  10085. // The `any` isn't ideal but it is the type of createElement props argument
  10086. const htmlProps = {};
  10087. const style = useStyle(props, visualState);
  10088. if (props.drag && props.dragListener !== false) {
  10089. // Disable the ghost element when a user drags
  10090. htmlProps.draggable = false;
  10091. // Disable text selection
  10092. style.userSelect =
  10093. style.WebkitUserSelect =
  10094. style.WebkitTouchCallout =
  10095. "none";
  10096. // Disable scrolling on the draggable direction
  10097. style.touchAction =
  10098. props.drag === true
  10099. ? "none"
  10100. : `pan-${props.drag === "x" ? "y" : "x"}`;
  10101. }
  10102. if (props.tabIndex === undefined &&
  10103. (props.onTap || props.onTapStart || props.whileTap)) {
  10104. htmlProps.tabIndex = 0;
  10105. }
  10106. htmlProps.style = style;
  10107. return htmlProps;
  10108. }
  10109. /**
  10110. * We keep these listed separately as we use the lowercase tag names as part
  10111. * of the runtime bundle to detect SVG components
  10112. */
  10113. const lowercaseSVGElements = [
  10114. "animate",
  10115. "circle",
  10116. "defs",
  10117. "desc",
  10118. "ellipse",
  10119. "g",
  10120. "image",
  10121. "line",
  10122. "filter",
  10123. "marker",
  10124. "mask",
  10125. "metadata",
  10126. "path",
  10127. "pattern",
  10128. "polygon",
  10129. "polyline",
  10130. "rect",
  10131. "stop",
  10132. "switch",
  10133. "symbol",
  10134. "svg",
  10135. "text",
  10136. "tspan",
  10137. "use",
  10138. "view",
  10139. ];
  10140. function isSVGComponent(Component) {
  10141. if (
  10142. /**
  10143. * If it's not a string, it's a custom React component. Currently we only support
  10144. * HTML custom React components.
  10145. */
  10146. typeof Component !== "string" ||
  10147. /**
  10148. * If it contains a dash, the element is a custom HTML webcomponent.
  10149. */
  10150. Component.includes("-")) {
  10151. return false;
  10152. }
  10153. else if (
  10154. /**
  10155. * If it's in our list of lowercase SVG tags, it's an SVG component
  10156. */
  10157. lowercaseSVGElements.indexOf(Component) > -1 ||
  10158. /**
  10159. * If it contains a capital letter, it's an SVG component
  10160. */
  10161. /[A-Z]/u.test(Component)) {
  10162. return true;
  10163. }
  10164. return false;
  10165. }
  10166. const dashKeys = {
  10167. offset: "stroke-dashoffset",
  10168. array: "stroke-dasharray",
  10169. };
  10170. const camelKeys = {
  10171. offset: "strokeDashoffset",
  10172. array: "strokeDasharray",
  10173. };
  10174. /**
  10175. * Build SVG path properties. Uses the path's measured length to convert
  10176. * our custom pathLength, pathSpacing and pathOffset into stroke-dashoffset
  10177. * and stroke-dasharray attributes.
  10178. *
  10179. * This function is mutative to reduce per-frame GC.
  10180. */
  10181. function buildSVGPath(attrs, length, spacing = 1, offset = 0, useDashCase = true) {
  10182. // Normalise path length by setting SVG attribute pathLength to 1
  10183. attrs.pathLength = 1;
  10184. // We use dash case when setting attributes directly to the DOM node and camel case
  10185. // when defining props on a React component.
  10186. const keys = useDashCase ? dashKeys : camelKeys;
  10187. // Build the dash offset
  10188. attrs[keys.offset] = px.transform(-offset);
  10189. // Build the dash array
  10190. const pathLength = px.transform(length);
  10191. const pathSpacing = px.transform(spacing);
  10192. attrs[keys.array] = `${pathLength} ${pathSpacing}`;
  10193. }
  10194. function calcOrigin(origin, offset, size) {
  10195. return typeof origin === "string"
  10196. ? origin
  10197. : px.transform(offset + size * origin);
  10198. }
  10199. /**
  10200. * The SVG transform origin defaults are different to CSS and is less intuitive,
  10201. * so we use the measured dimensions of the SVG to reconcile these.
  10202. */
  10203. function calcSVGTransformOrigin(dimensions, originX, originY) {
  10204. const pxOriginX = calcOrigin(originX, dimensions.x, dimensions.width);
  10205. const pxOriginY = calcOrigin(originY, dimensions.y, dimensions.height);
  10206. return `${pxOriginX} ${pxOriginY}`;
  10207. }
  10208. /**
  10209. * Build SVG visual attrbutes, like cx and style.transform
  10210. */
  10211. function buildSVGAttrs(state, { attrX, attrY, attrScale, originX, originY, pathLength, pathSpacing = 1, pathOffset = 0,
  10212. // This is object creation, which we try to avoid per-frame.
  10213. ...latest }, isSVGTag, transformTemplate) {
  10214. buildHTMLStyles(state, latest, transformTemplate);
  10215. /**
  10216. * For svg tags we just want to make sure viewBox is animatable and treat all the styles
  10217. * as normal HTML tags.
  10218. */
  10219. if (isSVGTag) {
  10220. if (state.style.viewBox) {
  10221. state.attrs.viewBox = state.style.viewBox;
  10222. }
  10223. return;
  10224. }
  10225. state.attrs = state.style;
  10226. state.style = {};
  10227. const { attrs, style, dimensions } = state;
  10228. /**
  10229. * However, we apply transforms as CSS transforms. So if we detect a transform we take it from attrs
  10230. * and copy it into style.
  10231. */
  10232. if (attrs.transform) {
  10233. if (dimensions)
  10234. style.transform = attrs.transform;
  10235. delete attrs.transform;
  10236. }
  10237. // Parse transformOrigin
  10238. if (dimensions &&
  10239. (originX !== undefined || originY !== undefined || style.transform)) {
  10240. style.transformOrigin = calcSVGTransformOrigin(dimensions, originX !== undefined ? originX : 0.5, originY !== undefined ? originY : 0.5);
  10241. }
  10242. // Render attrX/attrY/attrScale as attributes
  10243. if (attrX !== undefined)
  10244. attrs.x = attrX;
  10245. if (attrY !== undefined)
  10246. attrs.y = attrY;
  10247. if (attrScale !== undefined)
  10248. attrs.scale = attrScale;
  10249. // Build SVG path if one has been defined
  10250. if (pathLength !== undefined) {
  10251. buildSVGPath(attrs, pathLength, pathSpacing, pathOffset, false);
  10252. }
  10253. }
  10254. const createSvgRenderState = () => ({
  10255. ...createHtmlRenderState(),
  10256. attrs: {},
  10257. });
  10258. const isSVGTag = (tag) => typeof tag === "string" && tag.toLowerCase() === "svg";
  10259. function useSVGProps(props, visualState, _isStatic, Component) {
  10260. const visualProps = React$1.useMemo(() => {
  10261. const state = createSvgRenderState();
  10262. buildSVGAttrs(state, visualState, isSVGTag(Component), props.transformTemplate);
  10263. return {
  10264. ...state.attrs,
  10265. style: { ...state.style },
  10266. };
  10267. }, [visualState]);
  10268. if (props.style) {
  10269. const rawStyles = {};
  10270. copyRawValuesOnly(rawStyles, props.style, props);
  10271. visualProps.style = { ...rawStyles, ...visualProps.style };
  10272. }
  10273. return visualProps;
  10274. }
  10275. function createUseRender(forwardMotionProps = false) {
  10276. const useRender = (Component, props, ref, { latestValues }, isStatic) => {
  10277. const useVisualProps = isSVGComponent(Component)
  10278. ? useSVGProps
  10279. : useHTMLProps;
  10280. const visualProps = useVisualProps(props, latestValues, isStatic, Component);
  10281. const filteredProps = filterProps(props, typeof Component === "string", forwardMotionProps);
  10282. const elementProps = Component !== React$1.Fragment
  10283. ? { ...filteredProps, ...visualProps, ref }
  10284. : {};
  10285. /**
  10286. * If component has been handed a motion value as its child,
  10287. * memoise its initial value and render that. Subsequent updates
  10288. * will be handled by the onChange handler
  10289. */
  10290. const { children } = props;
  10291. const renderedChildren = React$1.useMemo(() => (isMotionValue(children) ? children.get() : children), [children]);
  10292. return React$1.createElement(Component, {
  10293. ...elementProps,
  10294. children: renderedChildren,
  10295. });
  10296. };
  10297. return useRender;
  10298. }
  10299. function makeState({ scrapeMotionValuesFromProps, createRenderState, onUpdate, }, props, context, presenceContext) {
  10300. const state = {
  10301. latestValues: makeLatestValues(props, context, presenceContext, scrapeMotionValuesFromProps),
  10302. renderState: createRenderState(),
  10303. };
  10304. if (onUpdate) {
  10305. /**
  10306. * onMount works without the VisualElement because it could be
  10307. * called before the VisualElement payload has been hydrated.
  10308. * (e.g. if someone is using m components <m.circle />)
  10309. */
  10310. state.onMount = (instance) => onUpdate({ props, current: instance, ...state });
  10311. state.onUpdate = (visualElement) => onUpdate(visualElement);
  10312. }
  10313. return state;
  10314. }
  10315. const makeUseVisualState = (config) => (props, isStatic) => {
  10316. const context = React$1.useContext(MotionContext);
  10317. const presenceContext = React$1.useContext(PresenceContext);
  10318. const make = () => makeState(config, props, context, presenceContext);
  10319. return isStatic ? make() : useConstant(make);
  10320. };
  10321. function makeLatestValues(props, context, presenceContext, scrapeMotionValues) {
  10322. const values = {};
  10323. const motionValues = scrapeMotionValues(props, {});
  10324. for (const key in motionValues) {
  10325. values[key] = resolveMotionValue(motionValues[key]);
  10326. }
  10327. let { initial, animate } = props;
  10328. const isControllingVariants$1 = isControllingVariants(props);
  10329. const isVariantNode$1 = isVariantNode(props);
  10330. if (context &&
  10331. isVariantNode$1 &&
  10332. !isControllingVariants$1 &&
  10333. props.inherit !== false) {
  10334. if (initial === undefined)
  10335. initial = context.initial;
  10336. if (animate === undefined)
  10337. animate = context.animate;
  10338. }
  10339. let isInitialAnimationBlocked = presenceContext
  10340. ? presenceContext.initial === false
  10341. : false;
  10342. isInitialAnimationBlocked = isInitialAnimationBlocked || initial === false;
  10343. const variantToSet = isInitialAnimationBlocked ? animate : initial;
  10344. if (variantToSet &&
  10345. typeof variantToSet !== "boolean" &&
  10346. !isAnimationControls(variantToSet)) {
  10347. const list = Array.isArray(variantToSet) ? variantToSet : [variantToSet];
  10348. for (let i = 0; i < list.length; i++) {
  10349. const resolved = resolveVariantFromProps(props, list[i]);
  10350. if (resolved) {
  10351. const { transitionEnd, transition, ...target } = resolved;
  10352. for (const key in target) {
  10353. let valueTarget = target[key];
  10354. if (Array.isArray(valueTarget)) {
  10355. /**
  10356. * Take final keyframe if the initial animation is blocked because
  10357. * we want to initialise at the end of that blocked animation.
  10358. */
  10359. const index = isInitialAnimationBlocked
  10360. ? valueTarget.length - 1
  10361. : 0;
  10362. valueTarget = valueTarget[index];
  10363. }
  10364. if (valueTarget !== null) {
  10365. values[key] = valueTarget;
  10366. }
  10367. }
  10368. for (const key in transitionEnd) {
  10369. values[key] = transitionEnd[key];
  10370. }
  10371. }
  10372. }
  10373. }
  10374. return values;
  10375. }
  10376. const htmlMotionConfig = {
  10377. useVisualState: makeUseVisualState({
  10378. scrapeMotionValuesFromProps: scrapeMotionValuesFromProps$1,
  10379. createRenderState: createHtmlRenderState,
  10380. }),
  10381. };
  10382. function updateSVGDimensions(instance, renderState) {
  10383. try {
  10384. renderState.dimensions =
  10385. typeof instance.getBBox === "function"
  10386. ? instance.getBBox()
  10387. : instance.getBoundingClientRect();
  10388. }
  10389. catch (e) {
  10390. // Most likely trying to measure an unrendered element under Firefox
  10391. renderState.dimensions = {
  10392. x: 0,
  10393. y: 0,
  10394. width: 0,
  10395. height: 0,
  10396. };
  10397. }
  10398. }
  10399. /**
  10400. * A set of attribute names that are always read/written as camel case.
  10401. */
  10402. const camelCaseAttributes = new Set([
  10403. "baseFrequency",
  10404. "diffuseConstant",
  10405. "kernelMatrix",
  10406. "kernelUnitLength",
  10407. "keySplines",
  10408. "keyTimes",
  10409. "limitingConeAngle",
  10410. "markerHeight",
  10411. "markerWidth",
  10412. "numOctaves",
  10413. "targetX",
  10414. "targetY",
  10415. "surfaceScale",
  10416. "specularConstant",
  10417. "specularExponent",
  10418. "stdDeviation",
  10419. "tableValues",
  10420. "viewBox",
  10421. "gradientTransform",
  10422. "pathLength",
  10423. "startOffset",
  10424. "textLength",
  10425. "lengthAdjust",
  10426. ]);
  10427. function renderSVG(element, renderState, _styleProp, projection) {
  10428. renderHTML(element, renderState, undefined, projection);
  10429. for (const key in renderState.attrs) {
  10430. element.setAttribute(!camelCaseAttributes.has(key) ? camelToDash(key) : key, renderState.attrs[key]);
  10431. }
  10432. }
  10433. function scrapeMotionValuesFromProps(props, prevProps, visualElement) {
  10434. const newValues = scrapeMotionValuesFromProps$1(props, prevProps, visualElement);
  10435. for (const key in props) {
  10436. if (isMotionValue(props[key]) ||
  10437. isMotionValue(prevProps[key])) {
  10438. const targetKey = transformPropOrder.indexOf(key) !== -1
  10439. ? "attr" + key.charAt(0).toUpperCase() + key.substring(1)
  10440. : key;
  10441. newValues[targetKey] = props[key];
  10442. }
  10443. }
  10444. return newValues;
  10445. }
  10446. const layoutProps = ["x", "y", "width", "height", "cx", "cy", "r"];
  10447. const svgMotionConfig = {
  10448. useVisualState: makeUseVisualState({
  10449. scrapeMotionValuesFromProps: scrapeMotionValuesFromProps,
  10450. createRenderState: createSvgRenderState,
  10451. onUpdate: ({ props, prevProps, current, renderState, latestValues, }) => {
  10452. if (!current)
  10453. return;
  10454. let hasTransform = !!props.drag;
  10455. if (!hasTransform) {
  10456. for (const key in latestValues) {
  10457. if (transformProps.has(key)) {
  10458. hasTransform = true;
  10459. break;
  10460. }
  10461. }
  10462. }
  10463. if (!hasTransform)
  10464. return;
  10465. let needsMeasure = !prevProps;
  10466. if (prevProps) {
  10467. /**
  10468. * Check the layout props for changes, if any are found we need to
  10469. * measure the element again.
  10470. */
  10471. for (let i = 0; i < layoutProps.length; i++) {
  10472. const key = layoutProps[i];
  10473. if (props[key] !==
  10474. prevProps[key]) {
  10475. needsMeasure = true;
  10476. }
  10477. }
  10478. }
  10479. if (!needsMeasure)
  10480. return;
  10481. frame.read(() => {
  10482. updateSVGDimensions(current, renderState);
  10483. frame.render(() => {
  10484. buildSVGAttrs(renderState, latestValues, isSVGTag(current.tagName), props.transformTemplate);
  10485. renderSVG(current, renderState);
  10486. });
  10487. });
  10488. },
  10489. }),
  10490. };
  10491. function createMotionComponentFactory(preloadedFeatures, createVisualElement) {
  10492. return function createMotionComponent(Component, { forwardMotionProps } = { forwardMotionProps: false }) {
  10493. const baseConfig = isSVGComponent(Component)
  10494. ? svgMotionConfig
  10495. : htmlMotionConfig;
  10496. const config = {
  10497. ...baseConfig,
  10498. preloadedFeatures,
  10499. useRender: createUseRender(forwardMotionProps),
  10500. createVisualElement,
  10501. Component,
  10502. };
  10503. return createRendererMotionComponent(config);
  10504. };
  10505. }
  10506. class SVGVisualElement extends DOMVisualElement {
  10507. constructor() {
  10508. super(...arguments);
  10509. this.type = "svg";
  10510. this.isSVGTag = false;
  10511. this.measureInstanceViewportBox = createBox;
  10512. this.updateDimensions = () => {
  10513. if (this.current && !this.renderState.dimensions) {
  10514. updateSVGDimensions(this.current, this.renderState);
  10515. }
  10516. };
  10517. }
  10518. getBaseTargetFromProps(props, key) {
  10519. return props[key];
  10520. }
  10521. readValueFromInstance(instance, key) {
  10522. if (transformProps.has(key)) {
  10523. const defaultType = getDefaultValueType(key);
  10524. return defaultType ? defaultType.default || 0 : 0;
  10525. }
  10526. key = !camelCaseAttributes.has(key) ? camelToDash(key) : key;
  10527. return instance.getAttribute(key);
  10528. }
  10529. scrapeMotionValuesFromProps(props, prevProps, visualElement) {
  10530. return scrapeMotionValuesFromProps(props, prevProps, visualElement);
  10531. }
  10532. onBindTransform() {
  10533. if (this.current && !this.renderState.dimensions) {
  10534. frame.postRender(this.updateDimensions);
  10535. }
  10536. }
  10537. build(renderState, latestValues, props) {
  10538. buildSVGAttrs(renderState, latestValues, this.isSVGTag, props.transformTemplate);
  10539. }
  10540. renderInstance(instance, renderState, styleProp, projection) {
  10541. renderSVG(instance, renderState, styleProp, projection);
  10542. }
  10543. mount(instance) {
  10544. this.isSVGTag = isSVGTag(instance.tagName);
  10545. super.mount(instance);
  10546. }
  10547. }
  10548. const createDomVisualElement = (Component, options) => {
  10549. return isSVGComponent(Component)
  10550. ? new SVGVisualElement(options)
  10551. : new HTMLVisualElement(options, {
  10552. allowProjection: Component !== React$1.Fragment,
  10553. });
  10554. };
  10555. const createMotionComponent = /*@__PURE__*/ createMotionComponentFactory({
  10556. ...animations,
  10557. ...gestureAnimations,
  10558. ...drag,
  10559. ...layout,
  10560. }, createDomVisualElement);
  10561. const motion = /*@__PURE__*/ createDOMMotionComponentProxy(createMotionComponent);
  10562. function checkReorder(order, value, offset, velocity) {
  10563. if (!velocity)
  10564. return order;
  10565. const index = order.findIndex((item) => item.value === value);
  10566. if (index === -1)
  10567. return order;
  10568. const nextOffset = velocity > 0 ? 1 : -1;
  10569. const nextItem = order[index + nextOffset];
  10570. if (!nextItem)
  10571. return order;
  10572. const item = order[index];
  10573. const nextLayout = nextItem.layout;
  10574. const nextItemCenter = mixNumber$1(nextLayout.min, nextLayout.max, 0.5);
  10575. if ((nextOffset === 1 && item.layout.max + offset > nextItemCenter) ||
  10576. (nextOffset === -1 && item.layout.min + offset < nextItemCenter)) {
  10577. return moveItem(order, index, index + nextOffset);
  10578. }
  10579. return order;
  10580. }
  10581. function ReorderGroupComponent({ children, as = "ul", axis = "y", onReorder, values, ...props }, externalRef) {
  10582. const Component = useConstant(() => motion[as]);
  10583. const order = [];
  10584. const isReordering = React$1.useRef(false);
  10585. exports.invariant(Boolean(values), "Reorder.Group must be provided a values prop");
  10586. const context = {
  10587. axis,
  10588. registerItem: (value, layout) => {
  10589. // If the entry was already added, update it rather than adding it again
  10590. const idx = order.findIndex((entry) => value === entry.value);
  10591. if (idx !== -1) {
  10592. order[idx].layout = layout[axis];
  10593. }
  10594. else {
  10595. order.push({ value: value, layout: layout[axis] });
  10596. }
  10597. order.sort(compareMin);
  10598. },
  10599. updateOrder: (item, offset, velocity) => {
  10600. if (isReordering.current)
  10601. return;
  10602. const newOrder = checkReorder(order, item, offset, velocity);
  10603. if (order !== newOrder) {
  10604. isReordering.current = true;
  10605. onReorder(newOrder
  10606. .map(getValue)
  10607. .filter((value) => values.indexOf(value) !== -1));
  10608. }
  10609. },
  10610. };
  10611. React$1.useEffect(() => {
  10612. isReordering.current = false;
  10613. });
  10614. return (jsx(Component, { ...props, ref: externalRef, ignoreStrict: true, children: jsx(ReorderContext.Provider, { value: context, children: children }) }));
  10615. }
  10616. const ReorderGroup = /*@__PURE__*/ React$1.forwardRef(ReorderGroupComponent);
  10617. function getValue(item) {
  10618. return item.value;
  10619. }
  10620. function compareMin(a, b) {
  10621. return a.layout.min - b.layout.min;
  10622. }
  10623. /**
  10624. * Creates a `MotionValue` to track the state and velocity of a value.
  10625. *
  10626. * Usually, these are created automatically. For advanced use-cases, like use with `useTransform`, you can create `MotionValue`s externally and pass them into the animated component via the `style` prop.
  10627. *
  10628. * ```jsx
  10629. * export const MyComponent = () => {
  10630. * const scale = useMotionValue(1)
  10631. *
  10632. * return <motion.div style={{ scale }} />
  10633. * }
  10634. * ```
  10635. *
  10636. * @param initial - The initial state.
  10637. *
  10638. * @public
  10639. */
  10640. function useMotionValue(initial) {
  10641. const value = useConstant(() => motionValue(initial));
  10642. /**
  10643. * If this motion value is being used in static mode, like on
  10644. * the Framer canvas, force components to rerender when the motion
  10645. * value is updated.
  10646. */
  10647. const { isStatic } = React$1.useContext(MotionConfigContext);
  10648. if (isStatic) {
  10649. const [, setLatest] = React$1.useState(initial);
  10650. React$1.useEffect(() => value.on("change", setLatest), []);
  10651. }
  10652. return value;
  10653. }
  10654. const isCustomValueType = (v) => {
  10655. return v && typeof v === "object" && v.mix;
  10656. };
  10657. const getMixer = (v) => (isCustomValueType(v) ? v.mix : undefined);
  10658. function transform(...args) {
  10659. const useImmediate = !Array.isArray(args[0]);
  10660. const argOffset = useImmediate ? 0 : -1;
  10661. const inputValue = args[0 + argOffset];
  10662. const inputRange = args[1 + argOffset];
  10663. const outputRange = args[2 + argOffset];
  10664. const options = args[3 + argOffset];
  10665. const interpolator = interpolate(inputRange, outputRange, {
  10666. mixer: getMixer(outputRange[0]),
  10667. ...options,
  10668. });
  10669. return useImmediate ? interpolator(inputValue) : interpolator;
  10670. }
  10671. function useCombineMotionValues(values, combineValues) {
  10672. /**
  10673. * Initialise the returned motion value. This remains the same between renders.
  10674. */
  10675. const value = useMotionValue(combineValues());
  10676. /**
  10677. * Create a function that will update the template motion value with the latest values.
  10678. * This is pre-bound so whenever a motion value updates it can schedule its
  10679. * execution in Framesync. If it's already been scheduled it won't be fired twice
  10680. * in a single frame.
  10681. */
  10682. const updateValue = () => value.set(combineValues());
  10683. /**
  10684. * Synchronously update the motion value with the latest values during the render.
  10685. * This ensures that within a React render, the styles applied to the DOM are up-to-date.
  10686. */
  10687. updateValue();
  10688. /**
  10689. * Subscribe to all motion values found within the template. Whenever any of them change,
  10690. * schedule an update.
  10691. */
  10692. useIsomorphicLayoutEffect(() => {
  10693. const scheduleUpdate = () => frame.preRender(updateValue, false, true);
  10694. const subscriptions = values.map((v) => v.on("change", scheduleUpdate));
  10695. return () => {
  10696. subscriptions.forEach((unsubscribe) => unsubscribe());
  10697. cancelFrame(updateValue);
  10698. };
  10699. });
  10700. return value;
  10701. }
  10702. function useComputed(compute) {
  10703. /**
  10704. * Open session of collectMotionValues. Any MotionValue that calls get()
  10705. * will be saved into this array.
  10706. */
  10707. collectMotionValues.current = [];
  10708. compute();
  10709. const value = useCombineMotionValues(collectMotionValues.current, compute);
  10710. /**
  10711. * Synchronously close session of collectMotionValues.
  10712. */
  10713. collectMotionValues.current = undefined;
  10714. return value;
  10715. }
  10716. function useTransform(input, inputRangeOrTransformer, outputRange, options) {
  10717. if (typeof input === "function") {
  10718. return useComputed(input);
  10719. }
  10720. const transformer = typeof inputRangeOrTransformer === "function"
  10721. ? inputRangeOrTransformer
  10722. : transform(inputRangeOrTransformer, outputRange, options);
  10723. return Array.isArray(input)
  10724. ? useListTransform(input, transformer)
  10725. : useListTransform([input], ([latest]) => transformer(latest));
  10726. }
  10727. function useListTransform(values, transformer) {
  10728. const latest = useConstant(() => []);
  10729. return useCombineMotionValues(values, () => {
  10730. latest.length = 0;
  10731. const numValues = values.length;
  10732. for (let i = 0; i < numValues; i++) {
  10733. latest[i] = values[i].get();
  10734. }
  10735. return transformer(latest);
  10736. });
  10737. }
  10738. function useDefaultMotionValue(value, defaultValue = 0) {
  10739. return isMotionValue(value) ? value : useMotionValue(defaultValue);
  10740. }
  10741. function ReorderItemComponent({ children, style = {}, value, as = "li", onDrag, layout = true, ...props }, externalRef) {
  10742. const Component = useConstant(() => motion[as]);
  10743. const context = React$1.useContext(ReorderContext);
  10744. const point = {
  10745. x: useDefaultMotionValue(style.x),
  10746. y: useDefaultMotionValue(style.y),
  10747. };
  10748. const zIndex = useTransform([point.x, point.y], ([latestX, latestY]) => latestX || latestY ? 1 : "unset");
  10749. exports.invariant(Boolean(context), "Reorder.Item must be a child of Reorder.Group");
  10750. const { axis, registerItem, updateOrder } = context;
  10751. return (jsx(Component, { drag: axis, ...props, dragSnapToOrigin: true, style: { ...style, x: point.x, y: point.y, zIndex }, layout: layout, onDrag: (event, gesturePoint) => {
  10752. const { velocity } = gesturePoint;
  10753. velocity[axis] &&
  10754. updateOrder(value, point[axis].get(), velocity[axis]);
  10755. onDrag && onDrag(event, gesturePoint);
  10756. }, onLayoutMeasure: (measured) => registerItem(value, measured), ref: externalRef, ignoreStrict: true, children: children }));
  10757. }
  10758. const ReorderItem = /*@__PURE__*/ React$1.forwardRef(ReorderItemComponent);
  10759. var namespace = /*#__PURE__*/Object.freeze({
  10760. __proto__: null,
  10761. Group: ReorderGroup,
  10762. Item: ReorderItem
  10763. });
  10764. const wrap = (min, max, v) => {
  10765. const rangeSize = max - min;
  10766. return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min;
  10767. };
  10768. function getEasingForSegment(easing, i) {
  10769. return isEasingArray(easing) ? easing[wrap(0, easing.length, i)] : easing;
  10770. }
  10771. function isDOMKeyframes(keyframes) {
  10772. return typeof keyframes === "object" && !Array.isArray(keyframes);
  10773. }
  10774. function resolveSubjects(subject, keyframes, scope, selectorCache) {
  10775. if (typeof subject === "string" && isDOMKeyframes(keyframes)) {
  10776. return resolveElements(subject, scope, selectorCache);
  10777. }
  10778. else if (subject instanceof NodeList) {
  10779. return Array.from(subject);
  10780. }
  10781. else if (Array.isArray(subject)) {
  10782. return subject;
  10783. }
  10784. else {
  10785. return [subject];
  10786. }
  10787. }
  10788. function calculateRepeatDuration(duration, repeat, _repeatDelay) {
  10789. return duration * (repeat + 1);
  10790. }
  10791. /**
  10792. * Given a absolute or relative time definition and current/prev time state of the sequence,
  10793. * calculate an absolute time for the next keyframes.
  10794. */
  10795. function calcNextTime(current, next, prev, labels) {
  10796. if (typeof next === "number") {
  10797. return next;
  10798. }
  10799. else if (next.startsWith("-") || next.startsWith("+")) {
  10800. return Math.max(0, current + parseFloat(next));
  10801. }
  10802. else if (next === "<") {
  10803. return prev;
  10804. }
  10805. else {
  10806. return labels.get(next) ?? current;
  10807. }
  10808. }
  10809. function eraseKeyframes(sequence, startTime, endTime) {
  10810. for (let i = 0; i < sequence.length; i++) {
  10811. const keyframe = sequence[i];
  10812. if (keyframe.at > startTime && keyframe.at < endTime) {
  10813. removeItem(sequence, keyframe);
  10814. // If we remove this item we have to push the pointer back one
  10815. i--;
  10816. }
  10817. }
  10818. }
  10819. function addKeyframes(sequence, keyframes, easing, offset, startTime, endTime) {
  10820. /**
  10821. * Erase every existing value between currentTime and targetTime,
  10822. * this will essentially splice this timeline into any currently
  10823. * defined ones.
  10824. */
  10825. eraseKeyframes(sequence, startTime, endTime);
  10826. for (let i = 0; i < keyframes.length; i++) {
  10827. sequence.push({
  10828. value: keyframes[i],
  10829. at: mixNumber$1(startTime, endTime, offset[i]),
  10830. easing: getEasingForSegment(easing, i),
  10831. });
  10832. }
  10833. }
  10834. /**
  10835. * Take an array of times that represent repeated keyframes. For instance
  10836. * if we have original times of [0, 0.5, 1] then our repeated times will
  10837. * be [0, 0.5, 1, 1, 1.5, 2]. Loop over the times and scale them back
  10838. * down to a 0-1 scale.
  10839. */
  10840. function normalizeTimes(times, repeat) {
  10841. for (let i = 0; i < times.length; i++) {
  10842. times[i] = times[i] / (repeat + 1);
  10843. }
  10844. }
  10845. function compareByTime(a, b) {
  10846. if (a.at === b.at) {
  10847. if (a.value === null)
  10848. return 1;
  10849. if (b.value === null)
  10850. return -1;
  10851. return 0;
  10852. }
  10853. else {
  10854. return a.at - b.at;
  10855. }
  10856. }
  10857. const defaultSegmentEasing = "easeInOut";
  10858. const MAX_REPEAT = 20;
  10859. function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...sequenceTransition } = {}, scope, generators) {
  10860. const defaultDuration = defaultTransition.duration || 0.3;
  10861. const animationDefinitions = new Map();
  10862. const sequences = new Map();
  10863. const elementCache = {};
  10864. const timeLabels = new Map();
  10865. let prevTime = 0;
  10866. let currentTime = 0;
  10867. let totalDuration = 0;
  10868. /**
  10869. * Build the timeline by mapping over the sequence array and converting
  10870. * the definitions into keyframes and offsets with absolute time values.
  10871. * These will later get converted into relative offsets in a second pass.
  10872. */
  10873. for (let i = 0; i < sequence.length; i++) {
  10874. const segment = sequence[i];
  10875. /**
  10876. * If this is a timeline label, mark it and skip the rest of this iteration.
  10877. */
  10878. if (typeof segment === "string") {
  10879. timeLabels.set(segment, currentTime);
  10880. continue;
  10881. }
  10882. else if (!Array.isArray(segment)) {
  10883. timeLabels.set(segment.name, calcNextTime(currentTime, segment.at, prevTime, timeLabels));
  10884. continue;
  10885. }
  10886. let [subject, keyframes, transition = {}] = segment;
  10887. /**
  10888. * If a relative or absolute time value has been specified we need to resolve
  10889. * it in relation to the currentTime.
  10890. */
  10891. if (transition.at !== undefined) {
  10892. currentTime = calcNextTime(currentTime, transition.at, prevTime, timeLabels);
  10893. }
  10894. /**
  10895. * Keep track of the maximum duration in this definition. This will be
  10896. * applied to currentTime once the definition has been parsed.
  10897. */
  10898. let maxDuration = 0;
  10899. const resolveValueSequence = (valueKeyframes, valueTransition, valueSequence, elementIndex = 0, numSubjects = 0) => {
  10900. const valueKeyframesAsList = keyframesAsList(valueKeyframes);
  10901. const { delay = 0, times = defaultOffset$1(valueKeyframesAsList), type = "keyframes", repeat, repeatType, repeatDelay = 0, ...remainingTransition } = valueTransition;
  10902. let { ease = defaultTransition.ease || "easeOut", duration } = valueTransition;
  10903. /**
  10904. * Resolve stagger() if defined.
  10905. */
  10906. const calculatedDelay = typeof delay === "function"
  10907. ? delay(elementIndex, numSubjects)
  10908. : delay;
  10909. /**
  10910. * If this animation should and can use a spring, generate a spring easing function.
  10911. */
  10912. const numKeyframes = valueKeyframesAsList.length;
  10913. const createGenerator = isGenerator(type)
  10914. ? type
  10915. : generators?.[type];
  10916. if (numKeyframes <= 2 && createGenerator) {
  10917. /**
  10918. * As we're creating an easing function from a spring,
  10919. * ideally we want to generate it using the real distance
  10920. * between the two keyframes. However this isn't always
  10921. * possible - in these situations we use 0-100.
  10922. */
  10923. let absoluteDelta = 100;
  10924. if (numKeyframes === 2 &&
  10925. isNumberKeyframesArray(valueKeyframesAsList)) {
  10926. const delta = valueKeyframesAsList[1] - valueKeyframesAsList[0];
  10927. absoluteDelta = Math.abs(delta);
  10928. }
  10929. const springTransition = { ...remainingTransition };
  10930. if (duration !== undefined) {
  10931. springTransition.duration = secondsToMilliseconds(duration);
  10932. }
  10933. const springEasing = createGeneratorEasing(springTransition, absoluteDelta, createGenerator);
  10934. ease = springEasing.ease;
  10935. duration = springEasing.duration;
  10936. }
  10937. duration ?? (duration = defaultDuration);
  10938. const startTime = currentTime + calculatedDelay;
  10939. /**
  10940. * If there's only one time offset of 0, fill in a second with length 1
  10941. */
  10942. if (times.length === 1 && times[0] === 0) {
  10943. times[1] = 1;
  10944. }
  10945. /**
  10946. * Fill out if offset if fewer offsets than keyframes
  10947. */
  10948. const remainder = times.length - valueKeyframesAsList.length;
  10949. remainder > 0 && fillOffset(times, remainder);
  10950. /**
  10951. * If only one value has been set, ie [1], push a null to the start of
  10952. * the keyframe array. This will let us mark a keyframe at this point
  10953. * that will later be hydrated with the previous value.
  10954. */
  10955. valueKeyframesAsList.length === 1 &&
  10956. valueKeyframesAsList.unshift(null);
  10957. /**
  10958. * Handle repeat options
  10959. */
  10960. if (repeat) {
  10961. exports.invariant(repeat < MAX_REPEAT, "Repeat count too high, must be less than 20");
  10962. duration = calculateRepeatDuration(duration, repeat);
  10963. const originalKeyframes = [...valueKeyframesAsList];
  10964. const originalTimes = [...times];
  10965. ease = Array.isArray(ease) ? [...ease] : [ease];
  10966. const originalEase = [...ease];
  10967. for (let repeatIndex = 0; repeatIndex < repeat; repeatIndex++) {
  10968. valueKeyframesAsList.push(...originalKeyframes);
  10969. for (let keyframeIndex = 0; keyframeIndex < originalKeyframes.length; keyframeIndex++) {
  10970. times.push(originalTimes[keyframeIndex] + (repeatIndex + 1));
  10971. ease.push(keyframeIndex === 0
  10972. ? "linear"
  10973. : getEasingForSegment(originalEase, keyframeIndex - 1));
  10974. }
  10975. }
  10976. normalizeTimes(times, repeat);
  10977. }
  10978. const targetTime = startTime + duration;
  10979. /**
  10980. * Add keyframes, mapping offsets to absolute time.
  10981. */
  10982. addKeyframes(valueSequence, valueKeyframesAsList, ease, times, startTime, targetTime);
  10983. maxDuration = Math.max(calculatedDelay + duration, maxDuration);
  10984. totalDuration = Math.max(targetTime, totalDuration);
  10985. };
  10986. if (isMotionValue(subject)) {
  10987. const subjectSequence = getSubjectSequence(subject, sequences);
  10988. resolveValueSequence(keyframes, transition, getValueSequence("default", subjectSequence));
  10989. }
  10990. else {
  10991. const subjects = resolveSubjects(subject, keyframes, scope, elementCache);
  10992. const numSubjects = subjects.length;
  10993. /**
  10994. * For every element in this segment, process the defined values.
  10995. */
  10996. for (let subjectIndex = 0; subjectIndex < numSubjects; subjectIndex++) {
  10997. /**
  10998. * Cast necessary, but we know these are of this type
  10999. */
  11000. keyframes = keyframes;
  11001. transition = transition;
  11002. const thisSubject = subjects[subjectIndex];
  11003. const subjectSequence = getSubjectSequence(thisSubject, sequences);
  11004. for (const key in keyframes) {
  11005. resolveValueSequence(keyframes[key], getValueTransition(transition, key), getValueSequence(key, subjectSequence), subjectIndex, numSubjects);
  11006. }
  11007. }
  11008. }
  11009. prevTime = currentTime;
  11010. currentTime += maxDuration;
  11011. }
  11012. /**
  11013. * For every element and value combination create a new animation.
  11014. */
  11015. sequences.forEach((valueSequences, element) => {
  11016. for (const key in valueSequences) {
  11017. const valueSequence = valueSequences[key];
  11018. /**
  11019. * Arrange all the keyframes in ascending time order.
  11020. */
  11021. valueSequence.sort(compareByTime);
  11022. const keyframes = [];
  11023. const valueOffset = [];
  11024. const valueEasing = [];
  11025. /**
  11026. * For each keyframe, translate absolute times into
  11027. * relative offsets based on the total duration of the timeline.
  11028. */
  11029. for (let i = 0; i < valueSequence.length; i++) {
  11030. const { at, value, easing } = valueSequence[i];
  11031. keyframes.push(value);
  11032. valueOffset.push(progress(0, totalDuration, at));
  11033. valueEasing.push(easing || "easeOut");
  11034. }
  11035. /**
  11036. * If the first keyframe doesn't land on offset: 0
  11037. * provide one by duplicating the initial keyframe. This ensures
  11038. * it snaps to the first keyframe when the animation starts.
  11039. */
  11040. if (valueOffset[0] !== 0) {
  11041. valueOffset.unshift(0);
  11042. keyframes.unshift(keyframes[0]);
  11043. valueEasing.unshift(defaultSegmentEasing);
  11044. }
  11045. /**
  11046. * If the last keyframe doesn't land on offset: 1
  11047. * provide one with a null wildcard value. This will ensure it
  11048. * stays static until the end of the animation.
  11049. */
  11050. if (valueOffset[valueOffset.length - 1] !== 1) {
  11051. valueOffset.push(1);
  11052. keyframes.push(null);
  11053. }
  11054. if (!animationDefinitions.has(element)) {
  11055. animationDefinitions.set(element, {
  11056. keyframes: {},
  11057. transition: {},
  11058. });
  11059. }
  11060. const definition = animationDefinitions.get(element);
  11061. definition.keyframes[key] = keyframes;
  11062. definition.transition[key] = {
  11063. ...defaultTransition,
  11064. duration: totalDuration,
  11065. ease: valueEasing,
  11066. times: valueOffset,
  11067. ...sequenceTransition,
  11068. };
  11069. }
  11070. });
  11071. return animationDefinitions;
  11072. }
  11073. function getSubjectSequence(subject, sequences) {
  11074. !sequences.has(subject) && sequences.set(subject, {});
  11075. return sequences.get(subject);
  11076. }
  11077. function getValueSequence(name, sequences) {
  11078. if (!sequences[name])
  11079. sequences[name] = [];
  11080. return sequences[name];
  11081. }
  11082. function keyframesAsList(keyframes) {
  11083. return Array.isArray(keyframes) ? keyframes : [keyframes];
  11084. }
  11085. function getValueTransition(transition, key) {
  11086. return transition && transition[key]
  11087. ? {
  11088. ...transition,
  11089. ...transition[key],
  11090. }
  11091. : { ...transition };
  11092. }
  11093. const isNumber = (keyframe) => typeof keyframe === "number";
  11094. const isNumberKeyframesArray = (keyframes) => keyframes.every(isNumber);
  11095. function isObjectKey(key, object) {
  11096. return key in object;
  11097. }
  11098. class ObjectVisualElement extends VisualElement {
  11099. constructor() {
  11100. super(...arguments);
  11101. this.type = "object";
  11102. }
  11103. readValueFromInstance(instance, key) {
  11104. if (isObjectKey(key, instance)) {
  11105. const value = instance[key];
  11106. if (typeof value === "string" || typeof value === "number") {
  11107. return value;
  11108. }
  11109. }
  11110. return undefined;
  11111. }
  11112. getBaseTargetFromProps() {
  11113. return undefined;
  11114. }
  11115. removeValueFromRenderState(key, renderState) {
  11116. delete renderState.output[key];
  11117. }
  11118. measureInstanceViewportBox() {
  11119. return createBox();
  11120. }
  11121. build(renderState, latestValues) {
  11122. Object.assign(renderState.output, latestValues);
  11123. }
  11124. renderInstance(instance, { output }) {
  11125. Object.assign(instance, output);
  11126. }
  11127. sortInstanceNodePosition() {
  11128. return 0;
  11129. }
  11130. }
  11131. function createDOMVisualElement(element) {
  11132. const options = {
  11133. presenceContext: null,
  11134. props: {},
  11135. visualState: {
  11136. renderState: {
  11137. transform: {},
  11138. transformOrigin: {},
  11139. style: {},
  11140. vars: {},
  11141. attrs: {},
  11142. },
  11143. latestValues: {},
  11144. },
  11145. };
  11146. const node = isSVGElement(element)
  11147. ? new SVGVisualElement(options)
  11148. : new HTMLVisualElement(options);
  11149. node.mount(element);
  11150. visualElementStore.set(element, node);
  11151. }
  11152. function createObjectVisualElement(subject) {
  11153. const options = {
  11154. presenceContext: null,
  11155. props: {},
  11156. visualState: {
  11157. renderState: {
  11158. output: {},
  11159. },
  11160. latestValues: {},
  11161. },
  11162. };
  11163. const node = new ObjectVisualElement(options);
  11164. node.mount(subject);
  11165. visualElementStore.set(subject, node);
  11166. }
  11167. function isSingleValue(subject, keyframes) {
  11168. return (isMotionValue(subject) ||
  11169. typeof subject === "number" ||
  11170. (typeof subject === "string" && !isDOMKeyframes(keyframes)));
  11171. }
  11172. /**
  11173. * Implementation
  11174. */
  11175. function animateSubject(subject, keyframes, options, scope) {
  11176. const animations = [];
  11177. if (isSingleValue(subject, keyframes)) {
  11178. animations.push(animateSingleValue(subject, isDOMKeyframes(keyframes)
  11179. ? keyframes.default || keyframes
  11180. : keyframes, options ? options.default || options : options));
  11181. }
  11182. else {
  11183. const subjects = resolveSubjects(subject, keyframes, scope);
  11184. const numSubjects = subjects.length;
  11185. exports.invariant(Boolean(numSubjects), "No valid elements provided.");
  11186. for (let i = 0; i < numSubjects; i++) {
  11187. const thisSubject = subjects[i];
  11188. const createVisualElement = thisSubject instanceof Element
  11189. ? createDOMVisualElement
  11190. : createObjectVisualElement;
  11191. if (!visualElementStore.has(thisSubject)) {
  11192. createVisualElement(thisSubject);
  11193. }
  11194. const visualElement = visualElementStore.get(thisSubject);
  11195. const transition = { ...options };
  11196. /**
  11197. * Resolve stagger function if provided.
  11198. */
  11199. if ("delay" in transition &&
  11200. typeof transition.delay === "function") {
  11201. transition.delay = transition.delay(i, numSubjects);
  11202. }
  11203. animations.push(...animateTarget(visualElement, { ...keyframes, transition }, {}));
  11204. }
  11205. }
  11206. return animations;
  11207. }
  11208. function animateSequence(sequence, options, scope) {
  11209. const animations = [];
  11210. const animationDefinitions = createAnimationsFromSequence(sequence, options, scope, { spring });
  11211. animationDefinitions.forEach(({ keyframes, transition }, subject) => {
  11212. animations.push(...animateSubject(subject, keyframes, transition));
  11213. });
  11214. return animations;
  11215. }
  11216. function isSequence(value) {
  11217. return Array.isArray(value) && value.some(Array.isArray);
  11218. }
  11219. /**
  11220. * Creates an animation function that is optionally scoped
  11221. * to a specific element.
  11222. */
  11223. function createScopedAnimate(scope) {
  11224. /**
  11225. * Implementation
  11226. */
  11227. function scopedAnimate(subjectOrSequence, optionsOrKeyframes, options) {
  11228. let animations = [];
  11229. if (isSequence(subjectOrSequence)) {
  11230. animations = animateSequence(subjectOrSequence, optionsOrKeyframes, scope);
  11231. }
  11232. else {
  11233. animations = animateSubject(subjectOrSequence, optionsOrKeyframes, options, scope);
  11234. }
  11235. const animation = new GroupAnimationWithThen(animations);
  11236. if (scope) {
  11237. scope.animations.push(animation);
  11238. }
  11239. return animation;
  11240. }
  11241. return scopedAnimate;
  11242. }
  11243. const animate = createScopedAnimate();
  11244. function animateElements(elementOrSelector, keyframes, options, scope) {
  11245. const elements = resolveElements(elementOrSelector, scope);
  11246. const numElements = elements.length;
  11247. exports.invariant(Boolean(numElements), "No valid element provided.");
  11248. const animations = [];
  11249. for (let i = 0; i < numElements; i++) {
  11250. const element = elements[i];
  11251. const elementTransition = { ...options };
  11252. /**
  11253. * Resolve stagger function if provided.
  11254. */
  11255. if (typeof elementTransition.delay === "function") {
  11256. elementTransition.delay = elementTransition.delay(i, numElements);
  11257. }
  11258. for (const valueName in keyframes) {
  11259. const valueKeyframes = keyframes[valueName];
  11260. const valueOptions = {
  11261. ...getValueTransition$1(elementTransition, valueName),
  11262. };
  11263. valueOptions.duration && (valueOptions.duration = secondsToMilliseconds(valueOptions.duration));
  11264. valueOptions.delay && (valueOptions.delay = secondsToMilliseconds(valueOptions.delay));
  11265. animations.push(new NativeAnimation({
  11266. element,
  11267. name: valueName,
  11268. keyframes: valueKeyframes,
  11269. transition: valueOptions,
  11270. allowFlatten: !elementTransition.type && !elementTransition.ease,
  11271. }));
  11272. }
  11273. }
  11274. return animations;
  11275. }
  11276. const createScopedWaapiAnimate = (scope) => {
  11277. function scopedAnimate(elementOrSelector, keyframes, options) {
  11278. return new GroupAnimationWithThen(animateElements(elementOrSelector, keyframes, options, scope));
  11279. }
  11280. return scopedAnimate;
  11281. };
  11282. const animateMini = /*@__PURE__*/ createScopedWaapiAnimate();
  11283. function observeTimeline(update, timeline) {
  11284. let prevProgress;
  11285. const onFrame = () => {
  11286. const { currentTime } = timeline;
  11287. const percentage = currentTime === null ? 0 : currentTime.value;
  11288. const progress = percentage / 100;
  11289. if (prevProgress !== progress) {
  11290. update(progress);
  11291. }
  11292. prevProgress = progress;
  11293. };
  11294. frame.update(onFrame, true);
  11295. return () => cancelFrame(onFrame);
  11296. }
  11297. const resizeHandlers = new WeakMap();
  11298. let observer;
  11299. function getElementSize(target, borderBoxSize) {
  11300. if (borderBoxSize) {
  11301. const { inlineSize, blockSize } = borderBoxSize[0];
  11302. return { width: inlineSize, height: blockSize };
  11303. }
  11304. else if (target instanceof SVGElement && "getBBox" in target) {
  11305. return target.getBBox();
  11306. }
  11307. else {
  11308. return {
  11309. width: target.offsetWidth,
  11310. height: target.offsetHeight,
  11311. };
  11312. }
  11313. }
  11314. function notifyTarget({ target, contentRect, borderBoxSize, }) {
  11315. resizeHandlers.get(target)?.forEach((handler) => {
  11316. handler({
  11317. target,
  11318. contentSize: contentRect,
  11319. get size() {
  11320. return getElementSize(target, borderBoxSize);
  11321. },
  11322. });
  11323. });
  11324. }
  11325. function notifyAll(entries) {
  11326. entries.forEach(notifyTarget);
  11327. }
  11328. function createResizeObserver() {
  11329. if (typeof ResizeObserver === "undefined")
  11330. return;
  11331. observer = new ResizeObserver(notifyAll);
  11332. }
  11333. function resizeElement(target, handler) {
  11334. if (!observer)
  11335. createResizeObserver();
  11336. const elements = resolveElements(target);
  11337. elements.forEach((element) => {
  11338. let elementHandlers = resizeHandlers.get(element);
  11339. if (!elementHandlers) {
  11340. elementHandlers = new Set();
  11341. resizeHandlers.set(element, elementHandlers);
  11342. }
  11343. elementHandlers.add(handler);
  11344. observer?.observe(element);
  11345. });
  11346. return () => {
  11347. elements.forEach((element) => {
  11348. const elementHandlers = resizeHandlers.get(element);
  11349. elementHandlers?.delete(handler);
  11350. if (!elementHandlers?.size) {
  11351. observer?.unobserve(element);
  11352. }
  11353. });
  11354. };
  11355. }
  11356. const windowCallbacks = new Set();
  11357. let windowResizeHandler;
  11358. function createWindowResizeHandler() {
  11359. windowResizeHandler = () => {
  11360. const size = {
  11361. width: window.innerWidth,
  11362. height: window.innerHeight,
  11363. };
  11364. const info = {
  11365. target: window,
  11366. size,
  11367. contentSize: size,
  11368. };
  11369. windowCallbacks.forEach((callback) => callback(info));
  11370. };
  11371. window.addEventListener("resize", windowResizeHandler);
  11372. }
  11373. function resizeWindow(callback) {
  11374. windowCallbacks.add(callback);
  11375. if (!windowResizeHandler)
  11376. createWindowResizeHandler();
  11377. return () => {
  11378. windowCallbacks.delete(callback);
  11379. if (!windowCallbacks.size && windowResizeHandler) {
  11380. windowResizeHandler = undefined;
  11381. }
  11382. };
  11383. }
  11384. function resize(a, b) {
  11385. return typeof a === "function" ? resizeWindow(a) : resizeElement(a, b);
  11386. }
  11387. /**
  11388. * A time in milliseconds, beyond which we consider the scroll velocity to be 0.
  11389. */
  11390. const maxElapsed = 50;
  11391. const createAxisInfo = () => ({
  11392. current: 0,
  11393. offset: [],
  11394. progress: 0,
  11395. scrollLength: 0,
  11396. targetOffset: 0,
  11397. targetLength: 0,
  11398. containerLength: 0,
  11399. velocity: 0,
  11400. });
  11401. const createScrollInfo = () => ({
  11402. time: 0,
  11403. x: createAxisInfo(),
  11404. y: createAxisInfo(),
  11405. });
  11406. const keys = {
  11407. x: {
  11408. length: "Width",
  11409. position: "Left",
  11410. },
  11411. y: {
  11412. length: "Height",
  11413. position: "Top",
  11414. },
  11415. };
  11416. function updateAxisInfo(element, axisName, info, time) {
  11417. const axis = info[axisName];
  11418. const { length, position } = keys[axisName];
  11419. const prev = axis.current;
  11420. const prevTime = info.time;
  11421. axis.current = element[`scroll${position}`];
  11422. axis.scrollLength = element[`scroll${length}`] - element[`client${length}`];
  11423. axis.offset.length = 0;
  11424. axis.offset[0] = 0;
  11425. axis.offset[1] = axis.scrollLength;
  11426. axis.progress = progress(0, axis.scrollLength, axis.current);
  11427. const elapsed = time - prevTime;
  11428. axis.velocity =
  11429. elapsed > maxElapsed
  11430. ? 0
  11431. : velocityPerSecond(axis.current - prev, elapsed);
  11432. }
  11433. function updateScrollInfo(element, info, time) {
  11434. updateAxisInfo(element, "x", info, time);
  11435. updateAxisInfo(element, "y", info, time);
  11436. info.time = time;
  11437. }
  11438. function calcInset(element, container) {
  11439. const inset = { x: 0, y: 0 };
  11440. let current = element;
  11441. while (current && current !== container) {
  11442. if (current instanceof HTMLElement) {
  11443. inset.x += current.offsetLeft;
  11444. inset.y += current.offsetTop;
  11445. current = current.offsetParent;
  11446. }
  11447. else if (current.tagName === "svg") {
  11448. /**
  11449. * This isn't an ideal approach to measuring the offset of <svg /> tags.
  11450. * It would be preferable, given they behave like HTMLElements in most ways
  11451. * to use offsetLeft/Top. But these don't exist on <svg />. Likewise we
  11452. * can't use .getBBox() like most SVG elements as these provide the offset
  11453. * relative to the SVG itself, which for <svg /> is usually 0x0.
  11454. */
  11455. const svgBoundingBox = current.getBoundingClientRect();
  11456. current = current.parentElement;
  11457. const parentBoundingBox = current.getBoundingClientRect();
  11458. inset.x += svgBoundingBox.left - parentBoundingBox.left;
  11459. inset.y += svgBoundingBox.top - parentBoundingBox.top;
  11460. }
  11461. else if (current instanceof SVGGraphicsElement) {
  11462. const { x, y } = current.getBBox();
  11463. inset.x += x;
  11464. inset.y += y;
  11465. let svg = null;
  11466. let parent = current.parentNode;
  11467. while (!svg) {
  11468. if (parent.tagName === "svg") {
  11469. svg = parent;
  11470. }
  11471. parent = current.parentNode;
  11472. }
  11473. current = svg;
  11474. }
  11475. else {
  11476. break;
  11477. }
  11478. }
  11479. return inset;
  11480. }
  11481. const namedEdges = {
  11482. start: 0,
  11483. center: 0.5,
  11484. end: 1,
  11485. };
  11486. function resolveEdge(edge, length, inset = 0) {
  11487. let delta = 0;
  11488. /**
  11489. * If we have this edge defined as a preset, replace the definition
  11490. * with the numerical value.
  11491. */
  11492. if (edge in namedEdges) {
  11493. edge = namedEdges[edge];
  11494. }
  11495. /**
  11496. * Handle unit values
  11497. */
  11498. if (typeof edge === "string") {
  11499. const asNumber = parseFloat(edge);
  11500. if (edge.endsWith("px")) {
  11501. delta = asNumber;
  11502. }
  11503. else if (edge.endsWith("%")) {
  11504. edge = asNumber / 100;
  11505. }
  11506. else if (edge.endsWith("vw")) {
  11507. delta = (asNumber / 100) * document.documentElement.clientWidth;
  11508. }
  11509. else if (edge.endsWith("vh")) {
  11510. delta = (asNumber / 100) * document.documentElement.clientHeight;
  11511. }
  11512. else {
  11513. edge = asNumber;
  11514. }
  11515. }
  11516. /**
  11517. * If the edge is defined as a number, handle as a progress value.
  11518. */
  11519. if (typeof edge === "number") {
  11520. delta = length * edge;
  11521. }
  11522. return inset + delta;
  11523. }
  11524. const defaultOffset = [0, 0];
  11525. function resolveOffset(offset, containerLength, targetLength, targetInset) {
  11526. let offsetDefinition = Array.isArray(offset) ? offset : defaultOffset;
  11527. let targetPoint = 0;
  11528. let containerPoint = 0;
  11529. if (typeof offset === "number") {
  11530. /**
  11531. * If we're provided offset: [0, 0.5, 1] then each number x should become
  11532. * [x, x], so we default to the behaviour of mapping 0 => 0 of both target
  11533. * and container etc.
  11534. */
  11535. offsetDefinition = [offset, offset];
  11536. }
  11537. else if (typeof offset === "string") {
  11538. offset = offset.trim();
  11539. if (offset.includes(" ")) {
  11540. offsetDefinition = offset.split(" ");
  11541. }
  11542. else {
  11543. /**
  11544. * If we're provided a definition like "100px" then we want to apply
  11545. * that only to the top of the target point, leaving the container at 0.
  11546. * Whereas a named offset like "end" should be applied to both.
  11547. */
  11548. offsetDefinition = [offset, namedEdges[offset] ? offset : `0`];
  11549. }
  11550. }
  11551. targetPoint = resolveEdge(offsetDefinition[0], targetLength, targetInset);
  11552. containerPoint = resolveEdge(offsetDefinition[1], containerLength);
  11553. return targetPoint - containerPoint;
  11554. }
  11555. const ScrollOffset = {
  11556. Enter: [
  11557. [0, 1],
  11558. [1, 1],
  11559. ],
  11560. Exit: [
  11561. [0, 0],
  11562. [1, 0],
  11563. ],
  11564. Any: [
  11565. [1, 0],
  11566. [0, 1],
  11567. ],
  11568. All: [
  11569. [0, 0],
  11570. [1, 1],
  11571. ],
  11572. };
  11573. const point = { x: 0, y: 0 };
  11574. function getTargetSize(target) {
  11575. return "getBBox" in target && target.tagName !== "svg"
  11576. ? target.getBBox()
  11577. : { width: target.clientWidth, height: target.clientHeight };
  11578. }
  11579. function resolveOffsets(container, info, options) {
  11580. const { offset: offsetDefinition = ScrollOffset.All } = options;
  11581. const { target = container, axis = "y" } = options;
  11582. const lengthLabel = axis === "y" ? "height" : "width";
  11583. const inset = target !== container ? calcInset(target, container) : point;
  11584. /**
  11585. * Measure the target and container. If they're the same thing then we
  11586. * use the container's scrollWidth/Height as the target, from there
  11587. * all other calculations can remain the same.
  11588. */
  11589. const targetSize = target === container
  11590. ? { width: container.scrollWidth, height: container.scrollHeight }
  11591. : getTargetSize(target);
  11592. const containerSize = {
  11593. width: container.clientWidth,
  11594. height: container.clientHeight,
  11595. };
  11596. /**
  11597. * Reset the length of the resolved offset array rather than creating a new one.
  11598. * TODO: More reusable data structures for targetSize/containerSize would also be good.
  11599. */
  11600. info[axis].offset.length = 0;
  11601. /**
  11602. * Populate the offset array by resolving the user's offset definition into
  11603. * a list of pixel scroll offets.
  11604. */
  11605. let hasChanged = !info[axis].interpolate;
  11606. const numOffsets = offsetDefinition.length;
  11607. for (let i = 0; i < numOffsets; i++) {
  11608. const offset = resolveOffset(offsetDefinition[i], containerSize[lengthLabel], targetSize[lengthLabel], inset[axis]);
  11609. if (!hasChanged && offset !== info[axis].interpolatorOffsets[i]) {
  11610. hasChanged = true;
  11611. }
  11612. info[axis].offset[i] = offset;
  11613. }
  11614. /**
  11615. * If the pixel scroll offsets have changed, create a new interpolator function
  11616. * to map scroll value into a progress.
  11617. */
  11618. if (hasChanged) {
  11619. info[axis].interpolate = interpolate(info[axis].offset, defaultOffset$1(offsetDefinition), { clamp: false });
  11620. info[axis].interpolatorOffsets = [...info[axis].offset];
  11621. }
  11622. info[axis].progress = clamp(0, 1, info[axis].interpolate(info[axis].current));
  11623. }
  11624. function measure(container, target = container, info) {
  11625. /**
  11626. * Find inset of target within scrollable container
  11627. */
  11628. info.x.targetOffset = 0;
  11629. info.y.targetOffset = 0;
  11630. if (target !== container) {
  11631. let node = target;
  11632. while (node && node !== container) {
  11633. info.x.targetOffset += node.offsetLeft;
  11634. info.y.targetOffset += node.offsetTop;
  11635. node = node.offsetParent;
  11636. }
  11637. }
  11638. info.x.targetLength =
  11639. target === container ? target.scrollWidth : target.clientWidth;
  11640. info.y.targetLength =
  11641. target === container ? target.scrollHeight : target.clientHeight;
  11642. info.x.containerLength = container.clientWidth;
  11643. info.y.containerLength = container.clientHeight;
  11644. /**
  11645. * In development mode ensure scroll containers aren't position: static as this makes
  11646. * it difficult to measure their relative positions.
  11647. */
  11648. {
  11649. if (container && target && target !== container) {
  11650. warnOnce(getComputedStyle(container).position !== "static", "Please ensure that the container has a non-static position, like 'relative', 'fixed', or 'absolute' to ensure scroll offset is calculated correctly.");
  11651. }
  11652. }
  11653. }
  11654. function createOnScrollHandler(element, onScroll, info, options = {}) {
  11655. return {
  11656. measure: () => measure(element, options.target, info),
  11657. update: (time) => {
  11658. updateScrollInfo(element, info, time);
  11659. if (options.offset || options.target) {
  11660. resolveOffsets(element, info, options);
  11661. }
  11662. },
  11663. notify: () => onScroll(info),
  11664. };
  11665. }
  11666. const scrollListeners = new WeakMap();
  11667. const resizeListeners = new WeakMap();
  11668. const onScrollHandlers = new WeakMap();
  11669. const getEventTarget = (element) => element === document.documentElement ? window : element;
  11670. function scrollInfo(onScroll, { container = document.documentElement, ...options } = {}) {
  11671. let containerHandlers = onScrollHandlers.get(container);
  11672. /**
  11673. * Get the onScroll handlers for this container.
  11674. * If one isn't found, create a new one.
  11675. */
  11676. if (!containerHandlers) {
  11677. containerHandlers = new Set();
  11678. onScrollHandlers.set(container, containerHandlers);
  11679. }
  11680. /**
  11681. * Create a new onScroll handler for the provided callback.
  11682. */
  11683. const info = createScrollInfo();
  11684. const containerHandler = createOnScrollHandler(container, onScroll, info, options);
  11685. containerHandlers.add(containerHandler);
  11686. /**
  11687. * Check if there's a scroll event listener for this container.
  11688. * If not, create one.
  11689. */
  11690. if (!scrollListeners.has(container)) {
  11691. const measureAll = () => {
  11692. for (const handler of containerHandlers)
  11693. handler.measure();
  11694. };
  11695. const updateAll = () => {
  11696. for (const handler of containerHandlers) {
  11697. handler.update(frameData.timestamp);
  11698. }
  11699. };
  11700. const notifyAll = () => {
  11701. for (const handler of containerHandlers)
  11702. handler.notify();
  11703. };
  11704. const listener = () => {
  11705. frame.read(measureAll, false, true);
  11706. frame.read(updateAll, false, true);
  11707. frame.update(notifyAll, false, true);
  11708. };
  11709. scrollListeners.set(container, listener);
  11710. const target = getEventTarget(container);
  11711. window.addEventListener("resize", listener, { passive: true });
  11712. if (container !== document.documentElement) {
  11713. resizeListeners.set(container, resize(container, listener));
  11714. }
  11715. target.addEventListener("scroll", listener, { passive: true });
  11716. }
  11717. const listener = scrollListeners.get(container);
  11718. frame.read(listener, false, true);
  11719. return () => {
  11720. cancelFrame(listener);
  11721. /**
  11722. * Check if we even have any handlers for this container.
  11723. */
  11724. const currentHandlers = onScrollHandlers.get(container);
  11725. if (!currentHandlers)
  11726. return;
  11727. currentHandlers.delete(containerHandler);
  11728. if (currentHandlers.size)
  11729. return;
  11730. /**
  11731. * If no more handlers, remove the scroll listener too.
  11732. */
  11733. const scrollListener = scrollListeners.get(container);
  11734. scrollListeners.delete(container);
  11735. if (scrollListener) {
  11736. getEventTarget(container).removeEventListener("scroll", scrollListener);
  11737. resizeListeners.get(container)?.();
  11738. window.removeEventListener("resize", scrollListener);
  11739. }
  11740. };
  11741. }
  11742. function scrollTimelineFallback({ source, container, axis = "y", }) {
  11743. // Support legacy source argument. Deprecate later.
  11744. if (source)
  11745. container = source;
  11746. // ScrollTimeline records progress as a percentage CSSUnitValue
  11747. const currentTime = { value: 0 };
  11748. const cancel = scrollInfo((info) => {
  11749. currentTime.value = info[axis].progress * 100;
  11750. }, { container, axis });
  11751. return { currentTime, cancel };
  11752. }
  11753. const timelineCache = new Map();
  11754. function getTimeline({ source, container = document.documentElement, axis = "y", } = {}) {
  11755. // Support legacy source argument. Deprecate later.
  11756. if (source)
  11757. container = source;
  11758. if (!timelineCache.has(container)) {
  11759. timelineCache.set(container, {});
  11760. }
  11761. const elementCache = timelineCache.get(container);
  11762. if (!elementCache[axis]) {
  11763. elementCache[axis] = supportsScrollTimeline()
  11764. ? new ScrollTimeline({ source: container, axis })
  11765. : scrollTimelineFallback({ source: container, axis });
  11766. }
  11767. return elementCache[axis];
  11768. }
  11769. /**
  11770. * If the onScroll function has two arguments, it's expecting
  11771. * more specific information about the scroll from scrollInfo.
  11772. */
  11773. function isOnScrollWithInfo(onScroll) {
  11774. return onScroll.length === 2;
  11775. }
  11776. /**
  11777. * Currently, we only support element tracking with `scrollInfo`, though in
  11778. * the future we can also offer ViewTimeline support.
  11779. */
  11780. function needsElementTracking(options) {
  11781. return options && (options.target || options.offset);
  11782. }
  11783. function scrollFunction(onScroll, options) {
  11784. if (isOnScrollWithInfo(onScroll) || needsElementTracking(options)) {
  11785. return scrollInfo((info) => {
  11786. onScroll(info[options.axis].progress, info);
  11787. }, options);
  11788. }
  11789. else {
  11790. return observeTimeline(onScroll, getTimeline(options));
  11791. }
  11792. }
  11793. function scrollAnimation(animation, options) {
  11794. animation.flatten();
  11795. if (needsElementTracking(options)) {
  11796. animation.pause();
  11797. return scrollInfo((info) => {
  11798. animation.time = animation.duration * info[options.axis].progress;
  11799. }, options);
  11800. }
  11801. else {
  11802. const timeline = getTimeline(options);
  11803. if (animation.attachTimeline) {
  11804. return animation.attachTimeline(timeline, (valueAnimation) => {
  11805. valueAnimation.pause();
  11806. return observeTimeline((progress) => {
  11807. valueAnimation.time = valueAnimation.duration * progress;
  11808. }, timeline);
  11809. });
  11810. }
  11811. else {
  11812. return noop;
  11813. }
  11814. }
  11815. }
  11816. function scroll(onScroll, { axis = "y", ...options } = {}) {
  11817. const optionsWithDefaults = { axis, ...options };
  11818. return typeof onScroll === "function"
  11819. ? scrollFunction(onScroll, optionsWithDefaults)
  11820. : scrollAnimation(onScroll, optionsWithDefaults);
  11821. }
  11822. const thresholds = {
  11823. some: 0,
  11824. all: 1,
  11825. };
  11826. function inView(elementOrSelector, onStart, { root, margin: rootMargin, amount = "some" } = {}) {
  11827. const elements = resolveElements(elementOrSelector);
  11828. const activeIntersections = new WeakMap();
  11829. const onIntersectionChange = (entries) => {
  11830. entries.forEach((entry) => {
  11831. const onEnd = activeIntersections.get(entry.target);
  11832. /**
  11833. * If there's no change to the intersection, we don't need to
  11834. * do anything here.
  11835. */
  11836. if (entry.isIntersecting === Boolean(onEnd))
  11837. return;
  11838. if (entry.isIntersecting) {
  11839. const newOnEnd = onStart(entry.target, entry);
  11840. if (typeof newOnEnd === "function") {
  11841. activeIntersections.set(entry.target, newOnEnd);
  11842. }
  11843. else {
  11844. observer.unobserve(entry.target);
  11845. }
  11846. }
  11847. else if (typeof onEnd === "function") {
  11848. onEnd(entry);
  11849. activeIntersections.delete(entry.target);
  11850. }
  11851. });
  11852. };
  11853. const observer = new IntersectionObserver(onIntersectionChange, {
  11854. root,
  11855. rootMargin,
  11856. threshold: typeof amount === "number" ? amount : thresholds[amount],
  11857. });
  11858. elements.forEach((element) => observer.observe(element));
  11859. return () => observer.disconnect();
  11860. }
  11861. function steps(numSteps, direction = "end") {
  11862. return (progress) => {
  11863. progress =
  11864. direction === "end"
  11865. ? Math.min(progress, 0.999)
  11866. : Math.max(progress, 0.001);
  11867. const expanded = progress * numSteps;
  11868. const rounded = direction === "end" ? Math.floor(expanded) : Math.ceil(expanded);
  11869. return clamp(0, 1, rounded / numSteps);
  11870. };
  11871. }
  11872. function getOriginIndex(from, total) {
  11873. if (from === "first") {
  11874. return 0;
  11875. }
  11876. else {
  11877. const lastIndex = total - 1;
  11878. return from === "last" ? lastIndex : lastIndex / 2;
  11879. }
  11880. }
  11881. function stagger(duration = 0.1, { startDelay = 0, from = 0, ease } = {}) {
  11882. return (i, total) => {
  11883. const fromIndex = typeof from === "number" ? from : getOriginIndex(from, total);
  11884. const distance = Math.abs(fromIndex - i);
  11885. let delay = duration * distance;
  11886. if (ease) {
  11887. const maxDelay = total * duration;
  11888. const easingFunction = easingDefinitionToFunction(ease);
  11889. delay = easingFunction(delay / maxDelay) * maxDelay;
  11890. }
  11891. return startDelay + delay;
  11892. };
  11893. }
  11894. const createMinimalMotionComponent =
  11895. /*@__PURE__*/ createMotionComponentFactory();
  11896. const m = /*@__PURE__*/ createDOMMotionComponentProxy(createMinimalMotionComponent);
  11897. function useUnmountEffect(callback) {
  11898. return React$1.useEffect(() => () => callback(), []);
  11899. }
  11900. /**
  11901. * @public
  11902. */
  11903. const domAnimation = {
  11904. renderer: createDomVisualElement,
  11905. ...animations,
  11906. ...gestureAnimations,
  11907. };
  11908. /**
  11909. * @public
  11910. */
  11911. const domMax = {
  11912. ...domAnimation,
  11913. ...drag,
  11914. ...layout,
  11915. };
  11916. /**
  11917. * @public
  11918. */
  11919. const domMin = {
  11920. renderer: createDomVisualElement,
  11921. ...animations,
  11922. };
  11923. function useMotionValueEvent(value, event, callback) {
  11924. /**
  11925. * useInsertionEffect will create subscriptions before any other
  11926. * effects will run. Effects run upwards through the tree so it
  11927. * can be that binding a useLayoutEffect higher up the tree can
  11928. * miss changes from lower down the tree.
  11929. */
  11930. React$1.useInsertionEffect(() => value.on(event, callback), [value, event, callback]);
  11931. }
  11932. function refWarning(name, ref) {
  11933. warning(Boolean(!ref || ref.current), `You have defined a ${name} options but the provided ref is not yet hydrated, probably because it's defined higher up the tree. Try calling useScroll() in the same component as the ref, or setting its \`layoutEffect: false\` option.`);
  11934. }
  11935. const createScrollMotionValues = () => ({
  11936. scrollX: motionValue(0),
  11937. scrollY: motionValue(0),
  11938. scrollXProgress: motionValue(0),
  11939. scrollYProgress: motionValue(0),
  11940. });
  11941. function useScroll({ container, target, layoutEffect = true, ...options } = {}) {
  11942. const values = useConstant(createScrollMotionValues);
  11943. const useLifecycleEffect = layoutEffect
  11944. ? useIsomorphicLayoutEffect
  11945. : React$1.useEffect;
  11946. useLifecycleEffect(() => {
  11947. refWarning("target", target);
  11948. refWarning("container", container);
  11949. return scroll((_progress, { x, y, }) => {
  11950. values.scrollX.set(x.current);
  11951. values.scrollXProgress.set(x.progress);
  11952. values.scrollY.set(y.current);
  11953. values.scrollYProgress.set(y.progress);
  11954. }, {
  11955. ...options,
  11956. container: container?.current || undefined,
  11957. target: target?.current || undefined,
  11958. });
  11959. }, [container, target, JSON.stringify(options.offset)]);
  11960. return values;
  11961. }
  11962. /**
  11963. * @deprecated useElementScroll is deprecated. Convert to useScroll({ container: ref })
  11964. */
  11965. function useElementScroll(ref) {
  11966. {
  11967. warnOnce(false, "useElementScroll is deprecated. Convert to useScroll({ container: ref }).");
  11968. }
  11969. return useScroll({ container: ref });
  11970. }
  11971. /**
  11972. * @deprecated useViewportScroll is deprecated. Convert to useScroll()
  11973. */
  11974. function useViewportScroll() {
  11975. {
  11976. warnOnce(false, "useViewportScroll is deprecated. Convert to useScroll().");
  11977. }
  11978. return useScroll();
  11979. }
  11980. /**
  11981. * Combine multiple motion values into a new one using a string template literal.
  11982. *
  11983. * ```jsx
  11984. * import {
  11985. * motion,
  11986. * useSpring,
  11987. * useMotionValue,
  11988. * useMotionTemplate
  11989. * } from "framer-motion"
  11990. *
  11991. * function Component() {
  11992. * const shadowX = useSpring(0)
  11993. * const shadowY = useMotionValue(0)
  11994. * const shadow = useMotionTemplate`drop-shadow(${shadowX}px ${shadowY}px 20px rgba(0,0,0,0.3))`
  11995. *
  11996. * return <motion.div style={{ filter: shadow }} />
  11997. * }
  11998. * ```
  11999. *
  12000. * @public
  12001. */
  12002. function useMotionTemplate(fragments, ...values) {
  12003. /**
  12004. * Create a function that will build a string from the latest motion values.
  12005. */
  12006. const numFragments = fragments.length;
  12007. function buildValue() {
  12008. let output = ``;
  12009. for (let i = 0; i < numFragments; i++) {
  12010. output += fragments[i];
  12011. const value = values[i];
  12012. if (value) {
  12013. output += isMotionValue(value) ? value.get() : value;
  12014. }
  12015. }
  12016. return output;
  12017. }
  12018. return useCombineMotionValues(values.filter(isMotionValue), buildValue);
  12019. }
  12020. function useSpring(source, config = {}) {
  12021. const { isStatic } = React$1.useContext(MotionConfigContext);
  12022. const activeSpringAnimation = React$1.useRef(null);
  12023. const initialValue = useConstant(() => isMotionValue(source) ? source.get() : source);
  12024. const unit = useConstant(() => typeof initialValue === "string"
  12025. ? initialValue.replace(/[\d.-]/g, "")
  12026. : undefined);
  12027. const value = useMotionValue(initialValue);
  12028. const latestValue = React$1.useRef(initialValue);
  12029. const latestSetter = React$1.useRef(noop);
  12030. const startAnimation = () => {
  12031. stopAnimation();
  12032. activeSpringAnimation.current = animateValue({
  12033. keyframes: [asNumber(value.get()), asNumber(latestValue.current)],
  12034. velocity: value.getVelocity(),
  12035. type: "spring",
  12036. restDelta: 0.001,
  12037. restSpeed: 0.01,
  12038. ...config,
  12039. onUpdate: latestSetter.current,
  12040. });
  12041. };
  12042. const stopAnimation = () => {
  12043. if (activeSpringAnimation.current) {
  12044. activeSpringAnimation.current.stop();
  12045. }
  12046. };
  12047. React$1.useInsertionEffect(() => {
  12048. return value.attach((v, set) => {
  12049. if (isStatic)
  12050. return set(v);
  12051. latestValue.current = v;
  12052. latestSetter.current = (latest) => set(parseValue(latest, unit));
  12053. frame.postRender(startAnimation);
  12054. return value.get();
  12055. }, stopAnimation);
  12056. }, [JSON.stringify(config)]);
  12057. useIsomorphicLayoutEffect(() => {
  12058. if (isMotionValue(source)) {
  12059. return source.on("change", (v) => value.set(parseValue(v, unit)));
  12060. }
  12061. }, [value, unit]);
  12062. return value;
  12063. }
  12064. function parseValue(v, unit) {
  12065. return unit ? v + unit : v;
  12066. }
  12067. function asNumber(v) {
  12068. return typeof v === "number" ? v : parseFloat(v);
  12069. }
  12070. function useAnimationFrame(callback) {
  12071. const initialTimestamp = React$1.useRef(0);
  12072. const { isStatic } = React$1.useContext(MotionConfigContext);
  12073. React$1.useEffect(() => {
  12074. if (isStatic)
  12075. return;
  12076. const provideTimeSinceStart = ({ timestamp, delta }) => {
  12077. if (!initialTimestamp.current)
  12078. initialTimestamp.current = timestamp;
  12079. callback(timestamp - initialTimestamp.current, delta);
  12080. };
  12081. frame.update(provideTimeSinceStart, true);
  12082. return () => cancelFrame(provideTimeSinceStart);
  12083. }, [callback]);
  12084. }
  12085. function useTime() {
  12086. const time = useMotionValue(0);
  12087. useAnimationFrame((t) => time.set(t));
  12088. return time;
  12089. }
  12090. /**
  12091. * Creates a `MotionValue` that updates when the velocity of the provided `MotionValue` changes.
  12092. *
  12093. * ```javascript
  12094. * const x = useMotionValue(0)
  12095. * const xVelocity = useVelocity(x)
  12096. * const xAcceleration = useVelocity(xVelocity)
  12097. * ```
  12098. *
  12099. * @public
  12100. */
  12101. function useVelocity(value) {
  12102. const velocity = useMotionValue(value.getVelocity());
  12103. const updateVelocity = () => {
  12104. const latest = value.getVelocity();
  12105. velocity.set(latest);
  12106. /**
  12107. * If we still have velocity, schedule an update for the next frame
  12108. * to keep checking until it is zero.
  12109. */
  12110. if (latest)
  12111. frame.update(updateVelocity);
  12112. };
  12113. useMotionValueEvent(value, "change", () => {
  12114. // Schedule an update to this value at the end of the current frame.
  12115. frame.update(updateVelocity, false, true);
  12116. });
  12117. return velocity;
  12118. }
  12119. function getWillChangeName(name) {
  12120. if (transformProps.has(name)) {
  12121. return "transform";
  12122. }
  12123. else if (acceleratedValues.has(name)) {
  12124. return camelToDash(name);
  12125. }
  12126. }
  12127. class WillChangeMotionValue extends MotionValue {
  12128. constructor() {
  12129. super(...arguments);
  12130. this.values = [];
  12131. }
  12132. add(name) {
  12133. const styleName = getWillChangeName(name);
  12134. if (styleName) {
  12135. addUniqueItem(this.values, styleName);
  12136. this.update();
  12137. }
  12138. }
  12139. update() {
  12140. this.set(this.values.length ? this.values.join(", ") : "auto");
  12141. }
  12142. }
  12143. function useWillChange() {
  12144. return useConstant(() => new WillChangeMotionValue("auto"));
  12145. }
  12146. /**
  12147. * A hook that returns `true` if we should be using reduced motion based on the current device's Reduced Motion setting.
  12148. *
  12149. * This can be used to implement changes to your UI based on Reduced Motion. For instance, replacing motion-sickness inducing
  12150. * `x`/`y` animations with `opacity`, disabling the autoplay of background videos, or turning off parallax motion.
  12151. *
  12152. * It will actively respond to changes and re-render your components with the latest setting.
  12153. *
  12154. * ```jsx
  12155. * export function Sidebar({ isOpen }) {
  12156. * const shouldReduceMotion = useReducedMotion()
  12157. * const closedX = shouldReduceMotion ? 0 : "-100%"
  12158. *
  12159. * return (
  12160. * <motion.div animate={{
  12161. * opacity: isOpen ? 1 : 0,
  12162. * x: isOpen ? 0 : closedX
  12163. * }} />
  12164. * )
  12165. * }
  12166. * ```
  12167. *
  12168. * @return boolean
  12169. *
  12170. * @public
  12171. */
  12172. function useReducedMotion() {
  12173. /**
  12174. * Lazy initialisation of prefersReducedMotion
  12175. */
  12176. !hasReducedMotionListener.current && initPrefersReducedMotion();
  12177. const [shouldReduceMotion] = React$1.useState(prefersReducedMotion.current);
  12178. {
  12179. warnOnce(shouldReduceMotion !== true, "You have Reduced Motion enabled on your device. Animations may not appear as expected.");
  12180. }
  12181. /**
  12182. * TODO See if people miss automatically updating shouldReduceMotion setting
  12183. */
  12184. return shouldReduceMotion;
  12185. }
  12186. function useReducedMotionConfig() {
  12187. const reducedMotionPreference = useReducedMotion();
  12188. const { reducedMotion } = React$1.useContext(MotionConfigContext);
  12189. if (reducedMotion === "never") {
  12190. return false;
  12191. }
  12192. else if (reducedMotion === "always") {
  12193. return true;
  12194. }
  12195. else {
  12196. return reducedMotionPreference;
  12197. }
  12198. }
  12199. function stopAnimation(visualElement) {
  12200. visualElement.values.forEach((value) => value.stop());
  12201. }
  12202. function setVariants(visualElement, variantLabels) {
  12203. const reversedLabels = [...variantLabels].reverse();
  12204. reversedLabels.forEach((key) => {
  12205. const variant = visualElement.getVariant(key);
  12206. variant && setTarget(visualElement, variant);
  12207. if (visualElement.variantChildren) {
  12208. visualElement.variantChildren.forEach((child) => {
  12209. setVariants(child, variantLabels);
  12210. });
  12211. }
  12212. });
  12213. }
  12214. function setValues(visualElement, definition) {
  12215. if (Array.isArray(definition)) {
  12216. return setVariants(visualElement, definition);
  12217. }
  12218. else if (typeof definition === "string") {
  12219. return setVariants(visualElement, [definition]);
  12220. }
  12221. else {
  12222. setTarget(visualElement, definition);
  12223. }
  12224. }
  12225. /**
  12226. * @public
  12227. */
  12228. function animationControls() {
  12229. /**
  12230. * Track whether the host component has mounted.
  12231. */
  12232. let hasMounted = false;
  12233. /**
  12234. * A collection of linked component animation controls.
  12235. */
  12236. const subscribers = new Set();
  12237. const controls = {
  12238. subscribe(visualElement) {
  12239. subscribers.add(visualElement);
  12240. return () => void subscribers.delete(visualElement);
  12241. },
  12242. start(definition, transitionOverride) {
  12243. exports.invariant(hasMounted, "controls.start() should only be called after a component has mounted. Consider calling within a useEffect hook.");
  12244. const animations = [];
  12245. subscribers.forEach((visualElement) => {
  12246. animations.push(animateVisualElement(visualElement, definition, {
  12247. transitionOverride,
  12248. }));
  12249. });
  12250. return Promise.all(animations);
  12251. },
  12252. set(definition) {
  12253. exports.invariant(hasMounted, "controls.set() should only be called after a component has mounted. Consider calling within a useEffect hook.");
  12254. return subscribers.forEach((visualElement) => {
  12255. setValues(visualElement, definition);
  12256. });
  12257. },
  12258. stop() {
  12259. subscribers.forEach((visualElement) => {
  12260. stopAnimation(visualElement);
  12261. });
  12262. },
  12263. mount() {
  12264. hasMounted = true;
  12265. return () => {
  12266. hasMounted = false;
  12267. controls.stop();
  12268. };
  12269. },
  12270. };
  12271. return controls;
  12272. }
  12273. function useAnimate() {
  12274. const scope = useConstant(() => ({
  12275. current: null, // Will be hydrated by React
  12276. animations: [],
  12277. }));
  12278. const animate = useConstant(() => createScopedAnimate(scope));
  12279. useUnmountEffect(() => {
  12280. scope.animations.forEach((animation) => animation.stop());
  12281. });
  12282. return [scope, animate];
  12283. }
  12284. function useAnimateMini() {
  12285. const scope = useConstant(() => ({
  12286. current: null, // Will be hydrated by React
  12287. animations: [],
  12288. }));
  12289. const animate = useConstant(() => createScopedWaapiAnimate(scope));
  12290. useUnmountEffect(() => {
  12291. scope.animations.forEach((animation) => animation.stop());
  12292. });
  12293. return [scope, animate];
  12294. }
  12295. /**
  12296. * Creates `AnimationControls`, which can be used to manually start, stop
  12297. * and sequence animations on one or more components.
  12298. *
  12299. * The returned `AnimationControls` should be passed to the `animate` property
  12300. * of the components you want to animate.
  12301. *
  12302. * These components can then be animated with the `start` method.
  12303. *
  12304. * ```jsx
  12305. * import * as React from 'react'
  12306. * import { motion, useAnimation } from 'framer-motion'
  12307. *
  12308. * export function MyComponent(props) {
  12309. * const controls = useAnimation()
  12310. *
  12311. * controls.start({
  12312. * x: 100,
  12313. * transition: { duration: 0.5 },
  12314. * })
  12315. *
  12316. * return <motion.div animate={controls} />
  12317. * }
  12318. * ```
  12319. *
  12320. * @returns Animation controller with `start` and `stop` methods
  12321. *
  12322. * @public
  12323. */
  12324. function useAnimationControls() {
  12325. const controls = useConstant(animationControls);
  12326. useIsomorphicLayoutEffect(controls.mount, []);
  12327. return controls;
  12328. }
  12329. const useAnimation = useAnimationControls;
  12330. function usePresenceData() {
  12331. const context = React$1.useContext(PresenceContext);
  12332. return context ? context.custom : undefined;
  12333. }
  12334. /**
  12335. * Attaches an event listener directly to the provided DOM element.
  12336. *
  12337. * Bypassing React's event system can be desirable, for instance when attaching non-passive
  12338. * event handlers.
  12339. *
  12340. * ```jsx
  12341. * const ref = useRef(null)
  12342. *
  12343. * useDomEvent(ref, 'wheel', onWheel, { passive: false })
  12344. *
  12345. * return <div ref={ref} />
  12346. * ```
  12347. *
  12348. * @param ref - React.RefObject that's been provided to the element you want to bind the listener to.
  12349. * @param eventName - Name of the event you want listen for.
  12350. * @param handler - Function to fire when receiving the event.
  12351. * @param options - Options to pass to `Event.addEventListener`.
  12352. *
  12353. * @public
  12354. */
  12355. function useDomEvent(ref, eventName, handler, options) {
  12356. React$1.useEffect(() => {
  12357. const element = ref.current;
  12358. if (handler && element) {
  12359. return addDomEvent(element, eventName, handler, options);
  12360. }
  12361. }, [ref, eventName, handler, options]);
  12362. }
  12363. /**
  12364. * Can manually trigger a drag gesture on one or more `drag`-enabled `motion` components.
  12365. *
  12366. * ```jsx
  12367. * const dragControls = useDragControls()
  12368. *
  12369. * function startDrag(event) {
  12370. * dragControls.start(event, { snapToCursor: true })
  12371. * }
  12372. *
  12373. * return (
  12374. * <>
  12375. * <div onPointerDown={startDrag} />
  12376. * <motion.div drag="x" dragControls={dragControls} />
  12377. * </>
  12378. * )
  12379. * ```
  12380. *
  12381. * @public
  12382. */
  12383. class DragControls {
  12384. constructor() {
  12385. this.componentControls = new Set();
  12386. }
  12387. /**
  12388. * Subscribe a component's internal `VisualElementDragControls` to the user-facing API.
  12389. *
  12390. * @internal
  12391. */
  12392. subscribe(controls) {
  12393. this.componentControls.add(controls);
  12394. return () => this.componentControls.delete(controls);
  12395. }
  12396. /**
  12397. * Start a drag gesture on every `motion` component that has this set of drag controls
  12398. * passed into it via the `dragControls` prop.
  12399. *
  12400. * ```jsx
  12401. * dragControls.start(e, {
  12402. * snapToCursor: true
  12403. * })
  12404. * ```
  12405. *
  12406. * @param event - PointerEvent
  12407. * @param options - Options
  12408. *
  12409. * @public
  12410. */
  12411. start(event, options) {
  12412. this.componentControls.forEach((controls) => {
  12413. controls.start(event.nativeEvent || event, options);
  12414. });
  12415. }
  12416. }
  12417. const createDragControls = () => new DragControls();
  12418. /**
  12419. * Usually, dragging is initiated by pressing down on a `motion` component with a `drag` prop
  12420. * and moving it. For some use-cases, for instance clicking at an arbitrary point on a video scrubber, we
  12421. * might want to initiate that dragging from a different component than the draggable one.
  12422. *
  12423. * By creating a `dragControls` using the `useDragControls` hook, we can pass this into
  12424. * the draggable component's `dragControls` prop. It exposes a `start` method
  12425. * that can start dragging from pointer events on other components.
  12426. *
  12427. * ```jsx
  12428. * const dragControls = useDragControls()
  12429. *
  12430. * function startDrag(event) {
  12431. * dragControls.start(event, { snapToCursor: true })
  12432. * }
  12433. *
  12434. * return (
  12435. * <>
  12436. * <div onPointerDown={startDrag} />
  12437. * <motion.div drag="x" dragControls={dragControls} />
  12438. * </>
  12439. * )
  12440. * ```
  12441. *
  12442. * @public
  12443. */
  12444. function useDragControls() {
  12445. return useConstant(createDragControls);
  12446. }
  12447. /**
  12448. * Checks if a component is a `motion` component.
  12449. */
  12450. function isMotionComponent(component) {
  12451. return (component !== null &&
  12452. typeof component === "object" &&
  12453. motionComponentSymbol in component);
  12454. }
  12455. /**
  12456. * Unwraps a `motion` component and returns either a string for `motion.div` or
  12457. * the React component for `motion(Component)`.
  12458. *
  12459. * If the component is not a `motion` component it returns undefined.
  12460. */
  12461. function unwrapMotionComponent(component) {
  12462. if (isMotionComponent(component)) {
  12463. return component[motionComponentSymbol];
  12464. }
  12465. return undefined;
  12466. }
  12467. function useInstantLayoutTransition() {
  12468. return startTransition;
  12469. }
  12470. function startTransition(callback) {
  12471. if (!rootProjectionNode.current)
  12472. return;
  12473. rootProjectionNode.current.isUpdating = false;
  12474. rootProjectionNode.current.blockUpdate();
  12475. callback && callback();
  12476. }
  12477. function useResetProjection() {
  12478. const reset = React$1.useCallback(() => {
  12479. const root = rootProjectionNode.current;
  12480. if (!root)
  12481. return;
  12482. root.resetTree();
  12483. }, []);
  12484. return reset;
  12485. }
  12486. /**
  12487. * Cycles through a series of visual properties. Can be used to toggle between or cycle through animations. It works similar to `useState` in React. It is provided an initial array of possible states, and returns an array of two arguments.
  12488. *
  12489. * An index value can be passed to the returned `cycle` function to cycle to a specific index.
  12490. *
  12491. * ```jsx
  12492. * import * as React from "react"
  12493. * import { motion, useCycle } from "framer-motion"
  12494. *
  12495. * export const MyComponent = () => {
  12496. * const [x, cycleX] = useCycle(0, 50, 100)
  12497. *
  12498. * return (
  12499. * <motion.div
  12500. * animate={{ x: x }}
  12501. * onTap={() => cycleX()}
  12502. * />
  12503. * )
  12504. * }
  12505. * ```
  12506. *
  12507. * @param items - items to cycle through
  12508. * @returns [currentState, cycleState]
  12509. *
  12510. * @public
  12511. */
  12512. function useCycle(...items) {
  12513. const index = React$1.useRef(0);
  12514. const [item, setItem] = React$1.useState(items[index.current]);
  12515. const runCycle = React$1.useCallback((next) => {
  12516. index.current =
  12517. typeof next !== "number"
  12518. ? wrap(0, items.length, index.current + 1)
  12519. : next;
  12520. setItem(items[index.current]);
  12521. },
  12522. // The array will change on each call, but by putting items.length at
  12523. // the front of this array, we guarantee the dependency comparison will match up
  12524. // eslint-disable-next-line react-hooks/exhaustive-deps
  12525. [items.length, ...items]);
  12526. return [item, runCycle];
  12527. }
  12528. function useInView(ref, { root, margin, amount, once = false, initial = false, } = {}) {
  12529. const [isInView, setInView] = React$1.useState(initial);
  12530. React$1.useEffect(() => {
  12531. if (!ref.current || (once && isInView))
  12532. return;
  12533. const onEnter = () => {
  12534. setInView(true);
  12535. return once ? undefined : () => setInView(false);
  12536. };
  12537. const options = {
  12538. root: (root && root.current) || undefined,
  12539. margin,
  12540. amount,
  12541. };
  12542. return inView(ref.current, onEnter, options);
  12543. }, [root, ref, margin, once, amount]);
  12544. return isInView;
  12545. }
  12546. function useInstantTransition() {
  12547. const [forceUpdate, forcedRenderCount] = useForceUpdate();
  12548. const startInstantLayoutTransition = useInstantLayoutTransition();
  12549. const unlockOnFrameRef = React$1.useRef(-1);
  12550. React$1.useEffect(() => {
  12551. /**
  12552. * Unblock after two animation frames, otherwise this will unblock too soon.
  12553. */
  12554. frame.postRender(() => frame.postRender(() => {
  12555. /**
  12556. * If the callback has been called again after the effect
  12557. * triggered this 2 frame delay, don't unblock animations. This
  12558. * prevents the previous effect from unblocking the current
  12559. * instant transition too soon. This becomes more likely when
  12560. * used in conjunction with React.startTransition().
  12561. */
  12562. if (forcedRenderCount !== unlockOnFrameRef.current)
  12563. return;
  12564. instantAnimationState.current = false;
  12565. }));
  12566. }, [forcedRenderCount]);
  12567. return (callback) => {
  12568. startInstantLayoutTransition(() => {
  12569. instantAnimationState.current = true;
  12570. forceUpdate();
  12571. callback();
  12572. unlockOnFrameRef.current = forcedRenderCount + 1;
  12573. });
  12574. };
  12575. }
  12576. function disableInstantTransitions() {
  12577. instantAnimationState.current = false;
  12578. }
  12579. const appearAnimationStore = new Map();
  12580. const appearComplete = new Map();
  12581. const appearStoreId = (elementId, valueName) => {
  12582. const key = transformProps.has(valueName) ? "transform" : valueName;
  12583. return `${elementId}: ${key}`;
  12584. };
  12585. function handoffOptimizedAppearAnimation(elementId, valueName, frame) {
  12586. const storeId = appearStoreId(elementId, valueName);
  12587. const optimisedAnimation = appearAnimationStore.get(storeId);
  12588. if (!optimisedAnimation) {
  12589. return null;
  12590. }
  12591. const { animation, startTime } = optimisedAnimation;
  12592. function cancelAnimation() {
  12593. window.MotionCancelOptimisedAnimation?.(elementId, valueName, frame);
  12594. }
  12595. /**
  12596. * We can cancel the animation once it's finished now that we've synced
  12597. * with Motion.
  12598. *
  12599. * Prefer onfinish over finished as onfinish is backwards compatible with
  12600. * older browsers.
  12601. */
  12602. animation.onfinish = cancelAnimation;
  12603. if (startTime === null || window.MotionHandoffIsComplete?.(elementId)) {
  12604. /**
  12605. * If the startTime is null, this animation is the Paint Ready detection animation
  12606. * and we can cancel it immediately without handoff.
  12607. *
  12608. * Or if we've already handed off the animation then we're now interrupting it.
  12609. * In which case we need to cancel it.
  12610. */
  12611. cancelAnimation();
  12612. return null;
  12613. }
  12614. else {
  12615. return startTime;
  12616. }
  12617. }
  12618. /**
  12619. * A single time to use across all animations to manually set startTime
  12620. * and ensure they're all in sync.
  12621. */
  12622. let startFrameTime;
  12623. /**
  12624. * A dummy animation to detect when Chrome is ready to start
  12625. * painting the page and hold off from triggering the real animation
  12626. * until then. We only need one animation to detect paint ready.
  12627. *
  12628. * https://bugs.chromium.org/p/chromium/issues/detail?id=1406850
  12629. */
  12630. let readyAnimation;
  12631. /**
  12632. * Keep track of animations that were suspended vs cancelled so we
  12633. * can easily resume them when we're done measuring layout.
  12634. */
  12635. const suspendedAnimations = new Set();
  12636. function resumeSuspendedAnimations() {
  12637. suspendedAnimations.forEach((data) => {
  12638. data.animation.play();
  12639. data.animation.startTime = data.startTime;
  12640. });
  12641. suspendedAnimations.clear();
  12642. }
  12643. function startOptimizedAppearAnimation(element, name, keyframes, options, onReady) {
  12644. // Prevent optimised appear animations if Motion has already started animating.
  12645. if (window.MotionIsMounted) {
  12646. return;
  12647. }
  12648. const id = element.dataset[optimizedAppearDataId];
  12649. if (!id)
  12650. return;
  12651. window.MotionHandoffAnimation = handoffOptimizedAppearAnimation;
  12652. const storeId = appearStoreId(id, name);
  12653. if (!readyAnimation) {
  12654. readyAnimation = startWaapiAnimation(element, name, [keyframes[0], keyframes[0]],
  12655. /**
  12656. * 10 secs is basically just a super-safe duration to give Chrome
  12657. * long enough to get the animation ready.
  12658. */
  12659. { duration: 10000, ease: "linear" });
  12660. appearAnimationStore.set(storeId, {
  12661. animation: readyAnimation,
  12662. startTime: null,
  12663. });
  12664. /**
  12665. * If there's no readyAnimation then there's been no instantiation
  12666. * of handoff animations.
  12667. */
  12668. window.MotionHandoffAnimation = handoffOptimizedAppearAnimation;
  12669. window.MotionHasOptimisedAnimation = (elementId, valueName) => {
  12670. if (!elementId)
  12671. return false;
  12672. /**
  12673. * Keep a map of elementIds that have started animating. We check
  12674. * via ID instead of Element because of hydration errors and
  12675. * pre-hydration checks. We also actively record IDs as they start
  12676. * animating rather than simply checking for data-appear-id as
  12677. * this attrbute might be present but not lead to an animation, for
  12678. * instance if the element's appear animation is on a different
  12679. * breakpoint.
  12680. */
  12681. if (!valueName) {
  12682. return appearComplete.has(elementId);
  12683. }
  12684. const animationId = appearStoreId(elementId, valueName);
  12685. return Boolean(appearAnimationStore.get(animationId));
  12686. };
  12687. window.MotionHandoffMarkAsComplete = (elementId) => {
  12688. if (appearComplete.has(elementId)) {
  12689. appearComplete.set(elementId, true);
  12690. }
  12691. };
  12692. window.MotionHandoffIsComplete = (elementId) => {
  12693. return appearComplete.get(elementId) === true;
  12694. };
  12695. /**
  12696. * We only need to cancel transform animations as
  12697. * they're the ones that will interfere with the
  12698. * layout animation measurements.
  12699. */
  12700. window.MotionCancelOptimisedAnimation = (elementId, valueName, frame, canResume) => {
  12701. const animationId = appearStoreId(elementId, valueName);
  12702. const data = appearAnimationStore.get(animationId);
  12703. if (!data)
  12704. return;
  12705. if (frame && canResume === undefined) {
  12706. /**
  12707. * Wait until the end of the subsequent frame to cancel the animation
  12708. * to ensure we don't remove the animation before the main thread has
  12709. * had a chance to resolve keyframes and render.
  12710. */
  12711. frame.postRender(() => {
  12712. frame.postRender(() => {
  12713. data.animation.cancel();
  12714. });
  12715. });
  12716. }
  12717. else {
  12718. data.animation.cancel();
  12719. }
  12720. if (frame && canResume) {
  12721. suspendedAnimations.add(data);
  12722. frame.render(resumeSuspendedAnimations);
  12723. }
  12724. else {
  12725. appearAnimationStore.delete(animationId);
  12726. /**
  12727. * If there are no more animations left, we can remove the cancel function.
  12728. * This will let us know when we can stop checking for conflicting layout animations.
  12729. */
  12730. if (!appearAnimationStore.size) {
  12731. window.MotionCancelOptimisedAnimation = undefined;
  12732. }
  12733. }
  12734. };
  12735. window.MotionCheckAppearSync = (visualElement, valueName, value) => {
  12736. const appearId = getOptimisedAppearId(visualElement);
  12737. if (!appearId)
  12738. return;
  12739. const valueIsOptimised = window.MotionHasOptimisedAnimation?.(appearId, valueName);
  12740. const externalAnimationValue = visualElement.props.values?.[valueName];
  12741. if (!valueIsOptimised || !externalAnimationValue)
  12742. return;
  12743. const removeSyncCheck = value.on("change", (latestValue) => {
  12744. if (externalAnimationValue.get() !== latestValue) {
  12745. window.MotionCancelOptimisedAnimation?.(appearId, valueName);
  12746. removeSyncCheck();
  12747. }
  12748. });
  12749. return removeSyncCheck;
  12750. };
  12751. }
  12752. const startAnimation = () => {
  12753. readyAnimation.cancel();
  12754. const appearAnimation = startWaapiAnimation(element, name, keyframes, options);
  12755. /**
  12756. * Record the time of the first started animation. We call performance.now() once
  12757. * here and once in handoff to ensure we're getting
  12758. * close to a frame-locked time. This keeps all animations in sync.
  12759. */
  12760. if (startFrameTime === undefined) {
  12761. startFrameTime = performance.now();
  12762. }
  12763. appearAnimation.startTime = startFrameTime;
  12764. appearAnimationStore.set(storeId, {
  12765. animation: appearAnimation,
  12766. startTime: startFrameTime,
  12767. });
  12768. if (onReady)
  12769. onReady(appearAnimation);
  12770. };
  12771. appearComplete.set(id, false);
  12772. if (readyAnimation.ready) {
  12773. readyAnimation.ready.then(startAnimation).catch(noop);
  12774. }
  12775. else {
  12776. startAnimation();
  12777. }
  12778. }
  12779. const createObject = () => ({});
  12780. class StateVisualElement extends VisualElement {
  12781. constructor() {
  12782. super(...arguments);
  12783. this.measureInstanceViewportBox = createBox;
  12784. }
  12785. build() { }
  12786. resetTransform() { }
  12787. restoreTransform() { }
  12788. removeValueFromRenderState() { }
  12789. renderInstance() { }
  12790. scrapeMotionValuesFromProps() {
  12791. return createObject();
  12792. }
  12793. getBaseTargetFromProps() {
  12794. return undefined;
  12795. }
  12796. readValueFromInstance(_state, key, options) {
  12797. return options.initialState[key] || 0;
  12798. }
  12799. sortInstanceNodePosition() {
  12800. return 0;
  12801. }
  12802. }
  12803. const useVisualState = makeUseVisualState({
  12804. scrapeMotionValuesFromProps: createObject,
  12805. createRenderState: createObject,
  12806. });
  12807. /**
  12808. * This is not an officially supported API and may be removed
  12809. * on any version.
  12810. */
  12811. function useAnimatedState(initialState) {
  12812. const [animationState, setAnimationState] = React$1.useState(initialState);
  12813. const visualState = useVisualState({}, false);
  12814. const element = useConstant(() => {
  12815. return new StateVisualElement({
  12816. props: {
  12817. onUpdate: (v) => {
  12818. setAnimationState({ ...v });
  12819. },
  12820. },
  12821. visualState,
  12822. presenceContext: null,
  12823. }, { initialState });
  12824. });
  12825. React$1.useLayoutEffect(() => {
  12826. element.mount({});
  12827. return () => element.unmount();
  12828. }, [element]);
  12829. const startAnimation = useConstant(() => (animationDefinition) => {
  12830. return animateVisualElement(element, animationDefinition);
  12831. });
  12832. return [animationState, startAnimation];
  12833. }
  12834. let id = 0;
  12835. const AnimateSharedLayout = ({ children }) => {
  12836. React__namespace.useEffect(() => {
  12837. exports.invariant(false, "AnimateSharedLayout is deprecated: https://www.framer.com/docs/guide-upgrade/##shared-layout-animations");
  12838. }, []);
  12839. return (jsx(LayoutGroup, { id: useConstant(() => `asl-${id++}`), children: children }));
  12840. };
  12841. // Keep things reasonable and avoid scale: Infinity. In practise we might need
  12842. // to add another value, opacity, that could interpolate scaleX/Y [0,0.01] => [0,1]
  12843. // to simply hide content at unreasonable scales.
  12844. const maxScale = 100000;
  12845. const invertScale = (scale) => scale > 0.001 ? 1 / scale : maxScale;
  12846. let hasWarned = false;
  12847. /**
  12848. * Returns a `MotionValue` each for `scaleX` and `scaleY` that update with the inverse
  12849. * of their respective parent scales.
  12850. *
  12851. * This is useful for undoing the distortion of content when scaling a parent component.
  12852. *
  12853. * By default, `useInvertedScale` will automatically fetch `scaleX` and `scaleY` from the nearest parent.
  12854. * By passing other `MotionValue`s in as `useInvertedScale({ scaleX, scaleY })`, it will invert the output
  12855. * of those instead.
  12856. *
  12857. * ```jsx
  12858. * const MyComponent = () => {
  12859. * const { scaleX, scaleY } = useInvertedScale()
  12860. * return <motion.div style={{ scaleX, scaleY }} />
  12861. * }
  12862. * ```
  12863. *
  12864. * @deprecated
  12865. */
  12866. function useInvertedScale(scale) {
  12867. let parentScaleX = useMotionValue(1);
  12868. let parentScaleY = useMotionValue(1);
  12869. const { visualElement } = React$1.useContext(MotionContext);
  12870. exports.invariant(!!(scale || visualElement), "If no scale values are provided, useInvertedScale must be used within a child of another motion component.");
  12871. warning(hasWarned, "useInvertedScale is deprecated and will be removed in 3.0. Use the layout prop instead.");
  12872. hasWarned = true;
  12873. if (scale) {
  12874. parentScaleX = scale.scaleX || parentScaleX;
  12875. parentScaleY = scale.scaleY || parentScaleY;
  12876. }
  12877. else if (visualElement) {
  12878. parentScaleX = visualElement.getValue("scaleX", 1);
  12879. parentScaleY = visualElement.getValue("scaleY", 1);
  12880. }
  12881. const scaleX = useTransform(parentScaleX, invertScale);
  12882. const scaleY = useTransform(parentScaleY, invertScale);
  12883. return { scaleX, scaleY };
  12884. }
  12885. exports.AcceleratedAnimation = AcceleratedAnimation;
  12886. exports.AnimatePresence = AnimatePresence;
  12887. exports.AnimateSharedLayout = AnimateSharedLayout;
  12888. exports.DeprecatedLayoutGroupContext = DeprecatedLayoutGroupContext;
  12889. exports.DragControls = DragControls;
  12890. exports.FlatTree = FlatTree;
  12891. exports.LayoutGroup = LayoutGroup;
  12892. exports.LayoutGroupContext = LayoutGroupContext;
  12893. exports.LazyMotion = LazyMotion;
  12894. exports.MotionConfig = MotionConfig;
  12895. exports.MotionConfigContext = MotionConfigContext;
  12896. exports.MotionContext = MotionContext;
  12897. exports.MotionGlobalConfig = MotionGlobalConfig;
  12898. exports.MotionValue = MotionValue;
  12899. exports.PresenceContext = PresenceContext;
  12900. exports.Reorder = namespace;
  12901. exports.SwitchLayoutGroupContext = SwitchLayoutGroupContext;
  12902. exports.VisualElement = VisualElement;
  12903. exports.WillChangeMotionValue = WillChangeMotionValue;
  12904. exports.addPointerEvent = addPointerEvent;
  12905. exports.addPointerInfo = addPointerInfo;
  12906. exports.addScaleCorrector = addScaleCorrector;
  12907. exports.animate = animate;
  12908. exports.animateMini = animateMini;
  12909. exports.animateValue = animateValue;
  12910. exports.animateVisualElement = animateVisualElement;
  12911. exports.animationControls = animationControls;
  12912. exports.animations = animations;
  12913. exports.anticipate = anticipate;
  12914. exports.backIn = backIn;
  12915. exports.backInOut = backInOut;
  12916. exports.backOut = backOut;
  12917. exports.buildTransform = buildTransform;
  12918. exports.calcLength = calcLength;
  12919. exports.cancelFrame = cancelFrame;
  12920. exports.cancelSync = cancelSync;
  12921. exports.circIn = circIn;
  12922. exports.circInOut = circInOut;
  12923. exports.circOut = circOut;
  12924. exports.clamp = clamp;
  12925. exports.color = color;
  12926. exports.complex = complex;
  12927. exports.createBox = createBox;
  12928. exports.createRendererMotionComponent = createRendererMotionComponent;
  12929. exports.createScopedAnimate = createScopedAnimate;
  12930. exports.cubicBezier = cubicBezier;
  12931. exports.delay = delay;
  12932. exports.disableInstantTransitions = disableInstantTransitions;
  12933. exports.distance = distance;
  12934. exports.distance2D = distance2D;
  12935. exports.domAnimation = domAnimation;
  12936. exports.domMax = domMax;
  12937. exports.domMin = domMin;
  12938. exports.easeIn = easeIn;
  12939. exports.easeInOut = easeInOut;
  12940. exports.easeOut = easeOut;
  12941. exports.filterProps = filterProps;
  12942. exports.findSpring = findSpring;
  12943. exports.frame = frame;
  12944. exports.frameData = frameData;
  12945. exports.hover = hover;
  12946. exports.inView = inView;
  12947. exports.inertia = inertia;
  12948. exports.interpolate = interpolate;
  12949. exports.isBrowser = isBrowser;
  12950. exports.isDragActive = isDragActive;
  12951. exports.isMotionComponent = isMotionComponent;
  12952. exports.isMotionValue = isMotionValue;
  12953. exports.isValidMotionProp = isValidMotionProp;
  12954. exports.keyframes = keyframes;
  12955. exports.m = m;
  12956. exports.makeUseVisualState = makeUseVisualState;
  12957. exports.mirrorEasing = mirrorEasing;
  12958. exports.mix = mix;
  12959. exports.motion = motion;
  12960. exports.motionValue = motionValue;
  12961. exports.noop = noop;
  12962. exports.optimizedAppearDataAttribute = optimizedAppearDataAttribute;
  12963. exports.pipe = pipe;
  12964. exports.press = press;
  12965. exports.progress = progress;
  12966. exports.px = px;
  12967. exports.resolveMotionValue = resolveMotionValue;
  12968. exports.reverseEasing = reverseEasing;
  12969. exports.scroll = scroll;
  12970. exports.scrollInfo = scrollInfo;
  12971. exports.spring = spring;
  12972. exports.stagger = stagger;
  12973. exports.startOptimizedAppearAnimation = startOptimizedAppearAnimation;
  12974. exports.steps = steps;
  12975. exports.sync = sync;
  12976. exports.time = time;
  12977. exports.transform = transform;
  12978. exports.unwrapMotionComponent = unwrapMotionComponent;
  12979. exports.useAnimate = useAnimate;
  12980. exports.useAnimateMini = useAnimateMini;
  12981. exports.useAnimation = useAnimation;
  12982. exports.useAnimationControls = useAnimationControls;
  12983. exports.useAnimationFrame = useAnimationFrame;
  12984. exports.useCycle = useCycle;
  12985. exports.useDeprecatedAnimatedState = useAnimatedState;
  12986. exports.useDeprecatedInvertedScale = useInvertedScale;
  12987. exports.useDomEvent = useDomEvent;
  12988. exports.useDragControls = useDragControls;
  12989. exports.useElementScroll = useElementScroll;
  12990. exports.useForceUpdate = useForceUpdate;
  12991. exports.useInView = useInView;
  12992. exports.useInstantLayoutTransition = useInstantLayoutTransition;
  12993. exports.useInstantTransition = useInstantTransition;
  12994. exports.useIsPresent = useIsPresent;
  12995. exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect;
  12996. exports.useMotionTemplate = useMotionTemplate;
  12997. exports.useMotionValue = useMotionValue;
  12998. exports.useMotionValueEvent = useMotionValueEvent;
  12999. exports.usePresence = usePresence;
  13000. exports.usePresenceData = usePresenceData;
  13001. exports.useReducedMotion = useReducedMotion;
  13002. exports.useReducedMotionConfig = useReducedMotionConfig;
  13003. exports.useResetProjection = useResetProjection;
  13004. exports.useScroll = useScroll;
  13005. exports.useSpring = useSpring;
  13006. exports.useTime = useTime;
  13007. exports.useTransform = useTransform;
  13008. exports.useUnmountEffect = useUnmountEffect;
  13009. exports.useVelocity = useVelocity;
  13010. exports.useViewportScroll = useViewportScroll;
  13011. exports.useWillChange = useWillChange;
  13012. exports.visualElementStore = visualElementStore;
  13013. exports.wrap = wrap;
  13014. }));