| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121 |
- 'use strict';
- Object.defineProperty(exports, '__esModule', { value: true });
- var jsxRuntime = require('react/jsx-runtime');
- var React = require('react');
- var create = require('./create-DwAwaNot.js');
- var motionDom = require('motion-dom');
- var motionUtils = require('motion-utils');
- function _interopNamespaceDefault(e) {
- var n = Object.create(null);
- if (e) {
- Object.keys(e).forEach(function (k) {
- if (k !== 'default') {
- var d = Object.getOwnPropertyDescriptor(e, k);
- Object.defineProperty(n, k, d.get ? d : {
- enumerable: true,
- get: function () { return e[k]; }
- });
- }
- });
- }
- n.default = e;
- return Object.freeze(n);
- }
- var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
- /**
- * Measurement functionality has to be within a separate component
- * to leverage snapshot lifecycle.
- */
- class PopChildMeasure extends React__namespace.Component {
- getSnapshotBeforeUpdate(prevProps) {
- const element = this.props.childRef.current;
- if (element && prevProps.isPresent && !this.props.isPresent) {
- const parent = element.offsetParent;
- const parentWidth = parent instanceof HTMLElement ? parent.offsetWidth || 0 : 0;
- const size = this.props.sizeRef.current;
- size.height = element.offsetHeight || 0;
- size.width = element.offsetWidth || 0;
- size.top = element.offsetTop;
- size.left = element.offsetLeft;
- size.right = parentWidth - size.width - size.left;
- }
- return null;
- }
- /**
- * Required with getSnapshotBeforeUpdate to stop React complaining.
- */
- componentDidUpdate() { }
- render() {
- return this.props.children;
- }
- }
- function PopChild({ children, isPresent, anchorX }) {
- const id = React.useId();
- const ref = React.useRef(null);
- const size = React.useRef({
- width: 0,
- height: 0,
- top: 0,
- left: 0,
- right: 0,
- });
- const { nonce } = React.useContext(create.MotionConfigContext);
- /**
- * We create and inject a style block so we can apply this explicit
- * sizing in a non-destructive manner by just deleting the style block.
- *
- * We can't apply size via render as the measurement happens
- * in getSnapshotBeforeUpdate (post-render), likewise if we apply the
- * styles directly on the DOM node, we might be overwriting
- * styles set via the style prop.
- */
- React.useInsertionEffect(() => {
- const { width, height, top, left, right } = size.current;
- if (isPresent || !ref.current || !width || !height)
- return;
- const x = anchorX === "left" ? `left: ${left}` : `right: ${right}`;
- ref.current.dataset.motionPopId = id;
- const style = document.createElement("style");
- if (nonce)
- style.nonce = nonce;
- document.head.appendChild(style);
- if (style.sheet) {
- style.sheet.insertRule(`
- [data-motion-pop-id="${id}"] {
- position: absolute !important;
- width: ${width}px !important;
- height: ${height}px !important;
- ${x}px !important;
- top: ${top}px !important;
- }
- `);
- }
- return () => {
- document.head.removeChild(style);
- };
- }, [isPresent]);
- return (jsxRuntime.jsx(PopChildMeasure, { isPresent: isPresent, childRef: ref, sizeRef: size, children: React__namespace.cloneElement(children, { ref }) }));
- }
- const PresenceChild = ({ children, initial, isPresent, onExitComplete, custom, presenceAffectsLayout, mode, anchorX, }) => {
- const presenceChildren = create.useConstant(newChildrenMap);
- const id = React.useId();
- const memoizedOnExitComplete = React.useCallback((childId) => {
- presenceChildren.set(childId, true);
- for (const isComplete of presenceChildren.values()) {
- if (!isComplete)
- return; // can stop searching when any is incomplete
- }
- onExitComplete && onExitComplete();
- }, [presenceChildren, onExitComplete]);
- const context = React.useMemo(() => ({
- id,
- initial,
- isPresent,
- custom,
- onExitComplete: memoizedOnExitComplete,
- register: (childId) => {
- presenceChildren.set(childId, false);
- return () => presenceChildren.delete(childId);
- },
- }),
- /**
- * If the presence of a child affects the layout of the components around it,
- * we want to make a new context value to ensure they get re-rendered
- * so they can detect that layout change.
- */
- presenceAffectsLayout
- ? [Math.random(), memoizedOnExitComplete]
- : [isPresent, memoizedOnExitComplete]);
- React.useMemo(() => {
- presenceChildren.forEach((_, key) => presenceChildren.set(key, false));
- }, [isPresent]);
- /**
- * If there's no `motion` components to fire exit animations, we want to remove this
- * component immediately.
- */
- React__namespace.useEffect(() => {
- !isPresent &&
- !presenceChildren.size &&
- onExitComplete &&
- onExitComplete();
- }, [isPresent]);
- if (mode === "popLayout") {
- children = (jsxRuntime.jsx(PopChild, { isPresent: isPresent, anchorX: anchorX, children: children }));
- }
- return (jsxRuntime.jsx(create.PresenceContext.Provider, { value: context, children: children }));
- };
- function newChildrenMap() {
- return new Map();
- }
- const getChildKey = (child) => child.key || "";
- function onlyElements(children) {
- const filtered = [];
- // We use forEach here instead of map as map mutates the component key by preprending `.$`
- React.Children.forEach(children, (child) => {
- if (React.isValidElement(child))
- filtered.push(child);
- });
- return filtered;
- }
- /**
- * `AnimatePresence` enables the animation of components that have been removed from the tree.
- *
- * When adding/removing more than a single child, every child **must** be given a unique `key` prop.
- *
- * Any `motion` components that have an `exit` property defined will animate out when removed from
- * the tree.
- *
- * ```jsx
- * import { motion, AnimatePresence } from 'framer-motion'
- *
- * export const Items = ({ items }) => (
- * <AnimatePresence>
- * {items.map(item => (
- * <motion.div
- * key={item.id}
- * initial={{ opacity: 0 }}
- * animate={{ opacity: 1 }}
- * exit={{ opacity: 0 }}
- * />
- * ))}
- * </AnimatePresence>
- * )
- * ```
- *
- * You can sequence exit animations throughout a tree using variants.
- *
- * If a child contains multiple `motion` components with `exit` props, it will only unmount the child
- * once all `motion` components have finished animating out. Likewise, any components using
- * `usePresence` all need to call `safeToRemove`.
- *
- * @public
- */
- const AnimatePresence = ({ children, custom, initial = true, onExitComplete, presenceAffectsLayout = true, mode = "sync", propagate = false, anchorX = "left", }) => {
- const [isParentPresent, safeToRemove] = create.usePresence(propagate);
- /**
- * Filter any children that aren't ReactElements. We can only track components
- * between renders with a props.key.
- */
- const presentChildren = React.useMemo(() => onlyElements(children), [children]);
- /**
- * Track the keys of the currently rendered children. This is used to
- * determine which children are exiting.
- */
- const presentKeys = propagate && !isParentPresent ? [] : presentChildren.map(getChildKey);
- /**
- * If `initial={false}` we only want to pass this to components in the first render.
- */
- const isInitialRender = React.useRef(true);
- /**
- * A ref containing the currently present children. When all exit animations
- * are complete, we use this to re-render the component with the latest children
- * *committed* rather than the latest children *rendered*.
- */
- const pendingPresentChildren = React.useRef(presentChildren);
- /**
- * Track which exiting children have finished animating out.
- */
- const exitComplete = create.useConstant(() => new Map());
- /**
- * Save children to render as React state. To ensure this component is concurrent-safe,
- * we check for exiting children via an effect.
- */
- const [diffedChildren, setDiffedChildren] = React.useState(presentChildren);
- const [renderedChildren, setRenderedChildren] = React.useState(presentChildren);
- create.useIsomorphicLayoutEffect(() => {
- isInitialRender.current = false;
- pendingPresentChildren.current = presentChildren;
- /**
- * Update complete status of exiting children.
- */
- for (let i = 0; i < renderedChildren.length; i++) {
- const key = getChildKey(renderedChildren[i]);
- if (!presentKeys.includes(key)) {
- if (exitComplete.get(key) !== true) {
- exitComplete.set(key, false);
- }
- }
- else {
- exitComplete.delete(key);
- }
- }
- }, [renderedChildren, presentKeys.length, presentKeys.join("-")]);
- const exitingChildren = [];
- if (presentChildren !== diffedChildren) {
- let nextChildren = [...presentChildren];
- /**
- * Loop through all the currently rendered components and decide which
- * are exiting.
- */
- for (let i = 0; i < renderedChildren.length; i++) {
- const child = renderedChildren[i];
- const key = getChildKey(child);
- if (!presentKeys.includes(key)) {
- nextChildren.splice(i, 0, child);
- exitingChildren.push(child);
- }
- }
- /**
- * If we're in "wait" mode, and we have exiting children, we want to
- * only render these until they've all exited.
- */
- if (mode === "wait" && exitingChildren.length) {
- nextChildren = exitingChildren;
- }
- setRenderedChildren(onlyElements(nextChildren));
- setDiffedChildren(presentChildren);
- /**
- * Early return to ensure once we've set state with the latest diffed
- * children, we can immediately re-render.
- */
- return null;
- }
- if (process.env.NODE_ENV !== "production" &&
- mode === "wait" &&
- renderedChildren.length > 1) {
- 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.`);
- }
- /**
- * If we've been provided a forceRender function by the LayoutGroupContext,
- * we can use it to force a re-render amongst all surrounding components once
- * all components have finished animating out.
- */
- const { forceRender } = React.useContext(create.LayoutGroupContext);
- return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderedChildren.map((child) => {
- const key = getChildKey(child);
- const isPresent = propagate && !isParentPresent
- ? false
- : presentChildren === renderedChildren ||
- presentKeys.includes(key);
- const onExit = () => {
- if (exitComplete.has(key)) {
- exitComplete.set(key, true);
- }
- else {
- return;
- }
- let isEveryExitComplete = true;
- exitComplete.forEach((isExitComplete) => {
- if (!isExitComplete)
- isEveryExitComplete = false;
- });
- if (isEveryExitComplete) {
- forceRender?.();
- setRenderedChildren(pendingPresentChildren.current);
- propagate && safeToRemove?.();
- onExitComplete && onExitComplete();
- }
- };
- return (jsxRuntime.jsx(PresenceChild, { isPresent: isPresent, initial: !isInitialRender.current || initial
- ? undefined
- : false, custom: custom, presenceAffectsLayout: presenceAffectsLayout, mode: mode, onExitComplete: isPresent ? undefined : onExit, anchorX: anchorX, children: child }, key));
- }) }));
- };
- /**
- * Note: Still used by components generated by old versions of Framer
- *
- * @deprecated
- */
- const DeprecatedLayoutGroupContext = React.createContext(null);
- const notify = (node) => !node.isLayoutDirty && node.willUpdate(false);
- function nodeGroup() {
- const nodes = new Set();
- const subscriptions = new WeakMap();
- const dirtyAll = () => nodes.forEach(notify);
- return {
- add: (node) => {
- nodes.add(node);
- subscriptions.set(node, node.addEventListener("willUpdate", dirtyAll));
- },
- remove: (node) => {
- nodes.delete(node);
- const unsubscribe = subscriptions.get(node);
- if (unsubscribe) {
- unsubscribe();
- subscriptions.delete(node);
- }
- dirtyAll();
- },
- dirty: dirtyAll,
- };
- }
- function useIsMounted() {
- const isMounted = React.useRef(false);
- create.useIsomorphicLayoutEffect(() => {
- isMounted.current = true;
- return () => {
- isMounted.current = false;
- };
- }, []);
- return isMounted;
- }
- function useForceUpdate() {
- const isMounted = useIsMounted();
- const [forcedRenderCount, setForcedRenderCount] = React.useState(0);
- const forceRender = React.useCallback(() => {
- isMounted.current && setForcedRenderCount(forcedRenderCount + 1);
- }, [forcedRenderCount]);
- /**
- * Defer this to the end of the next animation frame in case there are multiple
- * synchronous calls.
- */
- const deferredForceRender = React.useCallback(() => motionDom.frame.postRender(forceRender), [forceRender]);
- return [deferredForceRender, forcedRenderCount];
- }
- const shouldInheritGroup = (inherit) => inherit === true;
- const shouldInheritId = (inherit) => shouldInheritGroup(inherit === true) || inherit === "id";
- const LayoutGroup = ({ children, id, inherit = true }) => {
- const layoutGroupContext = React.useContext(create.LayoutGroupContext);
- const deprecatedLayoutGroupContext = React.useContext(DeprecatedLayoutGroupContext);
- const [forceRender, key] = useForceUpdate();
- const context = React.useRef(null);
- const upstreamId = layoutGroupContext.id || deprecatedLayoutGroupContext;
- if (context.current === null) {
- if (shouldInheritId(inherit) && upstreamId) {
- id = id ? upstreamId + "-" + id : upstreamId;
- }
- context.current = {
- id,
- group: shouldInheritGroup(inherit)
- ? layoutGroupContext.group || nodeGroup()
- : nodeGroup(),
- };
- }
- const memoizedContext = React.useMemo(() => ({ ...context.current, forceRender }), [key]);
- return (jsxRuntime.jsx(create.LayoutGroupContext.Provider, { value: memoizedContext, children: children }));
- };
- /**
- * Used in conjunction with the `m` component to reduce bundle size.
- *
- * `m` is a version of the `motion` component that only loads functionality
- * critical for the initial render.
- *
- * `LazyMotion` can then be used to either synchronously or asynchronously
- * load animation and gesture support.
- *
- * ```jsx
- * // Synchronous loading
- * import { LazyMotion, m, domAnimation } from "framer-motion"
- *
- * function App() {
- * return (
- * <LazyMotion features={domAnimation}>
- * <m.div animate={{ scale: 2 }} />
- * </LazyMotion>
- * )
- * }
- *
- * // Asynchronous loading
- * import { LazyMotion, m } from "framer-motion"
- *
- * function App() {
- * return (
- * <LazyMotion features={() => import('./path/to/domAnimation')}>
- * <m.div animate={{ scale: 2 }} />
- * </LazyMotion>
- * )
- * }
- * ```
- *
- * @public
- */
- function LazyMotion({ children, features, strict = false }) {
- const [, setIsLoaded] = React.useState(!isLazyBundle(features));
- const loadedRenderer = React.useRef(undefined);
- /**
- * If this is a synchronous load, load features immediately
- */
- if (!isLazyBundle(features)) {
- const { renderer, ...loadedFeatures } = features;
- loadedRenderer.current = renderer;
- create.loadFeatures(loadedFeatures);
- }
- React.useEffect(() => {
- if (isLazyBundle(features)) {
- features().then(({ renderer, ...loadedFeatures }) => {
- create.loadFeatures(loadedFeatures);
- loadedRenderer.current = renderer;
- setIsLoaded(true);
- });
- }
- }, []);
- return (jsxRuntime.jsx(create.LazyContext.Provider, { value: { renderer: loadedRenderer.current, strict }, children: children }));
- }
- function isLazyBundle(features) {
- return typeof features === "function";
- }
- /**
- * `MotionConfig` is used to set configuration options for all children `motion` components.
- *
- * ```jsx
- * import { motion, MotionConfig } from "framer-motion"
- *
- * export function App() {
- * return (
- * <MotionConfig transition={{ type: "spring" }}>
- * <motion.div animate={{ x: 100 }} />
- * </MotionConfig>
- * )
- * }
- * ```
- *
- * @public
- */
- function MotionConfig({ children, isValidProp, ...config }) {
- isValidProp && create.loadExternalIsValidProp(isValidProp);
- /**
- * Inherit props from any parent MotionConfig components
- */
- config = { ...React.useContext(create.MotionConfigContext), ...config };
- /**
- * Don't allow isStatic to change between renders as it affects how many hooks
- * motion components fire.
- */
- config.isStatic = create.useConstant(() => config.isStatic);
- /**
- * Creating a new config context object will re-render every `motion` component
- * every time it renders. So we only want to create a new one sparingly.
- */
- const context = React.useMemo(() => config, [
- JSON.stringify(config.transition),
- config.transformPagePoint,
- config.reducedMotion,
- ]);
- return (jsxRuntime.jsx(create.MotionConfigContext.Provider, { value: context, children: children }));
- }
- const ReorderContext = React.createContext(null);
- function createDOMMotionComponentProxy(componentFactory) {
- if (typeof Proxy === "undefined") {
- return componentFactory;
- }
- /**
- * A cache of generated `motion` components, e.g `motion.div`, `motion.input` etc.
- * Rather than generating them anew every render.
- */
- const componentCache = new Map();
- const deprecatedFactoryFunction = (...args) => {
- if (process.env.NODE_ENV !== "production") {
- motionUtils.warnOnce(false, "motion() is deprecated. Use motion.create() instead.");
- }
- return componentFactory(...args);
- };
- return new Proxy(deprecatedFactoryFunction, {
- /**
- * Called when `motion` is referenced with a prop: `motion.div`, `motion.input` etc.
- * The prop name is passed through as `key` and we can use that to generate a `motion`
- * DOM component with that name.
- */
- get: (_target, key) => {
- if (key === "create")
- return componentFactory;
- /**
- * If this element doesn't exist in the component cache, create it and cache.
- */
- if (!componentCache.has(key)) {
- componentCache.set(key, componentFactory(key));
- }
- return componentCache.get(key);
- },
- });
- }
- const motion = /*@__PURE__*/ createDOMMotionComponentProxy(create.createMotionComponent);
- function checkReorder(order, value, offset, velocity) {
- if (!velocity)
- return order;
- const index = order.findIndex((item) => item.value === value);
- if (index === -1)
- return order;
- const nextOffset = velocity > 0 ? 1 : -1;
- const nextItem = order[index + nextOffset];
- if (!nextItem)
- return order;
- const item = order[index];
- const nextLayout = nextItem.layout;
- const nextItemCenter = create.mixNumber(nextLayout.min, nextLayout.max, 0.5);
- if ((nextOffset === 1 && item.layout.max + offset > nextItemCenter) ||
- (nextOffset === -1 && item.layout.min + offset < nextItemCenter)) {
- return motionUtils.moveItem(order, index, index + nextOffset);
- }
- return order;
- }
- function ReorderGroupComponent({ children, as = "ul", axis = "y", onReorder, values, ...props }, externalRef) {
- const Component = create.useConstant(() => motion[as]);
- const order = [];
- const isReordering = React.useRef(false);
- motionUtils.invariant(Boolean(values), "Reorder.Group must be provided a values prop");
- const context = {
- axis,
- registerItem: (value, layout) => {
- // If the entry was already added, update it rather than adding it again
- const idx = order.findIndex((entry) => value === entry.value);
- if (idx !== -1) {
- order[idx].layout = layout[axis];
- }
- else {
- order.push({ value: value, layout: layout[axis] });
- }
- order.sort(compareMin);
- },
- updateOrder: (item, offset, velocity) => {
- if (isReordering.current)
- return;
- const newOrder = checkReorder(order, item, offset, velocity);
- if (order !== newOrder) {
- isReordering.current = true;
- onReorder(newOrder
- .map(getValue)
- .filter((value) => values.indexOf(value) !== -1));
- }
- },
- };
- React.useEffect(() => {
- isReordering.current = false;
- });
- return (jsxRuntime.jsx(Component, { ...props, ref: externalRef, ignoreStrict: true, children: jsxRuntime.jsx(ReorderContext.Provider, { value: context, children: children }) }));
- }
- const ReorderGroup = /*@__PURE__*/ React.forwardRef(ReorderGroupComponent);
- function getValue(item) {
- return item.value;
- }
- function compareMin(a, b) {
- return a.layout.min - b.layout.min;
- }
- /**
- * Creates a `MotionValue` to track the state and velocity of a value.
- *
- * 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.
- *
- * ```jsx
- * export const MyComponent = () => {
- * const scale = useMotionValue(1)
- *
- * return <motion.div style={{ scale }} />
- * }
- * ```
- *
- * @param initial - The initial state.
- *
- * @public
- */
- function useMotionValue(initial) {
- const value = create.useConstant(() => motionDom.motionValue(initial));
- /**
- * If this motion value is being used in static mode, like on
- * the Framer canvas, force components to rerender when the motion
- * value is updated.
- */
- const { isStatic } = React.useContext(create.MotionConfigContext);
- if (isStatic) {
- const [, setLatest] = React.useState(initial);
- React.useEffect(() => value.on("change", setLatest), []);
- }
- return value;
- }
- const isCustomValueType = (v) => {
- return v && typeof v === "object" && v.mix;
- };
- const getMixer = (v) => (isCustomValueType(v) ? v.mix : undefined);
- function transform(...args) {
- const useImmediate = !Array.isArray(args[0]);
- const argOffset = useImmediate ? 0 : -1;
- const inputValue = args[0 + argOffset];
- const inputRange = args[1 + argOffset];
- const outputRange = args[2 + argOffset];
- const options = args[3 + argOffset];
- const interpolator = create.interpolate(inputRange, outputRange, {
- mixer: getMixer(outputRange[0]),
- ...options,
- });
- return useImmediate ? interpolator(inputValue) : interpolator;
- }
- function useCombineMotionValues(values, combineValues) {
- /**
- * Initialise the returned motion value. This remains the same between renders.
- */
- const value = useMotionValue(combineValues());
- /**
- * Create a function that will update the template motion value with the latest values.
- * This is pre-bound so whenever a motion value updates it can schedule its
- * execution in Framesync. If it's already been scheduled it won't be fired twice
- * in a single frame.
- */
- const updateValue = () => value.set(combineValues());
- /**
- * Synchronously update the motion value with the latest values during the render.
- * This ensures that within a React render, the styles applied to the DOM are up-to-date.
- */
- updateValue();
- /**
- * Subscribe to all motion values found within the template. Whenever any of them change,
- * schedule an update.
- */
- create.useIsomorphicLayoutEffect(() => {
- const scheduleUpdate = () => motionDom.frame.preRender(updateValue, false, true);
- const subscriptions = values.map((v) => v.on("change", scheduleUpdate));
- return () => {
- subscriptions.forEach((unsubscribe) => unsubscribe());
- motionDom.cancelFrame(updateValue);
- };
- });
- return value;
- }
- function useComputed(compute) {
- /**
- * Open session of collectMotionValues. Any MotionValue that calls get()
- * will be saved into this array.
- */
- motionDom.collectMotionValues.current = [];
- compute();
- const value = useCombineMotionValues(motionDom.collectMotionValues.current, compute);
- /**
- * Synchronously close session of collectMotionValues.
- */
- motionDom.collectMotionValues.current = undefined;
- return value;
- }
- function useTransform(input, inputRangeOrTransformer, outputRange, options) {
- if (typeof input === "function") {
- return useComputed(input);
- }
- const transformer = typeof inputRangeOrTransformer === "function"
- ? inputRangeOrTransformer
- : transform(inputRangeOrTransformer, outputRange, options);
- return Array.isArray(input)
- ? useListTransform(input, transformer)
- : useListTransform([input], ([latest]) => transformer(latest));
- }
- function useListTransform(values, transformer) {
- const latest = create.useConstant(() => []);
- return useCombineMotionValues(values, () => {
- latest.length = 0;
- const numValues = values.length;
- for (let i = 0; i < numValues; i++) {
- latest[i] = values[i].get();
- }
- return transformer(latest);
- });
- }
- function useDefaultMotionValue(value, defaultValue = 0) {
- return create.isMotionValue(value) ? value : useMotionValue(defaultValue);
- }
- function ReorderItemComponent({ children, style = {}, value, as = "li", onDrag, layout = true, ...props }, externalRef) {
- const Component = create.useConstant(() => motion[as]);
- const context = React.useContext(ReorderContext);
- const point = {
- x: useDefaultMotionValue(style.x),
- y: useDefaultMotionValue(style.y),
- };
- const zIndex = useTransform([point.x, point.y], ([latestX, latestY]) => latestX || latestY ? 1 : "unset");
- motionUtils.invariant(Boolean(context), "Reorder.Item must be a child of Reorder.Group");
- const { axis, registerItem, updateOrder } = context;
- return (jsxRuntime.jsx(Component, { drag: axis, ...props, dragSnapToOrigin: true, style: { ...style, x: point.x, y: point.y, zIndex }, layout: layout, onDrag: (event, gesturePoint) => {
- const { velocity } = gesturePoint;
- velocity[axis] &&
- updateOrder(value, point[axis].get(), velocity[axis]);
- onDrag && onDrag(event, gesturePoint);
- }, onLayoutMeasure: (measured) => registerItem(value, measured), ref: externalRef, ignoreStrict: true, children: children }));
- }
- const ReorderItem = /*@__PURE__*/ React.forwardRef(ReorderItemComponent);
- var namespace = /*#__PURE__*/Object.freeze({
- __proto__: null,
- Group: ReorderGroup,
- Item: ReorderItem
- });
- const wrap = (min, max, v) => {
- const rangeSize = max - min;
- return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min;
- };
- function getEasingForSegment(easing, i) {
- return create.isEasingArray(easing) ? easing[wrap(0, easing.length, i)] : easing;
- }
- function isDOMKeyframes(keyframes) {
- return typeof keyframes === "object" && !Array.isArray(keyframes);
- }
- function resolveSubjects(subject, keyframes, scope, selectorCache) {
- if (typeof subject === "string" && isDOMKeyframes(keyframes)) {
- return motionDom.resolveElements(subject, scope, selectorCache);
- }
- else if (subject instanceof NodeList) {
- return Array.from(subject);
- }
- else if (Array.isArray(subject)) {
- return subject;
- }
- else {
- return [subject];
- }
- }
- function calculateRepeatDuration(duration, repeat, _repeatDelay) {
- return duration * (repeat + 1);
- }
- /**
- * Given a absolute or relative time definition and current/prev time state of the sequence,
- * calculate an absolute time for the next keyframes.
- */
- function calcNextTime(current, next, prev, labels) {
- if (typeof next === "number") {
- return next;
- }
- else if (next.startsWith("-") || next.startsWith("+")) {
- return Math.max(0, current + parseFloat(next));
- }
- else if (next === "<") {
- return prev;
- }
- else {
- return labels.get(next) ?? current;
- }
- }
- function eraseKeyframes(sequence, startTime, endTime) {
- for (let i = 0; i < sequence.length; i++) {
- const keyframe = sequence[i];
- if (keyframe.at > startTime && keyframe.at < endTime) {
- motionUtils.removeItem(sequence, keyframe);
- // If we remove this item we have to push the pointer back one
- i--;
- }
- }
- }
- function addKeyframes(sequence, keyframes, easing, offset, startTime, endTime) {
- /**
- * Erase every existing value between currentTime and targetTime,
- * this will essentially splice this timeline into any currently
- * defined ones.
- */
- eraseKeyframes(sequence, startTime, endTime);
- for (let i = 0; i < keyframes.length; i++) {
- sequence.push({
- value: keyframes[i],
- at: create.mixNumber(startTime, endTime, offset[i]),
- easing: getEasingForSegment(easing, i),
- });
- }
- }
- /**
- * Take an array of times that represent repeated keyframes. For instance
- * if we have original times of [0, 0.5, 1] then our repeated times will
- * be [0, 0.5, 1, 1, 1.5, 2]. Loop over the times and scale them back
- * down to a 0-1 scale.
- */
- function normalizeTimes(times, repeat) {
- for (let i = 0; i < times.length; i++) {
- times[i] = times[i] / (repeat + 1);
- }
- }
- function compareByTime(a, b) {
- if (a.at === b.at) {
- if (a.value === null)
- return 1;
- if (b.value === null)
- return -1;
- return 0;
- }
- else {
- return a.at - b.at;
- }
- }
- const defaultSegmentEasing = "easeInOut";
- const MAX_REPEAT = 20;
- function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...sequenceTransition } = {}, scope, generators) {
- const defaultDuration = defaultTransition.duration || 0.3;
- const animationDefinitions = new Map();
- const sequences = new Map();
- const elementCache = {};
- const timeLabels = new Map();
- let prevTime = 0;
- let currentTime = 0;
- let totalDuration = 0;
- /**
- * Build the timeline by mapping over the sequence array and converting
- * the definitions into keyframes and offsets with absolute time values.
- * These will later get converted into relative offsets in a second pass.
- */
- for (let i = 0; i < sequence.length; i++) {
- const segment = sequence[i];
- /**
- * If this is a timeline label, mark it and skip the rest of this iteration.
- */
- if (typeof segment === "string") {
- timeLabels.set(segment, currentTime);
- continue;
- }
- else if (!Array.isArray(segment)) {
- timeLabels.set(segment.name, calcNextTime(currentTime, segment.at, prevTime, timeLabels));
- continue;
- }
- let [subject, keyframes, transition = {}] = segment;
- /**
- * If a relative or absolute time value has been specified we need to resolve
- * it in relation to the currentTime.
- */
- if (transition.at !== undefined) {
- currentTime = calcNextTime(currentTime, transition.at, prevTime, timeLabels);
- }
- /**
- * Keep track of the maximum duration in this definition. This will be
- * applied to currentTime once the definition has been parsed.
- */
- let maxDuration = 0;
- const resolveValueSequence = (valueKeyframes, valueTransition, valueSequence, elementIndex = 0, numSubjects = 0) => {
- const valueKeyframesAsList = keyframesAsList(valueKeyframes);
- const { delay = 0, times = create.defaultOffset(valueKeyframesAsList), type = "keyframes", repeat, repeatType, repeatDelay = 0, ...remainingTransition } = valueTransition;
- let { ease = defaultTransition.ease || "easeOut", duration } = valueTransition;
- /**
- * Resolve stagger() if defined.
- */
- const calculatedDelay = typeof delay === "function"
- ? delay(elementIndex, numSubjects)
- : delay;
- /**
- * If this animation should and can use a spring, generate a spring easing function.
- */
- const numKeyframes = valueKeyframesAsList.length;
- const createGenerator = motionDom.isGenerator(type)
- ? type
- : generators?.[type];
- if (numKeyframes <= 2 && createGenerator) {
- /**
- * As we're creating an easing function from a spring,
- * ideally we want to generate it using the real distance
- * between the two keyframes. However this isn't always
- * possible - in these situations we use 0-100.
- */
- let absoluteDelta = 100;
- if (numKeyframes === 2 &&
- isNumberKeyframesArray(valueKeyframesAsList)) {
- const delta = valueKeyframesAsList[1] - valueKeyframesAsList[0];
- absoluteDelta = Math.abs(delta);
- }
- const springTransition = { ...remainingTransition };
- if (duration !== undefined) {
- springTransition.duration = motionUtils.secondsToMilliseconds(duration);
- }
- const springEasing = motionDom.createGeneratorEasing(springTransition, absoluteDelta, createGenerator);
- ease = springEasing.ease;
- duration = springEasing.duration;
- }
- duration ?? (duration = defaultDuration);
- const startTime = currentTime + calculatedDelay;
- /**
- * If there's only one time offset of 0, fill in a second with length 1
- */
- if (times.length === 1 && times[0] === 0) {
- times[1] = 1;
- }
- /**
- * Fill out if offset if fewer offsets than keyframes
- */
- const remainder = times.length - valueKeyframesAsList.length;
- remainder > 0 && create.fillOffset(times, remainder);
- /**
- * If only one value has been set, ie [1], push a null to the start of
- * the keyframe array. This will let us mark a keyframe at this point
- * that will later be hydrated with the previous value.
- */
- valueKeyframesAsList.length === 1 &&
- valueKeyframesAsList.unshift(null);
- /**
- * Handle repeat options
- */
- if (repeat) {
- motionUtils.invariant(repeat < MAX_REPEAT, "Repeat count too high, must be less than 20");
- duration = calculateRepeatDuration(duration, repeat);
- const originalKeyframes = [...valueKeyframesAsList];
- const originalTimes = [...times];
- ease = Array.isArray(ease) ? [...ease] : [ease];
- const originalEase = [...ease];
- for (let repeatIndex = 0; repeatIndex < repeat; repeatIndex++) {
- valueKeyframesAsList.push(...originalKeyframes);
- for (let keyframeIndex = 0; keyframeIndex < originalKeyframes.length; keyframeIndex++) {
- times.push(originalTimes[keyframeIndex] + (repeatIndex + 1));
- ease.push(keyframeIndex === 0
- ? "linear"
- : getEasingForSegment(originalEase, keyframeIndex - 1));
- }
- }
- normalizeTimes(times, repeat);
- }
- const targetTime = startTime + duration;
- /**
- * Add keyframes, mapping offsets to absolute time.
- */
- addKeyframes(valueSequence, valueKeyframesAsList, ease, times, startTime, targetTime);
- maxDuration = Math.max(calculatedDelay + duration, maxDuration);
- totalDuration = Math.max(targetTime, totalDuration);
- };
- if (create.isMotionValue(subject)) {
- const subjectSequence = getSubjectSequence(subject, sequences);
- resolveValueSequence(keyframes, transition, getValueSequence("default", subjectSequence));
- }
- else {
- const subjects = resolveSubjects(subject, keyframes, scope, elementCache);
- const numSubjects = subjects.length;
- /**
- * For every element in this segment, process the defined values.
- */
- for (let subjectIndex = 0; subjectIndex < numSubjects; subjectIndex++) {
- /**
- * Cast necessary, but we know these are of this type
- */
- keyframes = keyframes;
- transition = transition;
- const thisSubject = subjects[subjectIndex];
- const subjectSequence = getSubjectSequence(thisSubject, sequences);
- for (const key in keyframes) {
- resolveValueSequence(keyframes[key], getValueTransition(transition, key), getValueSequence(key, subjectSequence), subjectIndex, numSubjects);
- }
- }
- }
- prevTime = currentTime;
- currentTime += maxDuration;
- }
- /**
- * For every element and value combination create a new animation.
- */
- sequences.forEach((valueSequences, element) => {
- for (const key in valueSequences) {
- const valueSequence = valueSequences[key];
- /**
- * Arrange all the keyframes in ascending time order.
- */
- valueSequence.sort(compareByTime);
- const keyframes = [];
- const valueOffset = [];
- const valueEasing = [];
- /**
- * For each keyframe, translate absolute times into
- * relative offsets based on the total duration of the timeline.
- */
- for (let i = 0; i < valueSequence.length; i++) {
- const { at, value, easing } = valueSequence[i];
- keyframes.push(value);
- valueOffset.push(motionUtils.progress(0, totalDuration, at));
- valueEasing.push(easing || "easeOut");
- }
- /**
- * If the first keyframe doesn't land on offset: 0
- * provide one by duplicating the initial keyframe. This ensures
- * it snaps to the first keyframe when the animation starts.
- */
- if (valueOffset[0] !== 0) {
- valueOffset.unshift(0);
- keyframes.unshift(keyframes[0]);
- valueEasing.unshift(defaultSegmentEasing);
- }
- /**
- * If the last keyframe doesn't land on offset: 1
- * provide one with a null wildcard value. This will ensure it
- * stays static until the end of the animation.
- */
- if (valueOffset[valueOffset.length - 1] !== 1) {
- valueOffset.push(1);
- keyframes.push(null);
- }
- if (!animationDefinitions.has(element)) {
- animationDefinitions.set(element, {
- keyframes: {},
- transition: {},
- });
- }
- const definition = animationDefinitions.get(element);
- definition.keyframes[key] = keyframes;
- definition.transition[key] = {
- ...defaultTransition,
- duration: totalDuration,
- ease: valueEasing,
- times: valueOffset,
- ...sequenceTransition,
- };
- }
- });
- return animationDefinitions;
- }
- function getSubjectSequence(subject, sequences) {
- !sequences.has(subject) && sequences.set(subject, {});
- return sequences.get(subject);
- }
- function getValueSequence(name, sequences) {
- if (!sequences[name])
- sequences[name] = [];
- return sequences[name];
- }
- function keyframesAsList(keyframes) {
- return Array.isArray(keyframes) ? keyframes : [keyframes];
- }
- function getValueTransition(transition, key) {
- return transition && transition[key]
- ? {
- ...transition,
- ...transition[key],
- }
- : { ...transition };
- }
- const isNumber = (keyframe) => typeof keyframe === "number";
- const isNumberKeyframesArray = (keyframes) => keyframes.every(isNumber);
- function isObjectKey(key, object) {
- return key in object;
- }
- class ObjectVisualElement extends create.VisualElement {
- constructor() {
- super(...arguments);
- this.type = "object";
- }
- readValueFromInstance(instance, key) {
- if (isObjectKey(key, instance)) {
- const value = instance[key];
- if (typeof value === "string" || typeof value === "number") {
- return value;
- }
- }
- return undefined;
- }
- getBaseTargetFromProps() {
- return undefined;
- }
- removeValueFromRenderState(key, renderState) {
- delete renderState.output[key];
- }
- measureInstanceViewportBox() {
- return create.createBox();
- }
- build(renderState, latestValues) {
- Object.assign(renderState.output, latestValues);
- }
- renderInstance(instance, { output }) {
- Object.assign(instance, output);
- }
- sortInstanceNodePosition() {
- return 0;
- }
- }
- function createDOMVisualElement(element) {
- const options = {
- presenceContext: null,
- props: {},
- visualState: {
- renderState: {
- transform: {},
- transformOrigin: {},
- style: {},
- vars: {},
- attrs: {},
- },
- latestValues: {},
- },
- };
- const node = create.isSVGElement(element)
- ? new create.SVGVisualElement(options)
- : new create.HTMLVisualElement(options);
- node.mount(element);
- create.visualElementStore.set(element, node);
- }
- function createObjectVisualElement(subject) {
- const options = {
- presenceContext: null,
- props: {},
- visualState: {
- renderState: {
- output: {},
- },
- latestValues: {},
- },
- };
- const node = new ObjectVisualElement(options);
- node.mount(subject);
- create.visualElementStore.set(subject, node);
- }
- function isSingleValue(subject, keyframes) {
- return (create.isMotionValue(subject) ||
- typeof subject === "number" ||
- (typeof subject === "string" && !isDOMKeyframes(keyframes)));
- }
- /**
- * Implementation
- */
- function animateSubject(subject, keyframes, options, scope) {
- const animations = [];
- if (isSingleValue(subject, keyframes)) {
- animations.push(create.animateSingleValue(subject, isDOMKeyframes(keyframes)
- ? keyframes.default || keyframes
- : keyframes, options ? options.default || options : options));
- }
- else {
- const subjects = resolveSubjects(subject, keyframes, scope);
- const numSubjects = subjects.length;
- motionUtils.invariant(Boolean(numSubjects), "No valid elements provided.");
- for (let i = 0; i < numSubjects; i++) {
- const thisSubject = subjects[i];
- const createVisualElement = thisSubject instanceof Element
- ? createDOMVisualElement
- : createObjectVisualElement;
- if (!create.visualElementStore.has(thisSubject)) {
- createVisualElement(thisSubject);
- }
- const visualElement = create.visualElementStore.get(thisSubject);
- const transition = { ...options };
- /**
- * Resolve stagger function if provided.
- */
- if ("delay" in transition &&
- typeof transition.delay === "function") {
- transition.delay = transition.delay(i, numSubjects);
- }
- animations.push(...create.animateTarget(visualElement, { ...keyframes, transition }, {}));
- }
- }
- return animations;
- }
- function animateSequence(sequence, options, scope) {
- const animations = [];
- const animationDefinitions = createAnimationsFromSequence(sequence, options, scope, { spring: create.spring });
- animationDefinitions.forEach(({ keyframes, transition }, subject) => {
- animations.push(...animateSubject(subject, keyframes, transition));
- });
- return animations;
- }
- function isSequence(value) {
- return Array.isArray(value) && value.some(Array.isArray);
- }
- /**
- * Creates an animation function that is optionally scoped
- * to a specific element.
- */
- function createScopedAnimate(scope) {
- /**
- * Implementation
- */
- function scopedAnimate(subjectOrSequence, optionsOrKeyframes, options) {
- let animations = [];
- if (isSequence(subjectOrSequence)) {
- animations = animateSequence(subjectOrSequence, optionsOrKeyframes, scope);
- }
- else {
- animations = animateSubject(subjectOrSequence, optionsOrKeyframes, options, scope);
- }
- const animation = new motionDom.GroupAnimationWithThen(animations);
- if (scope) {
- scope.animations.push(animation);
- }
- return animation;
- }
- return scopedAnimate;
- }
- const animate = createScopedAnimate();
- function animateElements(elementOrSelector, keyframes, options, scope) {
- const elements = motionDom.resolveElements(elementOrSelector, scope);
- const numElements = elements.length;
- motionUtils.invariant(Boolean(numElements), "No valid element provided.");
- const animations = [];
- for (let i = 0; i < numElements; i++) {
- const element = elements[i];
- const elementTransition = { ...options };
- /**
- * Resolve stagger function if provided.
- */
- if (typeof elementTransition.delay === "function") {
- elementTransition.delay = elementTransition.delay(i, numElements);
- }
- for (const valueName in keyframes) {
- const valueKeyframes = keyframes[valueName];
- const valueOptions = {
- ...motionDom.getValueTransition(elementTransition, valueName),
- };
- valueOptions.duration && (valueOptions.duration = motionUtils.secondsToMilliseconds(valueOptions.duration));
- valueOptions.delay && (valueOptions.delay = motionUtils.secondsToMilliseconds(valueOptions.delay));
- animations.push(new motionDom.NativeAnimation({
- element,
- name: valueName,
- keyframes: valueKeyframes,
- transition: valueOptions,
- allowFlatten: !elementTransition.type && !elementTransition.ease,
- }));
- }
- }
- return animations;
- }
- const createScopedWaapiAnimate = (scope) => {
- function scopedAnimate(elementOrSelector, keyframes, options) {
- return new motionDom.GroupAnimationWithThen(animateElements(elementOrSelector, keyframes, options, scope));
- }
- return scopedAnimate;
- };
- const animateMini = /*@__PURE__*/ createScopedWaapiAnimate();
- function observeTimeline(update, timeline) {
- let prevProgress;
- const onFrame = () => {
- const { currentTime } = timeline;
- const percentage = currentTime === null ? 0 : currentTime.value;
- const progress = percentage / 100;
- if (prevProgress !== progress) {
- update(progress);
- }
- prevProgress = progress;
- };
- motionDom.frame.update(onFrame, true);
- return () => motionDom.cancelFrame(onFrame);
- }
- const resizeHandlers = new WeakMap();
- let observer;
- function getElementSize(target, borderBoxSize) {
- if (borderBoxSize) {
- const { inlineSize, blockSize } = borderBoxSize[0];
- return { width: inlineSize, height: blockSize };
- }
- else if (target instanceof SVGElement && "getBBox" in target) {
- return target.getBBox();
- }
- else {
- return {
- width: target.offsetWidth,
- height: target.offsetHeight,
- };
- }
- }
- function notifyTarget({ target, contentRect, borderBoxSize, }) {
- resizeHandlers.get(target)?.forEach((handler) => {
- handler({
- target,
- contentSize: contentRect,
- get size() {
- return getElementSize(target, borderBoxSize);
- },
- });
- });
- }
- function notifyAll(entries) {
- entries.forEach(notifyTarget);
- }
- function createResizeObserver() {
- if (typeof ResizeObserver === "undefined")
- return;
- observer = new ResizeObserver(notifyAll);
- }
- function resizeElement(target, handler) {
- if (!observer)
- createResizeObserver();
- const elements = motionDom.resolveElements(target);
- elements.forEach((element) => {
- let elementHandlers = resizeHandlers.get(element);
- if (!elementHandlers) {
- elementHandlers = new Set();
- resizeHandlers.set(element, elementHandlers);
- }
- elementHandlers.add(handler);
- observer?.observe(element);
- });
- return () => {
- elements.forEach((element) => {
- const elementHandlers = resizeHandlers.get(element);
- elementHandlers?.delete(handler);
- if (!elementHandlers?.size) {
- observer?.unobserve(element);
- }
- });
- };
- }
- const windowCallbacks = new Set();
- let windowResizeHandler;
- function createWindowResizeHandler() {
- windowResizeHandler = () => {
- const size = {
- width: window.innerWidth,
- height: window.innerHeight,
- };
- const info = {
- target: window,
- size,
- contentSize: size,
- };
- windowCallbacks.forEach((callback) => callback(info));
- };
- window.addEventListener("resize", windowResizeHandler);
- }
- function resizeWindow(callback) {
- windowCallbacks.add(callback);
- if (!windowResizeHandler)
- createWindowResizeHandler();
- return () => {
- windowCallbacks.delete(callback);
- if (!windowCallbacks.size && windowResizeHandler) {
- windowResizeHandler = undefined;
- }
- };
- }
- function resize(a, b) {
- return typeof a === "function" ? resizeWindow(a) : resizeElement(a, b);
- }
- /**
- * A time in milliseconds, beyond which we consider the scroll velocity to be 0.
- */
- const maxElapsed = 50;
- const createAxisInfo = () => ({
- current: 0,
- offset: [],
- progress: 0,
- scrollLength: 0,
- targetOffset: 0,
- targetLength: 0,
- containerLength: 0,
- velocity: 0,
- });
- const createScrollInfo = () => ({
- time: 0,
- x: createAxisInfo(),
- y: createAxisInfo(),
- });
- const keys = {
- x: {
- length: "Width",
- position: "Left",
- },
- y: {
- length: "Height",
- position: "Top",
- },
- };
- function updateAxisInfo(element, axisName, info, time) {
- const axis = info[axisName];
- const { length, position } = keys[axisName];
- const prev = axis.current;
- const prevTime = info.time;
- axis.current = element[`scroll${position}`];
- axis.scrollLength = element[`scroll${length}`] - element[`client${length}`];
- axis.offset.length = 0;
- axis.offset[0] = 0;
- axis.offset[1] = axis.scrollLength;
- axis.progress = motionUtils.progress(0, axis.scrollLength, axis.current);
- const elapsed = time - prevTime;
- axis.velocity =
- elapsed > maxElapsed
- ? 0
- : motionUtils.velocityPerSecond(axis.current - prev, elapsed);
- }
- function updateScrollInfo(element, info, time) {
- updateAxisInfo(element, "x", info, time);
- updateAxisInfo(element, "y", info, time);
- info.time = time;
- }
- function calcInset(element, container) {
- const inset = { x: 0, y: 0 };
- let current = element;
- while (current && current !== container) {
- if (current instanceof HTMLElement) {
- inset.x += current.offsetLeft;
- inset.y += current.offsetTop;
- current = current.offsetParent;
- }
- else if (current.tagName === "svg") {
- /**
- * This isn't an ideal approach to measuring the offset of <svg /> tags.
- * It would be preferable, given they behave like HTMLElements in most ways
- * to use offsetLeft/Top. But these don't exist on <svg />. Likewise we
- * can't use .getBBox() like most SVG elements as these provide the offset
- * relative to the SVG itself, which for <svg /> is usually 0x0.
- */
- const svgBoundingBox = current.getBoundingClientRect();
- current = current.parentElement;
- const parentBoundingBox = current.getBoundingClientRect();
- inset.x += svgBoundingBox.left - parentBoundingBox.left;
- inset.y += svgBoundingBox.top - parentBoundingBox.top;
- }
- else if (current instanceof SVGGraphicsElement) {
- const { x, y } = current.getBBox();
- inset.x += x;
- inset.y += y;
- let svg = null;
- let parent = current.parentNode;
- while (!svg) {
- if (parent.tagName === "svg") {
- svg = parent;
- }
- parent = current.parentNode;
- }
- current = svg;
- }
- else {
- break;
- }
- }
- return inset;
- }
- const namedEdges = {
- start: 0,
- center: 0.5,
- end: 1,
- };
- function resolveEdge(edge, length, inset = 0) {
- let delta = 0;
- /**
- * If we have this edge defined as a preset, replace the definition
- * with the numerical value.
- */
- if (edge in namedEdges) {
- edge = namedEdges[edge];
- }
- /**
- * Handle unit values
- */
- if (typeof edge === "string") {
- const asNumber = parseFloat(edge);
- if (edge.endsWith("px")) {
- delta = asNumber;
- }
- else if (edge.endsWith("%")) {
- edge = asNumber / 100;
- }
- else if (edge.endsWith("vw")) {
- delta = (asNumber / 100) * document.documentElement.clientWidth;
- }
- else if (edge.endsWith("vh")) {
- delta = (asNumber / 100) * document.documentElement.clientHeight;
- }
- else {
- edge = asNumber;
- }
- }
- /**
- * If the edge is defined as a number, handle as a progress value.
- */
- if (typeof edge === "number") {
- delta = length * edge;
- }
- return inset + delta;
- }
- const defaultOffset = [0, 0];
- function resolveOffset(offset, containerLength, targetLength, targetInset) {
- let offsetDefinition = Array.isArray(offset) ? offset : defaultOffset;
- let targetPoint = 0;
- let containerPoint = 0;
- if (typeof offset === "number") {
- /**
- * If we're provided offset: [0, 0.5, 1] then each number x should become
- * [x, x], so we default to the behaviour of mapping 0 => 0 of both target
- * and container etc.
- */
- offsetDefinition = [offset, offset];
- }
- else if (typeof offset === "string") {
- offset = offset.trim();
- if (offset.includes(" ")) {
- offsetDefinition = offset.split(" ");
- }
- else {
- /**
- * If we're provided a definition like "100px" then we want to apply
- * that only to the top of the target point, leaving the container at 0.
- * Whereas a named offset like "end" should be applied to both.
- */
- offsetDefinition = [offset, namedEdges[offset] ? offset : `0`];
- }
- }
- targetPoint = resolveEdge(offsetDefinition[0], targetLength, targetInset);
- containerPoint = resolveEdge(offsetDefinition[1], containerLength);
- return targetPoint - containerPoint;
- }
- const ScrollOffset = {
- Enter: [
- [0, 1],
- [1, 1],
- ],
- Exit: [
- [0, 0],
- [1, 0],
- ],
- Any: [
- [1, 0],
- [0, 1],
- ],
- All: [
- [0, 0],
- [1, 1],
- ],
- };
- const point = { x: 0, y: 0 };
- function getTargetSize(target) {
- return "getBBox" in target && target.tagName !== "svg"
- ? target.getBBox()
- : { width: target.clientWidth, height: target.clientHeight };
- }
- function resolveOffsets(container, info, options) {
- const { offset: offsetDefinition = ScrollOffset.All } = options;
- const { target = container, axis = "y" } = options;
- const lengthLabel = axis === "y" ? "height" : "width";
- const inset = target !== container ? calcInset(target, container) : point;
- /**
- * Measure the target and container. If they're the same thing then we
- * use the container's scrollWidth/Height as the target, from there
- * all other calculations can remain the same.
- */
- const targetSize = target === container
- ? { width: container.scrollWidth, height: container.scrollHeight }
- : getTargetSize(target);
- const containerSize = {
- width: container.clientWidth,
- height: container.clientHeight,
- };
- /**
- * Reset the length of the resolved offset array rather than creating a new one.
- * TODO: More reusable data structures for targetSize/containerSize would also be good.
- */
- info[axis].offset.length = 0;
- /**
- * Populate the offset array by resolving the user's offset definition into
- * a list of pixel scroll offets.
- */
- let hasChanged = !info[axis].interpolate;
- const numOffsets = offsetDefinition.length;
- for (let i = 0; i < numOffsets; i++) {
- const offset = resolveOffset(offsetDefinition[i], containerSize[lengthLabel], targetSize[lengthLabel], inset[axis]);
- if (!hasChanged && offset !== info[axis].interpolatorOffsets[i]) {
- hasChanged = true;
- }
- info[axis].offset[i] = offset;
- }
- /**
- * If the pixel scroll offsets have changed, create a new interpolator function
- * to map scroll value into a progress.
- */
- if (hasChanged) {
- info[axis].interpolate = create.interpolate(info[axis].offset, create.defaultOffset(offsetDefinition), { clamp: false });
- info[axis].interpolatorOffsets = [...info[axis].offset];
- }
- info[axis].progress = create.clamp(0, 1, info[axis].interpolate(info[axis].current));
- }
- function measure(container, target = container, info) {
- /**
- * Find inset of target within scrollable container
- */
- info.x.targetOffset = 0;
- info.y.targetOffset = 0;
- if (target !== container) {
- let node = target;
- while (node && node !== container) {
- info.x.targetOffset += node.offsetLeft;
- info.y.targetOffset += node.offsetTop;
- node = node.offsetParent;
- }
- }
- info.x.targetLength =
- target === container ? target.scrollWidth : target.clientWidth;
- info.y.targetLength =
- target === container ? target.scrollHeight : target.clientHeight;
- info.x.containerLength = container.clientWidth;
- info.y.containerLength = container.clientHeight;
- /**
- * In development mode ensure scroll containers aren't position: static as this makes
- * it difficult to measure their relative positions.
- */
- if (process.env.NODE_ENV !== "production") {
- if (container && target && target !== container) {
- motionUtils.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.");
- }
- }
- }
- function createOnScrollHandler(element, onScroll, info, options = {}) {
- return {
- measure: () => measure(element, options.target, info),
- update: (time) => {
- updateScrollInfo(element, info, time);
- if (options.offset || options.target) {
- resolveOffsets(element, info, options);
- }
- },
- notify: () => onScroll(info),
- };
- }
- const scrollListeners = new WeakMap();
- const resizeListeners = new WeakMap();
- const onScrollHandlers = new WeakMap();
- const getEventTarget = (element) => element === document.documentElement ? window : element;
- function scrollInfo(onScroll, { container = document.documentElement, ...options } = {}) {
- let containerHandlers = onScrollHandlers.get(container);
- /**
- * Get the onScroll handlers for this container.
- * If one isn't found, create a new one.
- */
- if (!containerHandlers) {
- containerHandlers = new Set();
- onScrollHandlers.set(container, containerHandlers);
- }
- /**
- * Create a new onScroll handler for the provided callback.
- */
- const info = createScrollInfo();
- const containerHandler = createOnScrollHandler(container, onScroll, info, options);
- containerHandlers.add(containerHandler);
- /**
- * Check if there's a scroll event listener for this container.
- * If not, create one.
- */
- if (!scrollListeners.has(container)) {
- const measureAll = () => {
- for (const handler of containerHandlers)
- handler.measure();
- };
- const updateAll = () => {
- for (const handler of containerHandlers) {
- handler.update(motionDom.frameData.timestamp);
- }
- };
- const notifyAll = () => {
- for (const handler of containerHandlers)
- handler.notify();
- };
- const listener = () => {
- motionDom.frame.read(measureAll, false, true);
- motionDom.frame.read(updateAll, false, true);
- motionDom.frame.update(notifyAll, false, true);
- };
- scrollListeners.set(container, listener);
- const target = getEventTarget(container);
- window.addEventListener("resize", listener, { passive: true });
- if (container !== document.documentElement) {
- resizeListeners.set(container, resize(container, listener));
- }
- target.addEventListener("scroll", listener, { passive: true });
- }
- const listener = scrollListeners.get(container);
- motionDom.frame.read(listener, false, true);
- return () => {
- motionDom.cancelFrame(listener);
- /**
- * Check if we even have any handlers for this container.
- */
- const currentHandlers = onScrollHandlers.get(container);
- if (!currentHandlers)
- return;
- currentHandlers.delete(containerHandler);
- if (currentHandlers.size)
- return;
- /**
- * If no more handlers, remove the scroll listener too.
- */
- const scrollListener = scrollListeners.get(container);
- scrollListeners.delete(container);
- if (scrollListener) {
- getEventTarget(container).removeEventListener("scroll", scrollListener);
- resizeListeners.get(container)?.();
- window.removeEventListener("resize", scrollListener);
- }
- };
- }
- function scrollTimelineFallback({ source, container, axis = "y", }) {
- // Support legacy source argument. Deprecate later.
- if (source)
- container = source;
- // ScrollTimeline records progress as a percentage CSSUnitValue
- const currentTime = { value: 0 };
- const cancel = scrollInfo((info) => {
- currentTime.value = info[axis].progress * 100;
- }, { container, axis });
- return { currentTime, cancel };
- }
- const timelineCache = new Map();
- function getTimeline({ source, container = document.documentElement, axis = "y", } = {}) {
- // Support legacy source argument. Deprecate later.
- if (source)
- container = source;
- if (!timelineCache.has(container)) {
- timelineCache.set(container, {});
- }
- const elementCache = timelineCache.get(container);
- if (!elementCache[axis]) {
- elementCache[axis] = motionDom.supportsScrollTimeline()
- ? new ScrollTimeline({ source: container, axis })
- : scrollTimelineFallback({ source: container, axis });
- }
- return elementCache[axis];
- }
- /**
- * If the onScroll function has two arguments, it's expecting
- * more specific information about the scroll from scrollInfo.
- */
- function isOnScrollWithInfo(onScroll) {
- return onScroll.length === 2;
- }
- /**
- * Currently, we only support element tracking with `scrollInfo`, though in
- * the future we can also offer ViewTimeline support.
- */
- function needsElementTracking(options) {
- return options && (options.target || options.offset);
- }
- function scrollFunction(onScroll, options) {
- if (isOnScrollWithInfo(onScroll) || needsElementTracking(options)) {
- return scrollInfo((info) => {
- onScroll(info[options.axis].progress, info);
- }, options);
- }
- else {
- return observeTimeline(onScroll, getTimeline(options));
- }
- }
- function scrollAnimation(animation, options) {
- animation.flatten();
- if (needsElementTracking(options)) {
- animation.pause();
- return scrollInfo((info) => {
- animation.time = animation.duration * info[options.axis].progress;
- }, options);
- }
- else {
- const timeline = getTimeline(options);
- if (animation.attachTimeline) {
- return animation.attachTimeline(timeline, (valueAnimation) => {
- valueAnimation.pause();
- return observeTimeline((progress) => {
- valueAnimation.time = valueAnimation.duration * progress;
- }, timeline);
- });
- }
- else {
- return motionUtils.noop;
- }
- }
- }
- function scroll(onScroll, { axis = "y", ...options } = {}) {
- const optionsWithDefaults = { axis, ...options };
- return typeof onScroll === "function"
- ? scrollFunction(onScroll, optionsWithDefaults)
- : scrollAnimation(onScroll, optionsWithDefaults);
- }
- const thresholds = {
- some: 0,
- all: 1,
- };
- function inView(elementOrSelector, onStart, { root, margin: rootMargin, amount = "some" } = {}) {
- const elements = motionDom.resolveElements(elementOrSelector);
- const activeIntersections = new WeakMap();
- const onIntersectionChange = (entries) => {
- entries.forEach((entry) => {
- const onEnd = activeIntersections.get(entry.target);
- /**
- * If there's no change to the intersection, we don't need to
- * do anything here.
- */
- if (entry.isIntersecting === Boolean(onEnd))
- return;
- if (entry.isIntersecting) {
- const newOnEnd = onStart(entry.target, entry);
- if (typeof newOnEnd === "function") {
- activeIntersections.set(entry.target, newOnEnd);
- }
- else {
- observer.unobserve(entry.target);
- }
- }
- else if (typeof onEnd === "function") {
- onEnd(entry);
- activeIntersections.delete(entry.target);
- }
- });
- };
- const observer = new IntersectionObserver(onIntersectionChange, {
- root,
- rootMargin,
- threshold: typeof amount === "number" ? amount : thresholds[amount],
- });
- elements.forEach((element) => observer.observe(element));
- return () => observer.disconnect();
- }
- function steps(numSteps, direction = "end") {
- return (progress) => {
- progress =
- direction === "end"
- ? Math.min(progress, 0.999)
- : Math.max(progress, 0.001);
- const expanded = progress * numSteps;
- const rounded = direction === "end" ? Math.floor(expanded) : Math.ceil(expanded);
- return create.clamp(0, 1, rounded / numSteps);
- };
- }
- function getOriginIndex(from, total) {
- if (from === "first") {
- return 0;
- }
- else {
- const lastIndex = total - 1;
- return from === "last" ? lastIndex : lastIndex / 2;
- }
- }
- function stagger(duration = 0.1, { startDelay = 0, from = 0, ease } = {}) {
- return (i, total) => {
- const fromIndex = typeof from === "number" ? from : getOriginIndex(from, total);
- const distance = Math.abs(fromIndex - i);
- let delay = duration * distance;
- if (ease) {
- const maxDelay = total * duration;
- const easingFunction = create.easingDefinitionToFunction(ease);
- delay = easingFunction(delay / maxDelay) * maxDelay;
- }
- return startDelay + delay;
- };
- }
- const createMinimalMotionComponent =
- /*@__PURE__*/ create.createMotionComponentFactory();
- const m = /*@__PURE__*/ createDOMMotionComponentProxy(createMinimalMotionComponent);
- function useUnmountEffect(callback) {
- return React.useEffect(() => () => callback(), []);
- }
- /**
- * @public
- */
- const domAnimation = {
- renderer: create.createDomVisualElement,
- ...create.animations,
- ...create.gestureAnimations,
- };
- /**
- * @public
- */
- const domMax = {
- ...domAnimation,
- ...create.drag,
- ...create.layout,
- };
- /**
- * @public
- */
- const domMin = {
- renderer: create.createDomVisualElement,
- ...create.animations,
- };
- function useMotionValueEvent(value, event, callback) {
- /**
- * useInsertionEffect will create subscriptions before any other
- * effects will run. Effects run upwards through the tree so it
- * can be that binding a useLayoutEffect higher up the tree can
- * miss changes from lower down the tree.
- */
- React.useInsertionEffect(() => value.on(event, callback), [value, event, callback]);
- }
- function refWarning(name, ref) {
- motionUtils.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.`);
- }
- const createScrollMotionValues = () => ({
- scrollX: motionDom.motionValue(0),
- scrollY: motionDom.motionValue(0),
- scrollXProgress: motionDom.motionValue(0),
- scrollYProgress: motionDom.motionValue(0),
- });
- function useScroll({ container, target, layoutEffect = true, ...options } = {}) {
- const values = create.useConstant(createScrollMotionValues);
- const useLifecycleEffect = layoutEffect
- ? create.useIsomorphicLayoutEffect
- : React.useEffect;
- useLifecycleEffect(() => {
- refWarning("target", target);
- refWarning("container", container);
- return scroll((_progress, { x, y, }) => {
- values.scrollX.set(x.current);
- values.scrollXProgress.set(x.progress);
- values.scrollY.set(y.current);
- values.scrollYProgress.set(y.progress);
- }, {
- ...options,
- container: container?.current || undefined,
- target: target?.current || undefined,
- });
- }, [container, target, JSON.stringify(options.offset)]);
- return values;
- }
- /**
- * @deprecated useElementScroll is deprecated. Convert to useScroll({ container: ref })
- */
- function useElementScroll(ref) {
- if (process.env.NODE_ENV === "development") {
- motionUtils.warnOnce(false, "useElementScroll is deprecated. Convert to useScroll({ container: ref }).");
- }
- return useScroll({ container: ref });
- }
- /**
- * @deprecated useViewportScroll is deprecated. Convert to useScroll()
- */
- function useViewportScroll() {
- if (process.env.NODE_ENV !== "production") {
- motionUtils.warnOnce(false, "useViewportScroll is deprecated. Convert to useScroll().");
- }
- return useScroll();
- }
- /**
- * Combine multiple motion values into a new one using a string template literal.
- *
- * ```jsx
- * import {
- * motion,
- * useSpring,
- * useMotionValue,
- * useMotionTemplate
- * } from "framer-motion"
- *
- * function Component() {
- * const shadowX = useSpring(0)
- * const shadowY = useMotionValue(0)
- * const shadow = useMotionTemplate`drop-shadow(${shadowX}px ${shadowY}px 20px rgba(0,0,0,0.3))`
- *
- * return <motion.div style={{ filter: shadow }} />
- * }
- * ```
- *
- * @public
- */
- function useMotionTemplate(fragments, ...values) {
- /**
- * Create a function that will build a string from the latest motion values.
- */
- const numFragments = fragments.length;
- function buildValue() {
- let output = ``;
- for (let i = 0; i < numFragments; i++) {
- output += fragments[i];
- const value = values[i];
- if (value) {
- output += create.isMotionValue(value) ? value.get() : value;
- }
- }
- return output;
- }
- return useCombineMotionValues(values.filter(create.isMotionValue), buildValue);
- }
- function useSpring(source, config = {}) {
- const { isStatic } = React.useContext(create.MotionConfigContext);
- const activeSpringAnimation = React.useRef(null);
- const initialValue = create.useConstant(() => create.isMotionValue(source) ? source.get() : source);
- const unit = create.useConstant(() => typeof initialValue === "string"
- ? initialValue.replace(/[\d.-]/g, "")
- : undefined);
- const value = useMotionValue(initialValue);
- const latestValue = React.useRef(initialValue);
- const latestSetter = React.useRef(motionUtils.noop);
- const startAnimation = () => {
- stopAnimation();
- activeSpringAnimation.current = create.animateValue({
- keyframes: [asNumber(value.get()), asNumber(latestValue.current)],
- velocity: value.getVelocity(),
- type: "spring",
- restDelta: 0.001,
- restSpeed: 0.01,
- ...config,
- onUpdate: latestSetter.current,
- });
- };
- const stopAnimation = () => {
- if (activeSpringAnimation.current) {
- activeSpringAnimation.current.stop();
- }
- };
- React.useInsertionEffect(() => {
- return value.attach((v, set) => {
- if (isStatic)
- return set(v);
- latestValue.current = v;
- latestSetter.current = (latest) => set(parseValue(latest, unit));
- motionDom.frame.postRender(startAnimation);
- return value.get();
- }, stopAnimation);
- }, [JSON.stringify(config)]);
- create.useIsomorphicLayoutEffect(() => {
- if (create.isMotionValue(source)) {
- return source.on("change", (v) => value.set(parseValue(v, unit)));
- }
- }, [value, unit]);
- return value;
- }
- function parseValue(v, unit) {
- return unit ? v + unit : v;
- }
- function asNumber(v) {
- return typeof v === "number" ? v : parseFloat(v);
- }
- function useAnimationFrame(callback) {
- const initialTimestamp = React.useRef(0);
- const { isStatic } = React.useContext(create.MotionConfigContext);
- React.useEffect(() => {
- if (isStatic)
- return;
- const provideTimeSinceStart = ({ timestamp, delta }) => {
- if (!initialTimestamp.current)
- initialTimestamp.current = timestamp;
- callback(timestamp - initialTimestamp.current, delta);
- };
- motionDom.frame.update(provideTimeSinceStart, true);
- return () => motionDom.cancelFrame(provideTimeSinceStart);
- }, [callback]);
- }
- function useTime() {
- const time = useMotionValue(0);
- useAnimationFrame((t) => time.set(t));
- return time;
- }
- /**
- * Creates a `MotionValue` that updates when the velocity of the provided `MotionValue` changes.
- *
- * ```javascript
- * const x = useMotionValue(0)
- * const xVelocity = useVelocity(x)
- * const xAcceleration = useVelocity(xVelocity)
- * ```
- *
- * @public
- */
- function useVelocity(value) {
- const velocity = useMotionValue(value.getVelocity());
- const updateVelocity = () => {
- const latest = value.getVelocity();
- velocity.set(latest);
- /**
- * If we still have velocity, schedule an update for the next frame
- * to keep checking until it is zero.
- */
- if (latest)
- motionDom.frame.update(updateVelocity);
- };
- useMotionValueEvent(value, "change", () => {
- // Schedule an update to this value at the end of the current frame.
- motionDom.frame.update(updateVelocity, false, true);
- });
- return velocity;
- }
- function getWillChangeName(name) {
- if (create.transformProps.has(name)) {
- return "transform";
- }
- else if (create.acceleratedValues.has(name)) {
- return create.camelToDash(name);
- }
- }
- class WillChangeMotionValue extends motionDom.MotionValue {
- constructor() {
- super(...arguments);
- this.values = [];
- }
- add(name) {
- const styleName = getWillChangeName(name);
- if (styleName) {
- motionUtils.addUniqueItem(this.values, styleName);
- this.update();
- }
- }
- update() {
- this.set(this.values.length ? this.values.join(", ") : "auto");
- }
- }
- function useWillChange() {
- return create.useConstant(() => new WillChangeMotionValue("auto"));
- }
- /**
- * A hook that returns `true` if we should be using reduced motion based on the current device's Reduced Motion setting.
- *
- * This can be used to implement changes to your UI based on Reduced Motion. For instance, replacing motion-sickness inducing
- * `x`/`y` animations with `opacity`, disabling the autoplay of background videos, or turning off parallax motion.
- *
- * It will actively respond to changes and re-render your components with the latest setting.
- *
- * ```jsx
- * export function Sidebar({ isOpen }) {
- * const shouldReduceMotion = useReducedMotion()
- * const closedX = shouldReduceMotion ? 0 : "-100%"
- *
- * return (
- * <motion.div animate={{
- * opacity: isOpen ? 1 : 0,
- * x: isOpen ? 0 : closedX
- * }} />
- * )
- * }
- * ```
- *
- * @return boolean
- *
- * @public
- */
- function useReducedMotion() {
- /**
- * Lazy initialisation of prefersReducedMotion
- */
- !create.hasReducedMotionListener.current && create.initPrefersReducedMotion();
- const [shouldReduceMotion] = React.useState(create.prefersReducedMotion.current);
- if (process.env.NODE_ENV !== "production") {
- motionUtils.warnOnce(shouldReduceMotion !== true, "You have Reduced Motion enabled on your device. Animations may not appear as expected.");
- }
- /**
- * TODO See if people miss automatically updating shouldReduceMotion setting
- */
- return shouldReduceMotion;
- }
- function useReducedMotionConfig() {
- const reducedMotionPreference = useReducedMotion();
- const { reducedMotion } = React.useContext(create.MotionConfigContext);
- if (reducedMotion === "never") {
- return false;
- }
- else if (reducedMotion === "always") {
- return true;
- }
- else {
- return reducedMotionPreference;
- }
- }
- function stopAnimation(visualElement) {
- visualElement.values.forEach((value) => value.stop());
- }
- function setVariants(visualElement, variantLabels) {
- const reversedLabels = [...variantLabels].reverse();
- reversedLabels.forEach((key) => {
- const variant = visualElement.getVariant(key);
- variant && create.setTarget(visualElement, variant);
- if (visualElement.variantChildren) {
- visualElement.variantChildren.forEach((child) => {
- setVariants(child, variantLabels);
- });
- }
- });
- }
- function setValues(visualElement, definition) {
- if (Array.isArray(definition)) {
- return setVariants(visualElement, definition);
- }
- else if (typeof definition === "string") {
- return setVariants(visualElement, [definition]);
- }
- else {
- create.setTarget(visualElement, definition);
- }
- }
- /**
- * @public
- */
- function animationControls() {
- /**
- * Track whether the host component has mounted.
- */
- let hasMounted = false;
- /**
- * A collection of linked component animation controls.
- */
- const subscribers = new Set();
- const controls = {
- subscribe(visualElement) {
- subscribers.add(visualElement);
- return () => void subscribers.delete(visualElement);
- },
- start(definition, transitionOverride) {
- motionUtils.invariant(hasMounted, "controls.start() should only be called after a component has mounted. Consider calling within a useEffect hook.");
- const animations = [];
- subscribers.forEach((visualElement) => {
- animations.push(create.animateVisualElement(visualElement, definition, {
- transitionOverride,
- }));
- });
- return Promise.all(animations);
- },
- set(definition) {
- motionUtils.invariant(hasMounted, "controls.set() should only be called after a component has mounted. Consider calling within a useEffect hook.");
- return subscribers.forEach((visualElement) => {
- setValues(visualElement, definition);
- });
- },
- stop() {
- subscribers.forEach((visualElement) => {
- stopAnimation(visualElement);
- });
- },
- mount() {
- hasMounted = true;
- return () => {
- hasMounted = false;
- controls.stop();
- };
- },
- };
- return controls;
- }
- function useAnimate() {
- const scope = create.useConstant(() => ({
- current: null, // Will be hydrated by React
- animations: [],
- }));
- const animate = create.useConstant(() => createScopedAnimate(scope));
- useUnmountEffect(() => {
- scope.animations.forEach((animation) => animation.stop());
- });
- return [scope, animate];
- }
- function useAnimateMini() {
- const scope = create.useConstant(() => ({
- current: null, // Will be hydrated by React
- animations: [],
- }));
- const animate = create.useConstant(() => createScopedWaapiAnimate(scope));
- useUnmountEffect(() => {
- scope.animations.forEach((animation) => animation.stop());
- });
- return [scope, animate];
- }
- /**
- * Creates `AnimationControls`, which can be used to manually start, stop
- * and sequence animations on one or more components.
- *
- * The returned `AnimationControls` should be passed to the `animate` property
- * of the components you want to animate.
- *
- * These components can then be animated with the `start` method.
- *
- * ```jsx
- * import * as React from 'react'
- * import { motion, useAnimation } from 'framer-motion'
- *
- * export function MyComponent(props) {
- * const controls = useAnimation()
- *
- * controls.start({
- * x: 100,
- * transition: { duration: 0.5 },
- * })
- *
- * return <motion.div animate={controls} />
- * }
- * ```
- *
- * @returns Animation controller with `start` and `stop` methods
- *
- * @public
- */
- function useAnimationControls() {
- const controls = create.useConstant(animationControls);
- create.useIsomorphicLayoutEffect(controls.mount, []);
- return controls;
- }
- const useAnimation = useAnimationControls;
- function usePresenceData() {
- const context = React.useContext(create.PresenceContext);
- return context ? context.custom : undefined;
- }
- /**
- * Attaches an event listener directly to the provided DOM element.
- *
- * Bypassing React's event system can be desirable, for instance when attaching non-passive
- * event handlers.
- *
- * ```jsx
- * const ref = useRef(null)
- *
- * useDomEvent(ref, 'wheel', onWheel, { passive: false })
- *
- * return <div ref={ref} />
- * ```
- *
- * @param ref - React.RefObject that's been provided to the element you want to bind the listener to.
- * @param eventName - Name of the event you want listen for.
- * @param handler - Function to fire when receiving the event.
- * @param options - Options to pass to `Event.addEventListener`.
- *
- * @public
- */
- function useDomEvent(ref, eventName, handler, options) {
- React.useEffect(() => {
- const element = ref.current;
- if (handler && element) {
- return create.addDomEvent(element, eventName, handler, options);
- }
- }, [ref, eventName, handler, options]);
- }
- /**
- * Can manually trigger a drag gesture on one or more `drag`-enabled `motion` components.
- *
- * ```jsx
- * const dragControls = useDragControls()
- *
- * function startDrag(event) {
- * dragControls.start(event, { snapToCursor: true })
- * }
- *
- * return (
- * <>
- * <div onPointerDown={startDrag} />
- * <motion.div drag="x" dragControls={dragControls} />
- * </>
- * )
- * ```
- *
- * @public
- */
- class DragControls {
- constructor() {
- this.componentControls = new Set();
- }
- /**
- * Subscribe a component's internal `VisualElementDragControls` to the user-facing API.
- *
- * @internal
- */
- subscribe(controls) {
- this.componentControls.add(controls);
- return () => this.componentControls.delete(controls);
- }
- /**
- * Start a drag gesture on every `motion` component that has this set of drag controls
- * passed into it via the `dragControls` prop.
- *
- * ```jsx
- * dragControls.start(e, {
- * snapToCursor: true
- * })
- * ```
- *
- * @param event - PointerEvent
- * @param options - Options
- *
- * @public
- */
- start(event, options) {
- this.componentControls.forEach((controls) => {
- controls.start(event.nativeEvent || event, options);
- });
- }
- }
- const createDragControls = () => new DragControls();
- /**
- * Usually, dragging is initiated by pressing down on a `motion` component with a `drag` prop
- * and moving it. For some use-cases, for instance clicking at an arbitrary point on a video scrubber, we
- * might want to initiate that dragging from a different component than the draggable one.
- *
- * By creating a `dragControls` using the `useDragControls` hook, we can pass this into
- * the draggable component's `dragControls` prop. It exposes a `start` method
- * that can start dragging from pointer events on other components.
- *
- * ```jsx
- * const dragControls = useDragControls()
- *
- * function startDrag(event) {
- * dragControls.start(event, { snapToCursor: true })
- * }
- *
- * return (
- * <>
- * <div onPointerDown={startDrag} />
- * <motion.div drag="x" dragControls={dragControls} />
- * </>
- * )
- * ```
- *
- * @public
- */
- function useDragControls() {
- return create.useConstant(createDragControls);
- }
- /**
- * Checks if a component is a `motion` component.
- */
- function isMotionComponent(component) {
- return (component !== null &&
- typeof component === "object" &&
- create.motionComponentSymbol in component);
- }
- /**
- * Unwraps a `motion` component and returns either a string for `motion.div` or
- * the React component for `motion(Component)`.
- *
- * If the component is not a `motion` component it returns undefined.
- */
- function unwrapMotionComponent(component) {
- if (isMotionComponent(component)) {
- return component[create.motionComponentSymbol];
- }
- return undefined;
- }
- function useInstantLayoutTransition() {
- return startTransition;
- }
- function startTransition(callback) {
- if (!create.rootProjectionNode.current)
- return;
- create.rootProjectionNode.current.isUpdating = false;
- create.rootProjectionNode.current.blockUpdate();
- callback && callback();
- }
- function useResetProjection() {
- const reset = React.useCallback(() => {
- const root = create.rootProjectionNode.current;
- if (!root)
- return;
- root.resetTree();
- }, []);
- return reset;
- }
- /**
- * 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.
- *
- * An index value can be passed to the returned `cycle` function to cycle to a specific index.
- *
- * ```jsx
- * import * as React from "react"
- * import { motion, useCycle } from "framer-motion"
- *
- * export const MyComponent = () => {
- * const [x, cycleX] = useCycle(0, 50, 100)
- *
- * return (
- * <motion.div
- * animate={{ x: x }}
- * onTap={() => cycleX()}
- * />
- * )
- * }
- * ```
- *
- * @param items - items to cycle through
- * @returns [currentState, cycleState]
- *
- * @public
- */
- function useCycle(...items) {
- const index = React.useRef(0);
- const [item, setItem] = React.useState(items[index.current]);
- const runCycle = React.useCallback((next) => {
- index.current =
- typeof next !== "number"
- ? wrap(0, items.length, index.current + 1)
- : next;
- setItem(items[index.current]);
- },
- // The array will change on each call, but by putting items.length at
- // the front of this array, we guarantee the dependency comparison will match up
- // eslint-disable-next-line react-hooks/exhaustive-deps
- [items.length, ...items]);
- return [item, runCycle];
- }
- function useInView(ref, { root, margin, amount, once = false, initial = false, } = {}) {
- const [isInView, setInView] = React.useState(initial);
- React.useEffect(() => {
- if (!ref.current || (once && isInView))
- return;
- const onEnter = () => {
- setInView(true);
- return once ? undefined : () => setInView(false);
- };
- const options = {
- root: (root && root.current) || undefined,
- margin,
- amount,
- };
- return inView(ref.current, onEnter, options);
- }, [root, ref, margin, once, amount]);
- return isInView;
- }
- function useInstantTransition() {
- const [forceUpdate, forcedRenderCount] = useForceUpdate();
- const startInstantLayoutTransition = useInstantLayoutTransition();
- const unlockOnFrameRef = React.useRef(-1);
- React.useEffect(() => {
- /**
- * Unblock after two animation frames, otherwise this will unblock too soon.
- */
- motionDom.frame.postRender(() => motionDom.frame.postRender(() => {
- /**
- * If the callback has been called again after the effect
- * triggered this 2 frame delay, don't unblock animations. This
- * prevents the previous effect from unblocking the current
- * instant transition too soon. This becomes more likely when
- * used in conjunction with React.startTransition().
- */
- if (forcedRenderCount !== unlockOnFrameRef.current)
- return;
- create.instantAnimationState.current = false;
- }));
- }, [forcedRenderCount]);
- return (callback) => {
- startInstantLayoutTransition(() => {
- create.instantAnimationState.current = true;
- forceUpdate();
- callback();
- unlockOnFrameRef.current = forcedRenderCount + 1;
- });
- };
- }
- function disableInstantTransitions() {
- create.instantAnimationState.current = false;
- }
- const appearAnimationStore = new Map();
- const appearComplete = new Map();
- const appearStoreId = (elementId, valueName) => {
- const key = create.transformProps.has(valueName) ? "transform" : valueName;
- return `${elementId}: ${key}`;
- };
- function handoffOptimizedAppearAnimation(elementId, valueName, frame) {
- const storeId = appearStoreId(elementId, valueName);
- const optimisedAnimation = appearAnimationStore.get(storeId);
- if (!optimisedAnimation) {
- return null;
- }
- const { animation, startTime } = optimisedAnimation;
- function cancelAnimation() {
- window.MotionCancelOptimisedAnimation?.(elementId, valueName, frame);
- }
- /**
- * We can cancel the animation once it's finished now that we've synced
- * with Motion.
- *
- * Prefer onfinish over finished as onfinish is backwards compatible with
- * older browsers.
- */
- animation.onfinish = cancelAnimation;
- if (startTime === null || window.MotionHandoffIsComplete?.(elementId)) {
- /**
- * If the startTime is null, this animation is the Paint Ready detection animation
- * and we can cancel it immediately without handoff.
- *
- * Or if we've already handed off the animation then we're now interrupting it.
- * In which case we need to cancel it.
- */
- cancelAnimation();
- return null;
- }
- else {
- return startTime;
- }
- }
- /**
- * A single time to use across all animations to manually set startTime
- * and ensure they're all in sync.
- */
- let startFrameTime;
- /**
- * A dummy animation to detect when Chrome is ready to start
- * painting the page and hold off from triggering the real animation
- * until then. We only need one animation to detect paint ready.
- *
- * https://bugs.chromium.org/p/chromium/issues/detail?id=1406850
- */
- let readyAnimation;
- /**
- * Keep track of animations that were suspended vs cancelled so we
- * can easily resume them when we're done measuring layout.
- */
- const suspendedAnimations = new Set();
- function resumeSuspendedAnimations() {
- suspendedAnimations.forEach((data) => {
- data.animation.play();
- data.animation.startTime = data.startTime;
- });
- suspendedAnimations.clear();
- }
- function startOptimizedAppearAnimation(element, name, keyframes, options, onReady) {
- // Prevent optimised appear animations if Motion has already started animating.
- if (window.MotionIsMounted) {
- return;
- }
- const id = element.dataset[create.optimizedAppearDataId];
- if (!id)
- return;
- window.MotionHandoffAnimation = handoffOptimizedAppearAnimation;
- const storeId = appearStoreId(id, name);
- if (!readyAnimation) {
- readyAnimation = motionDom.startWaapiAnimation(element, name, [keyframes[0], keyframes[0]],
- /**
- * 10 secs is basically just a super-safe duration to give Chrome
- * long enough to get the animation ready.
- */
- { duration: 10000, ease: "linear" });
- appearAnimationStore.set(storeId, {
- animation: readyAnimation,
- startTime: null,
- });
- /**
- * If there's no readyAnimation then there's been no instantiation
- * of handoff animations.
- */
- window.MotionHandoffAnimation = handoffOptimizedAppearAnimation;
- window.MotionHasOptimisedAnimation = (elementId, valueName) => {
- if (!elementId)
- return false;
- /**
- * Keep a map of elementIds that have started animating. We check
- * via ID instead of Element because of hydration errors and
- * pre-hydration checks. We also actively record IDs as they start
- * animating rather than simply checking for data-appear-id as
- * this attrbute might be present but not lead to an animation, for
- * instance if the element's appear animation is on a different
- * breakpoint.
- */
- if (!valueName) {
- return appearComplete.has(elementId);
- }
- const animationId = appearStoreId(elementId, valueName);
- return Boolean(appearAnimationStore.get(animationId));
- };
- window.MotionHandoffMarkAsComplete = (elementId) => {
- if (appearComplete.has(elementId)) {
- appearComplete.set(elementId, true);
- }
- };
- window.MotionHandoffIsComplete = (elementId) => {
- return appearComplete.get(elementId) === true;
- };
- /**
- * We only need to cancel transform animations as
- * they're the ones that will interfere with the
- * layout animation measurements.
- */
- window.MotionCancelOptimisedAnimation = (elementId, valueName, frame, canResume) => {
- const animationId = appearStoreId(elementId, valueName);
- const data = appearAnimationStore.get(animationId);
- if (!data)
- return;
- if (frame && canResume === undefined) {
- /**
- * Wait until the end of the subsequent frame to cancel the animation
- * to ensure we don't remove the animation before the main thread has
- * had a chance to resolve keyframes and render.
- */
- frame.postRender(() => {
- frame.postRender(() => {
- data.animation.cancel();
- });
- });
- }
- else {
- data.animation.cancel();
- }
- if (frame && canResume) {
- suspendedAnimations.add(data);
- frame.render(resumeSuspendedAnimations);
- }
- else {
- appearAnimationStore.delete(animationId);
- /**
- * If there are no more animations left, we can remove the cancel function.
- * This will let us know when we can stop checking for conflicting layout animations.
- */
- if (!appearAnimationStore.size) {
- window.MotionCancelOptimisedAnimation = undefined;
- }
- }
- };
- window.MotionCheckAppearSync = (visualElement, valueName, value) => {
- const appearId = create.getOptimisedAppearId(visualElement);
- if (!appearId)
- return;
- const valueIsOptimised = window.MotionHasOptimisedAnimation?.(appearId, valueName);
- const externalAnimationValue = visualElement.props.values?.[valueName];
- if (!valueIsOptimised || !externalAnimationValue)
- return;
- const removeSyncCheck = value.on("change", (latestValue) => {
- if (externalAnimationValue.get() !== latestValue) {
- window.MotionCancelOptimisedAnimation?.(appearId, valueName);
- removeSyncCheck();
- }
- });
- return removeSyncCheck;
- };
- }
- const startAnimation = () => {
- readyAnimation.cancel();
- const appearAnimation = motionDom.startWaapiAnimation(element, name, keyframes, options);
- /**
- * Record the time of the first started animation. We call performance.now() once
- * here and once in handoff to ensure we're getting
- * close to a frame-locked time. This keeps all animations in sync.
- */
- if (startFrameTime === undefined) {
- startFrameTime = performance.now();
- }
- appearAnimation.startTime = startFrameTime;
- appearAnimationStore.set(storeId, {
- animation: appearAnimation,
- startTime: startFrameTime,
- });
- if (onReady)
- onReady(appearAnimation);
- };
- appearComplete.set(id, false);
- if (readyAnimation.ready) {
- readyAnimation.ready.then(startAnimation).catch(motionUtils.noop);
- }
- else {
- startAnimation();
- }
- }
- const createObject = () => ({});
- class StateVisualElement extends create.VisualElement {
- constructor() {
- super(...arguments);
- this.measureInstanceViewportBox = create.createBox;
- }
- build() { }
- resetTransform() { }
- restoreTransform() { }
- removeValueFromRenderState() { }
- renderInstance() { }
- scrapeMotionValuesFromProps() {
- return createObject();
- }
- getBaseTargetFromProps() {
- return undefined;
- }
- readValueFromInstance(_state, key, options) {
- return options.initialState[key] || 0;
- }
- sortInstanceNodePosition() {
- return 0;
- }
- }
- const useVisualState = create.makeUseVisualState({
- scrapeMotionValuesFromProps: createObject,
- createRenderState: createObject,
- });
- /**
- * This is not an officially supported API and may be removed
- * on any version.
- */
- function useAnimatedState(initialState) {
- const [animationState, setAnimationState] = React.useState(initialState);
- const visualState = useVisualState({}, false);
- const element = create.useConstant(() => {
- return new StateVisualElement({
- props: {
- onUpdate: (v) => {
- setAnimationState({ ...v });
- },
- },
- visualState,
- presenceContext: null,
- }, { initialState });
- });
- React.useLayoutEffect(() => {
- element.mount({});
- return () => element.unmount();
- }, [element]);
- const startAnimation = create.useConstant(() => (animationDefinition) => {
- return create.animateVisualElement(element, animationDefinition);
- });
- return [animationState, startAnimation];
- }
- let id = 0;
- const AnimateSharedLayout = ({ children }) => {
- React__namespace.useEffect(() => {
- motionUtils.invariant(false, "AnimateSharedLayout is deprecated: https://www.framer.com/docs/guide-upgrade/##shared-layout-animations");
- }, []);
- return (jsxRuntime.jsx(LayoutGroup, { id: create.useConstant(() => `asl-${id++}`), children: children }));
- };
- // Keep things reasonable and avoid scale: Infinity. In practise we might need
- // to add another value, opacity, that could interpolate scaleX/Y [0,0.01] => [0,1]
- // to simply hide content at unreasonable scales.
- const maxScale = 100000;
- const invertScale = (scale) => scale > 0.001 ? 1 / scale : maxScale;
- let hasWarned = false;
- /**
- * Returns a `MotionValue` each for `scaleX` and `scaleY` that update with the inverse
- * of their respective parent scales.
- *
- * This is useful for undoing the distortion of content when scaling a parent component.
- *
- * By default, `useInvertedScale` will automatically fetch `scaleX` and `scaleY` from the nearest parent.
- * By passing other `MotionValue`s in as `useInvertedScale({ scaleX, scaleY })`, it will invert the output
- * of those instead.
- *
- * ```jsx
- * const MyComponent = () => {
- * const { scaleX, scaleY } = useInvertedScale()
- * return <motion.div style={{ scaleX, scaleY }} />
- * }
- * ```
- *
- * @deprecated
- */
- function useInvertedScale(scale) {
- let parentScaleX = useMotionValue(1);
- let parentScaleY = useMotionValue(1);
- const { visualElement } = React.useContext(create.MotionContext);
- motionUtils.invariant(!!(scale || visualElement), "If no scale values are provided, useInvertedScale must be used within a child of another motion component.");
- motionUtils.warning(hasWarned, "useInvertedScale is deprecated and will be removed in 3.0. Use the layout prop instead.");
- hasWarned = true;
- if (scale) {
- parentScaleX = scale.scaleX || parentScaleX;
- parentScaleY = scale.scaleY || parentScaleY;
- }
- else if (visualElement) {
- parentScaleX = visualElement.getValue("scaleX", 1);
- parentScaleY = visualElement.getValue("scaleY", 1);
- }
- const scaleX = useTransform(parentScaleX, invertScale);
- const scaleY = useTransform(parentScaleY, invertScale);
- return { scaleX, scaleY };
- }
- exports.AcceleratedAnimation = create.AcceleratedAnimation;
- exports.FlatTree = create.FlatTree;
- exports.LayoutGroupContext = create.LayoutGroupContext;
- exports.MotionConfigContext = create.MotionConfigContext;
- exports.MotionContext = create.MotionContext;
- exports.PresenceContext = create.PresenceContext;
- exports.SwitchLayoutGroupContext = create.SwitchLayoutGroupContext;
- exports.VisualElement = create.VisualElement;
- exports.addPointerEvent = create.addPointerEvent;
- exports.addPointerInfo = create.addPointerInfo;
- exports.addScaleCorrector = create.addScaleCorrector;
- exports.animateValue = create.animateValue;
- exports.animateVisualElement = create.animateVisualElement;
- exports.animations = create.animations;
- exports.anticipate = create.anticipate;
- exports.backIn = create.backIn;
- exports.backInOut = create.backInOut;
- exports.backOut = create.backOut;
- exports.buildTransform = create.buildTransform;
- exports.calcLength = create.calcLength;
- exports.circIn = create.circIn;
- exports.circInOut = create.circInOut;
- exports.circOut = create.circOut;
- exports.clamp = create.clamp;
- exports.color = create.color;
- exports.complex = create.complex;
- exports.createBox = create.createBox;
- exports.createRendererMotionComponent = create.createRendererMotionComponent;
- exports.cubicBezier = create.cubicBezier;
- exports.delay = create.delay;
- exports.distance = create.distance;
- exports.distance2D = create.distance2D;
- exports.easeIn = create.easeIn;
- exports.easeInOut = create.easeInOut;
- exports.easeOut = create.easeOut;
- exports.filterProps = create.filterProps;
- exports.findSpring = create.findSpring;
- exports.inertia = create.inertia;
- exports.interpolate = create.interpolate;
- exports.isBrowser = create.isBrowser;
- exports.isMotionValue = create.isMotionValue;
- exports.isValidMotionProp = create.isValidMotionProp;
- exports.keyframes = create.keyframes;
- exports.makeUseVisualState = create.makeUseVisualState;
- exports.mirrorEasing = create.mirrorEasing;
- exports.mix = create.mix;
- exports.optimizedAppearDataAttribute = create.optimizedAppearDataAttribute;
- exports.pipe = create.pipe;
- exports.px = create.px;
- exports.resolveMotionValue = create.resolveMotionValue;
- exports.reverseEasing = create.reverseEasing;
- exports.spring = create.spring;
- exports.useIsPresent = create.useIsPresent;
- exports.useIsomorphicLayoutEffect = create.useIsomorphicLayoutEffect;
- exports.usePresence = create.usePresence;
- exports.visualElementStore = create.visualElementStore;
- Object.defineProperty(exports, "MotionValue", {
- enumerable: true,
- get: function () { return motionDom.MotionValue; }
- });
- Object.defineProperty(exports, "cancelFrame", {
- enumerable: true,
- get: function () { return motionDom.cancelFrame; }
- });
- Object.defineProperty(exports, "cancelSync", {
- enumerable: true,
- get: function () { return motionDom.cancelSync; }
- });
- Object.defineProperty(exports, "frame", {
- enumerable: true,
- get: function () { return motionDom.frame; }
- });
- Object.defineProperty(exports, "frameData", {
- enumerable: true,
- get: function () { return motionDom.frameData; }
- });
- Object.defineProperty(exports, "hover", {
- enumerable: true,
- get: function () { return motionDom.hover; }
- });
- Object.defineProperty(exports, "isDragActive", {
- enumerable: true,
- get: function () { return motionDom.isDragActive; }
- });
- Object.defineProperty(exports, "motionValue", {
- enumerable: true,
- get: function () { return motionDom.motionValue; }
- });
- Object.defineProperty(exports, "press", {
- enumerable: true,
- get: function () { return motionDom.press; }
- });
- Object.defineProperty(exports, "sync", {
- enumerable: true,
- get: function () { return motionDom.sync; }
- });
- Object.defineProperty(exports, "time", {
- enumerable: true,
- get: function () { return motionDom.time; }
- });
- Object.defineProperty(exports, "MotionGlobalConfig", {
- enumerable: true,
- get: function () { return motionUtils.MotionGlobalConfig; }
- });
- Object.defineProperty(exports, "invariant", {
- enumerable: true,
- get: function () { return motionUtils.invariant; }
- });
- Object.defineProperty(exports, "noop", {
- enumerable: true,
- get: function () { return motionUtils.noop; }
- });
- Object.defineProperty(exports, "progress", {
- enumerable: true,
- get: function () { return motionUtils.progress; }
- });
- exports.AnimatePresence = AnimatePresence;
- exports.AnimateSharedLayout = AnimateSharedLayout;
- exports.DeprecatedLayoutGroupContext = DeprecatedLayoutGroupContext;
- exports.DragControls = DragControls;
- exports.LayoutGroup = LayoutGroup;
- exports.LazyMotion = LazyMotion;
- exports.MotionConfig = MotionConfig;
- exports.Reorder = namespace;
- exports.WillChangeMotionValue = WillChangeMotionValue;
- exports.animate = animate;
- exports.animateMini = animateMini;
- exports.animationControls = animationControls;
- exports.createScopedAnimate = createScopedAnimate;
- exports.disableInstantTransitions = disableInstantTransitions;
- exports.domAnimation = domAnimation;
- exports.domMax = domMax;
- exports.domMin = domMin;
- exports.inView = inView;
- exports.isMotionComponent = isMotionComponent;
- exports.m = m;
- exports.motion = motion;
- exports.scroll = scroll;
- exports.scrollInfo = scrollInfo;
- exports.stagger = stagger;
- exports.startOptimizedAppearAnimation = startOptimizedAppearAnimation;
- exports.steps = steps;
- exports.transform = transform;
- exports.unwrapMotionComponent = unwrapMotionComponent;
- exports.useAnimate = useAnimate;
- exports.useAnimateMini = useAnimateMini;
- exports.useAnimation = useAnimation;
- exports.useAnimationControls = useAnimationControls;
- exports.useAnimationFrame = useAnimationFrame;
- exports.useCycle = useCycle;
- exports.useDeprecatedAnimatedState = useAnimatedState;
- exports.useDeprecatedInvertedScale = useInvertedScale;
- exports.useDomEvent = useDomEvent;
- exports.useDragControls = useDragControls;
- exports.useElementScroll = useElementScroll;
- exports.useForceUpdate = useForceUpdate;
- exports.useInView = useInView;
- exports.useInstantLayoutTransition = useInstantLayoutTransition;
- exports.useInstantTransition = useInstantTransition;
- exports.useMotionTemplate = useMotionTemplate;
- exports.useMotionValue = useMotionValue;
- exports.useMotionValueEvent = useMotionValueEvent;
- exports.usePresenceData = usePresenceData;
- exports.useReducedMotion = useReducedMotion;
- exports.useReducedMotionConfig = useReducedMotionConfig;
- exports.useResetProjection = useResetProjection;
- exports.useScroll = useScroll;
- exports.useSpring = useSpring;
- exports.useTime = useTime;
- exports.useTransform = useTransform;
- exports.useUnmountEffect = useUnmountEffect;
- exports.useVelocity = useVelocity;
- exports.useViewportScroll = useViewportScroll;
- exports.useWillChange = useWillChange;
- exports.wrap = wrap;
|