UNPKG

@vimeo/iris

Version:
1,432 lines (1,365 loc) 298 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var tslib_es6 = require('../../tslib.es6-3ec409b7.js'); var React = require('react'); var useId = require('../../use-id-29bc1b38.js'); require('../../es.typed-array.set-5ee45ede.js'); var utils_general_generateUID = require('../../utils/general/generateUID.js'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } function _interopNamespace(e) { if (e && e.__esModule) return 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__*/_interopNamespace(React); var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var createDefinition = function (propNames) { return { isEnabled: function (props) { return propNames.some(function (name) { return !!props[name]; }); } }; }; var featureDefinitions = { measureLayout: createDefinition(["layout", "layoutId", "drag"]), animation: createDefinition(["animate", "exit", "variants", "whileHover", "whileTap", "whileFocus", "whileDrag", "whileInView"]), exit: createDefinition(["exit"]), drag: createDefinition(["drag", "dragControls"]), focus: createDefinition(["whileFocus"]), hover: createDefinition(["whileHover", "onHoverStart", "onHoverEnd"]), tap: createDefinition(["whileTap", "onTap", "onTapStart", "onTapCancel"]), pan: createDefinition(["onPan", "onPanStart", "onPanSessionStart", "onPanEnd"]), inView: createDefinition(["whileInView", "onViewportEnter", "onViewportLeave"]) }; function loadFeatures(features) { for (var key in features) { if (features[key] === null) continue; if (key === "projectionNodeConstructor") { featureDefinitions.projectionNodeConstructor = features[key]; } else { featureDefinitions[key].Component = features[key]; } } } var warning = function () {}; var invariant = function () {}; if (process.env.NODE_ENV !== 'production') { warning = function (check, message) { if (!check && typeof console !== 'undefined') { console.warn(message); } }; invariant = function (check, message) { if (!check) { throw new Error(message); } }; } var LazyContext = /*#__PURE__*/React.createContext({ strict: false }); var featureNames = Object.keys(featureDefinitions); var numFeatures = featureNames.length; /** * Load features via renderless components based on the provided MotionProps. */ function useFeatures(props, visualElement, preloadedFeatures) { var features = []; var lazyContext = React.useContext(LazyContext); if (!visualElement) return null; /** * If we're in development mode, check to make sure we're not rendering a motion component * as a child of LazyMotion, as this will break the file-size benefits of using it. */ if (useId.env !== "production" && preloadedFeatures && lazyContext.strict) { invariant(false, "You have rendered a `motion` component within a `LazyMotion` component. This will break tree shaking. Import and render a `m` component instead."); } for (var i = 0; i < numFeatures; i++) { var name_1 = featureNames[i]; var _a = featureDefinitions[name_1], isEnabled = _a.isEnabled, Component = _a.Component; /** * It might be possible in the future to use this moment to * dynamically request functionality. In initial tests this * was producing a lot of duplication amongst bundles. */ if (isEnabled(props) && Component) { features.push( /*#__PURE__*/React__namespace.createElement(Component, tslib_es6.__assign({ key: name_1 }, props, { visualElement: visualElement }))); } } return features; } /** * @public */ var MotionConfigContext = /*#__PURE__*/React.createContext({ transformPagePoint: function (p) { return p; }, isStatic: false, reducedMotion: "never" }); var MotionContext = /*#__PURE__*/React.createContext({}); function useVisualElementContext() { return React.useContext(MotionContext).visualElement; } // Does this device prefer reduced motion? Returns `null` server-side. var prefersReducedMotion = { current: null }; var hasDetected = false; function initPrefersReducedMotion() { hasDetected = true; if (!useId.isBrowser) return; if (window.matchMedia) { var motionMediaQuery_1 = window.matchMedia("(prefers-reduced-motion)"); var setReducedMotionPreferences = function () { return prefersReducedMotion.current = motionMediaQuery_1.matches; }; motionMediaQuery_1.addListener(setReducedMotionPreferences); setReducedMotionPreferences(); } else { prefersReducedMotion.current = false; } } /** * 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 */ !hasDetected && initPrefersReducedMotion(); var _a = tslib_es6.__read(React.useState(prefersReducedMotion.current), 1), shouldReduceMotion = _a[0]; /** * TODO See if people miss automatically updating shouldReduceMotion setting */ return shouldReduceMotion; } function useReducedMotionConfig() { var reducedMotionPreference = useReducedMotion(); var reducedMotion = React.useContext(MotionConfigContext).reducedMotion; if (reducedMotion === "never") { return false; } else if (reducedMotion === "always") { return true; } else { return reducedMotionPreference; } } function useVisualElement(Component, visualState, props, createVisualElement) { var lazyContext = React.useContext(LazyContext); var parent = useVisualElementContext(); var presenceContext = React.useContext(useId.PresenceContext); var shouldReduceMotion = useReducedMotionConfig(); var visualElementRef = React.useRef(undefined); /** * If we haven't preloaded a renderer, check to see if we have one lazy-loaded */ if (!createVisualElement) createVisualElement = lazyContext.renderer; if (!visualElementRef.current && createVisualElement) { visualElementRef.current = createVisualElement(Component, { visualState: visualState, parent: parent, props: props, presenceId: presenceContext === null || presenceContext === void 0 ? void 0 : presenceContext.id, blockInitialAnimation: (presenceContext === null || presenceContext === void 0 ? void 0 : presenceContext.initial) === false, shouldReduceMotion: shouldReduceMotion }); } var visualElement = visualElementRef.current; useId.useIsomorphicLayoutEffect(function () { visualElement === null || visualElement === void 0 ? void 0 : visualElement.syncRender(); }); React.useEffect(function () { var _a; (_a = visualElement === null || visualElement === void 0 ? void 0 : visualElement.animationState) === null || _a === void 0 ? void 0 : _a.animateChanges(); }); useId.useIsomorphicLayoutEffect(function () { return function () { return visualElement === null || visualElement === void 0 ? void 0 : visualElement.notifyUnmount(); }; }, []); return visualElement; } function isRefObject(ref) { return typeof ref === "object" && Object.prototype.hasOwnProperty.call(ref, "current"); } /** * Creates a ref function that, when called, hydrates the provided * external ref and VisualElement. */ function useMotionRef(visualState, visualElement, externalRef) { return React.useCallback(function (instance) { var _a; instance && ((_a = visualState.mount) === null || _a === void 0 ? void 0 : _a.call(visualState, instance)); if (visualElement) { instance ? visualElement.mount(instance) : visualElement.unmount(); } if (externalRef) { if (typeof externalRef === "function") { externalRef(instance); } else if (isRefObject(externalRef)) { externalRef.current = instance; } } }, /** * Only pass a new ref callback to React if we've received a visual element * factory. Otherwise we'll be mounting/remounting every time externalRef * or other dependencies change. */ [visualElement]); } /** * Decides if the supplied variable is an array of variant labels */ function isVariantLabels(v) { return Array.isArray(v); } /** * Decides if the supplied variable is variant label */ function isVariantLabel(v) { return typeof v === "string" || isVariantLabels(v); } /** * Creates an object containing the latest state of every MotionValue on a VisualElement */ function getCurrent(visualElement) { var current = {}; visualElement.forEachValue(function (value, key) { return current[key] = value.get(); }); return current; } /** * Creates an object containing the latest velocity of every MotionValue on a VisualElement */ function getVelocity$1(visualElement) { var velocity = {}; visualElement.forEachValue(function (value, key) { return velocity[key] = value.getVelocity(); }); return velocity; } function resolveVariantFromProps(props, definition, custom, currentValues, currentVelocity) { var _a; if (currentValues === void 0) { currentValues = {}; } if (currentVelocity === void 0) { currentVelocity = {}; } /** * If the variant definition is a function, resolve. */ if (typeof definition === "function") { definition = definition(custom !== null && custom !== void 0 ? custom : props.custom, currentValues, currentVelocity); } /** * If the variant definition is a variant label, or * the function returned a variant label, resolve. */ if (typeof definition === "string") { definition = (_a = props.variants) === null || _a === void 0 ? void 0 : _a[definition]; } /** * At this point we've resolved both functions and variant labels, * but the resolved variant label might itself have been a function. * If so, resolve. This can only have returned a valid target object. */ if (typeof definition === "function") { definition = definition(custom !== null && custom !== void 0 ? custom : props.custom, currentValues, currentVelocity); } return definition; } function resolveVariant(visualElement, definition, custom) { var props = visualElement.getProps(); return resolveVariantFromProps(props, definition, custom !== null && custom !== void 0 ? custom : props.custom, getCurrent(visualElement), getVelocity$1(visualElement)); } function checkIfControllingVariants(props) { var _a; return typeof ((_a = props.animate) === null || _a === void 0 ? void 0 : _a.start) === "function" || isVariantLabel(props.initial) || isVariantLabel(props.animate) || isVariantLabel(props.whileHover) || isVariantLabel(props.whileDrag) || isVariantLabel(props.whileTap) || isVariantLabel(props.whileFocus) || isVariantLabel(props.exit); } function checkIfVariantNode(props) { return Boolean(checkIfControllingVariants(props) || props.variants); } function getCurrentTreeVariants(props, context) { if (checkIfControllingVariants(props)) { var initial = props.initial, animate = props.animate; return { initial: initial === false || isVariantLabel(initial) ? initial : undefined, animate: isVariantLabel(animate) ? animate : undefined }; } return props.inherit !== false ? context : {}; } function useCreateMotionContext(props) { var _a = getCurrentTreeVariants(props, React.useContext(MotionContext)), initial = _a.initial, animate = _a.animate; return React.useMemo(function () { return { initial: initial, animate: animate }; }, [variantLabelsAsDependency(initial), variantLabelsAsDependency(animate)]); } function variantLabelsAsDependency(prop) { return Array.isArray(prop) ? prop.join(" ") : prop; } /** * This should only ever be modified on the client otherwise it'll * persist through server requests. If we need instanced states we * could lazy-init via root. */ var globalProjectionState = { /** * Global flag as to whether the tree has animated since the last time * we resized the window */ hasAnimatedSinceResize: true, /** * We set this to true once, on the first update. Any nodes added to the tree beyond that * update will be given a `data-projection-id` attribute. */ hasEverUpdated: false }; var id = 1; function useProjectionId() { return useId.useConstant(function () { if (globalProjectionState.hasEverUpdated) { return id++; } }); } /** * Internal, exported only for usage in Framer */ var SwitchLayoutGroupContext = /*#__PURE__*/React.createContext({}); function useProjection(projectionId, _a, visualElement, ProjectionNodeConstructor) { var _b; var layoutId = _a.layoutId, layout = _a.layout, drag = _a.drag, dragConstraints = _a.dragConstraints, layoutScroll = _a.layoutScroll; var initialPromotionConfig = React.useContext(SwitchLayoutGroupContext); if (!ProjectionNodeConstructor || !visualElement || (visualElement === null || visualElement === void 0 ? void 0 : visualElement.projection)) { return; } visualElement.projection = new ProjectionNodeConstructor(projectionId, visualElement.getLatestValues(), (_b = visualElement.parent) === null || _b === void 0 ? void 0 : _b.projection); visualElement.projection.setOptions({ layoutId: layoutId, layout: layout, alwaysMeasureLayout: Boolean(drag) || dragConstraints && isRefObject(dragConstraints), visualElement: visualElement, scheduleRender: function () { return visualElement.scheduleRender(); }, /** * TODO: Update options in an effect. This could be tricky as it'll be too late * to update by the time layout animations run. * We also need to fix this safeToRemove by linking it up to the one returned by usePresence, * ensuring it gets called if there's no potential layout animations. * */ animationType: typeof layout === "string" ? layout : "both", initialPromotionConfig: initialPromotionConfig, layoutScroll: layoutScroll }); } var VisualElementHandler = /** @class */function (_super) { tslib_es6.__extends(VisualElementHandler, _super); function VisualElementHandler() { return _super !== null && _super.apply(this, arguments) || this; } /** * Update visual element props as soon as we know this update is going to be commited. */ VisualElementHandler.prototype.getSnapshotBeforeUpdate = function () { this.updateProps(); return null; }; VisualElementHandler.prototype.componentDidUpdate = function () {}; VisualElementHandler.prototype.updateProps = function () { var _a = this.props, visualElement = _a.visualElement, props = _a.props; if (visualElement) visualElement.setProps(props); }; VisualElementHandler.prototype.render = function () { return this.props.children; }; return VisualElementHandler; }(React__default["default"].Component); /** * Create a `motion` component. * * This function accepts a Component argument, which can be either a string (ie "div" * for `motion.div`), or an actual React component. * * Alongside this is a config option which provides a way of rendering the provided * component "offline", or outside the React render cycle. */ function createMotionComponent(_a) { var preloadedFeatures = _a.preloadedFeatures, createVisualElement = _a.createVisualElement, projectionNodeConstructor = _a.projectionNodeConstructor, useRender = _a.useRender, useVisualState = _a.useVisualState, Component = _a.Component; preloadedFeatures && loadFeatures(preloadedFeatures); function MotionComponent(props, externalRef) { var layoutId = useLayoutId(props); props = tslib_es6.__assign(tslib_es6.__assign({}, props), { layoutId: layoutId }); /** * If we're rendering in a static environment, we only visually update the component * as a result of a React-rerender rather than interactions or animations. This * means we don't need to load additional memory structures like VisualElement, * or any gesture/animation features. */ var config = React.useContext(MotionConfigContext); var features = null; var context = useCreateMotionContext(props); /** * Create a unique projection ID for this component. If a new component is added * during a layout animation we'll use this to query the DOM and hydrate its ref early, allowing * us to measure it as soon as any layout effect flushes pending layout animations. * * Performance note: It'd be better not to have to search the DOM for these elements. * For newly-entering components it could be enough to only correct treeScale, in which * case we could mount in a scale-correction mode. This wouldn't be enough for * shared element transitions however. Perhaps for those we could revert to a root node * that gets forceRendered and layout animations are triggered on its layout effect. */ var projectionId = config.isStatic ? undefined : useProjectionId(); /** * */ var visualState = useVisualState(props, config.isStatic); if (!config.isStatic && useId.isBrowser) { /** * Create a VisualElement for this component. A VisualElement provides a common * interface to renderer-specific APIs (ie DOM/Three.js etc) as well as * providing a way of rendering to these APIs outside of the React render loop * for more performant animations and interactions */ context.visualElement = useVisualElement(Component, visualState, tslib_es6.__assign(tslib_es6.__assign({}, config), props), createVisualElement); useProjection(projectionId, props, context.visualElement, projectionNodeConstructor || featureDefinitions.projectionNodeConstructor); /** * Load Motion gesture and animation features. These are rendered as renderless * components so each feature can optionally make use of React lifecycle methods. */ features = useFeatures(props, context.visualElement, preloadedFeatures); } /** * The mount order and hierarchy is specific to ensure our element ref * is hydrated by the time features fire their effects. */ return /*#__PURE__*/React__namespace.createElement(VisualElementHandler, { visualElement: context.visualElement, props: tslib_es6.__assign(tslib_es6.__assign({}, config), props) }, features, /*#__PURE__*/React__namespace.createElement(MotionContext.Provider, { value: context }, useRender(Component, props, projectionId, useMotionRef(visualState, context.visualElement, externalRef), visualState, config.isStatic, context.visualElement))); } return /*#__PURE__*/React.forwardRef(MotionComponent); } function useLayoutId(_a) { var _b; var layoutId = _a.layoutId; var layoutGroupId = (_b = React.useContext(useId.LayoutGroupContext)) === null || _b === void 0 ? void 0 : _b.id; return layoutGroupId && layoutId !== undefined ? layoutGroupId + "-" + layoutId : layoutId; } /** * We keep these listed seperately as we use the lowercase tag names as part * of the runtime bundle to detect SVG components */ var lowercaseSVGElements = ["animate", "circle", "defs", "desc", "ellipse", "g", "image", "line", "filter", "marker", "mask", "metadata", "path", "pattern", "polygon", "polyline", "rect", "stop", "svg", "switch", "symbol", "text", "tspan", "use", "view"]; function isSVGComponent(Component) { if ( /** * If it's not a string, it's a custom React component. Currently we only support * HTML custom React components. */ typeof Component !== "string" || /** * If it contains a dash, the element is a custom HTML webcomponent. */ Component.includes("-")) { return false; } else if ( /** * If it's in our list of lowercase SVG tags, it's an SVG component */ lowercaseSVGElements.indexOf(Component) > -1 || /** * If it contains a capital letter, it's an SVG component */ /[A-Z]/.test(Component)) { return true; } return false; } var scaleCorrectors = {}; function addScaleCorrector(correctors) { Object.assign(scaleCorrectors, correctors); } /** * A list of all transformable axes. We'll use this list to generated a version * of each axes for each transform. */ var transformAxes = ["", "X", "Y", "Z"]; /** * An ordered array of each transformable value. By default, transform values * will be sorted to this order. */ var order = ["translate", "scale", "rotate", "skew"]; /** * Generate a list of every possible transform key. */ var transformProps = ["transformPerspective", "x", "y", "z"]; order.forEach(function (operationKey) { return transformAxes.forEach(function (axesKey) { return transformProps.push(operationKey + axesKey); }); }); /** * A function to use with Array.sort to sort transform keys by their default order. */ function sortTransformProps(a, b) { return transformProps.indexOf(a) - transformProps.indexOf(b); } /** * A quick lookup for transform props. */ var transformPropSet = new Set(transformProps); function isTransformProp(key) { return transformPropSet.has(key); } /** * A quick lookup for transform origin props */ var transformOriginProps = new Set(["originX", "originY", "originZ"]); function isTransformOriginProp(key) { return transformOriginProps.has(key); } function isForcedMotionValue(key, _a) { var layout = _a.layout, layoutId = _a.layoutId; return isTransformProp(key) || isTransformOriginProp(key) || (layout || layoutId !== undefined) && (!!scaleCorrectors[key] || key === "opacity"); } var isMotionValue = function (value) { return Boolean(value !== null && typeof value === "object" && value.getVelocity); }; var translateAlias = { x: "translateX", y: "translateY", z: "translateZ", transformPerspective: "perspective" }; /** * Build a CSS transform style from individual x/y/scale etc properties. * * This outputs with a default order of transforms/scales/rotations, this can be customised by * providing a transformTemplate function. */ function buildTransform(_a, _b, transformIsDefault, transformTemplate) { var transform = _a.transform, transformKeys = _a.transformKeys; var _c = _b.enableHardwareAcceleration, enableHardwareAcceleration = _c === void 0 ? true : _c, _d = _b.allowTransformNone, allowTransformNone = _d === void 0 ? true : _d; // The transform string we're going to build into. var transformString = ""; // Transform keys into their default order - this will determine the output order. transformKeys.sort(sortTransformProps); // Track whether the defined transform has a defined z so we don't add a // second to enable hardware acceleration var transformHasZ = false; // Loop over each transform and build them into transformString var numTransformKeys = transformKeys.length; for (var i = 0; i < numTransformKeys; i++) { var key = transformKeys[i]; transformString += "".concat(translateAlias[key] || key, "(").concat(transform[key], ") "); if (key === "z") transformHasZ = true; } if (!transformHasZ && enableHardwareAcceleration) { transformString += "translateZ(0)"; } else { transformString = transformString.trim(); } // If we have a custom `transform` template, pass our transform values and // generated transformString to that before returning if (transformTemplate) { transformString = transformTemplate(transform, transformIsDefault ? "" : transformString); } else if (allowTransformNone && transformIsDefault) { transformString = "none"; } return transformString; } /** * Build a transformOrigin style. Uses the same defaults as the browser for * undefined origins. */ function buildTransformOrigin(_a) { var _b = _a.originX, originX = _b === void 0 ? "50%" : _b, _c = _a.originY, originY = _c === void 0 ? "50%" : _c, _d = _a.originZ, originZ = _d === void 0 ? 0 : _d; return "".concat(originX, " ").concat(originY, " ").concat(originZ); } /** * Returns true if the provided key is a CSS variable */ function isCSSVariable$1(key) { return key.startsWith("--"); } /** * Provided a value and a ValueType, returns the value as that value type. */ var getValueAsType = function (value, type) { return type && typeof value === "number" ? type.transform(value) : value; }; const clamp$1 = (min, max) => v => Math.max(Math.min(v, max), min); const sanitize = v => v % 1 ? Number(v.toFixed(5)) : v; const floatRegex = /(-)?([\d]*\.?[\d])+/g; const colorRegex = /(#[0-9a-f]{6}|#[0-9a-f]{3}|#(?:[0-9a-f]{2}){2,4}|(rgb|hsl)a?\((-?[\d\.]+%?[,\s]+){2,3}\s*\/*\s*[\d\.]+%?\))/gi; const singleColorRegex = /^(#[0-9a-f]{3}|#(?:[0-9a-f]{2}){2,4}|(rgb|hsl)a?\((-?[\d\.]+%?[,\s]+){2,3}\s*\/*\s*[\d\.]+%?\))$/i; function isString(v) { return typeof v === 'string'; } const number = { test: v => typeof v === 'number', parse: parseFloat, transform: v => v }; const alpha = Object.assign(Object.assign({}, number), { transform: clamp$1(0, 1) }); const scale = Object.assign(Object.assign({}, number), { default: 1 }); const createUnitType = unit => ({ test: v => isString(v) && v.endsWith(unit) && v.split(' ').length === 1, parse: parseFloat, transform: v => `${v}${unit}` }); const degrees = createUnitType('deg'); const percent = createUnitType('%'); const px = createUnitType('px'); const vh = createUnitType('vh'); const vw = createUnitType('vw'); const progressPercentage = Object.assign(Object.assign({}, percent), { parse: v => percent.parse(v) / 100, transform: v => percent.transform(v * 100) }); const isColorString = (type, testProp) => v => { return Boolean(isString(v) && singleColorRegex.test(v) && v.startsWith(type) || testProp && Object.prototype.hasOwnProperty.call(v, testProp)); }; const splitColor = (aName, bName, cName) => v => { if (!isString(v)) return v; const [a, b, c, alpha] = v.match(floatRegex); return { [aName]: parseFloat(a), [bName]: parseFloat(b), [cName]: parseFloat(c), alpha: alpha !== undefined ? parseFloat(alpha) : 1 }; }; const hsla = { test: isColorString('hsl', 'hue'), parse: splitColor('hue', 'saturation', 'lightness'), transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => { return 'hsla(' + Math.round(hue) + ', ' + percent.transform(sanitize(saturation)) + ', ' + percent.transform(sanitize(lightness)) + ', ' + sanitize(alpha.transform(alpha$1)) + ')'; } }; const clampRgbUnit = clamp$1(0, 255); const rgbUnit = Object.assign(Object.assign({}, number), { transform: v => Math.round(clampRgbUnit(v)) }); const rgba = { test: isColorString('rgb', 'red'), parse: splitColor('red', 'green', 'blue'), transform: ({ red, green, blue, alpha: alpha$1 = 1 }) => 'rgba(' + rgbUnit.transform(red) + ', ' + rgbUnit.transform(green) + ', ' + rgbUnit.transform(blue) + ', ' + sanitize(alpha.transform(alpha$1)) + ')' }; function parseHex(v) { let r = ''; let g = ''; let b = ''; let a = ''; if (v.length > 5) { r = v.substr(1, 2); g = v.substr(3, 2); b = v.substr(5, 2); a = v.substr(7, 2); } else { r = v.substr(1, 1); g = v.substr(2, 1); b = v.substr(3, 1); a = v.substr(4, 1); r += r; g += g; b += b; a += a; } return { red: parseInt(r, 16), green: parseInt(g, 16), blue: parseInt(b, 16), alpha: a ? parseInt(a, 16) / 255 : 1 }; } const hex = { test: isColorString('#'), parse: parseHex, transform: rgba.transform }; const color = { test: v => rgba.test(v) || hex.test(v) || hsla.test(v), parse: v => { if (rgba.test(v)) { return rgba.parse(v); } else if (hsla.test(v)) { return hsla.parse(v); } else { return hex.parse(v); } }, transform: v => { return isString(v) ? v : v.hasOwnProperty('red') ? rgba.transform(v) : hsla.transform(v); } }; const colorToken = '${c}'; const numberToken = '${n}'; function test(v) { var _a, _b, _c, _d; return isNaN(v) && isString(v) && ((_b = (_a = v.match(floatRegex)) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) + ((_d = (_c = v.match(colorRegex)) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0) > 0; } function analyse$1(v) { if (typeof v === 'number') v = `${v}`; const values = []; let numColors = 0; const colors = v.match(colorRegex); if (colors) { numColors = colors.length; v = v.replace(colorRegex, colorToken); values.push(...colors.map(color.parse)); } const numbers = v.match(floatRegex); if (numbers) { v = v.replace(floatRegex, numberToken); values.push(...numbers.map(number.parse)); } return { values, numColors, tokenised: v }; } function parse(v) { return analyse$1(v).values; } function createTransformer(v) { const { values, numColors, tokenised } = analyse$1(v); const numValues = values.length; return v => { let output = tokenised; for (let i = 0; i < numValues; i++) { output = output.replace(i < numColors ? colorToken : numberToken, i < numColors ? color.transform(v[i]) : sanitize(v[i])); } return output; }; } const convertNumbersToZero = v => typeof v === 'number' ? 0 : v; function getAnimatableNone$1(v) { const parsed = parse(v); const transformer = createTransformer(v); return transformer(parsed.map(convertNumbersToZero)); } const complex = { test, parse, createTransformer, getAnimatableNone: getAnimatableNone$1 }; const maxDefaults = new Set(['brightness', 'contrast', 'saturate', 'opacity']); function applyDefaultFilter(v) { let [name, value] = v.slice(0, -1).split('('); if (name === 'drop-shadow') return v; const [number] = value.match(floatRegex) || []; if (!number) return v; const unit = value.replace(number, ''); let defaultValue = maxDefaults.has(name) ? 1 : 0; if (number !== value) defaultValue *= 100; return name + '(' + defaultValue + unit + ')'; } const functionRegex = /([a-z-]*)\(.*?\)/g; const filter = Object.assign(Object.assign({}, complex), { getAnimatableNone: v => { const functions = v.match(functionRegex); return functions ? functions.map(applyDefaultFilter).join(' ') : v; } }); var int = tslib_es6.__assign(tslib_es6.__assign({}, number), { transform: Math.round }); var numberValueTypes = { // Border props borderWidth: px, borderTopWidth: px, borderRightWidth: px, borderBottomWidth: px, borderLeftWidth: px, borderRadius: px, radius: px, borderTopLeftRadius: px, borderTopRightRadius: px, borderBottomRightRadius: px, borderBottomLeftRadius: px, // Positioning props width: px, maxWidth: px, height: px, maxHeight: px, size: px, top: px, right: px, bottom: px, left: px, // Spacing props padding: px, paddingTop: px, paddingRight: px, paddingBottom: px, paddingLeft: px, margin: px, marginTop: px, marginRight: px, marginBottom: px, marginLeft: px, // Transform props rotate: degrees, rotateX: degrees, rotateY: degrees, rotateZ: degrees, scale: scale, scaleX: scale, scaleY: scale, scaleZ: scale, skew: degrees, skewX: degrees, skewY: degrees, distance: px, translateX: px, translateY: px, translateZ: px, x: px, y: px, z: px, perspective: px, transformPerspective: px, opacity: alpha, originX: progressPercentage, originY: progressPercentage, originZ: px, // Misc zIndex: int, // SVG fillOpacity: alpha, strokeOpacity: alpha, numOctaves: int }; function buildHTMLStyles(state, latestValues, options, transformTemplate) { var _a; var style = state.style, vars = state.vars, transform = state.transform, transformKeys = state.transformKeys, transformOrigin = state.transformOrigin; // Empty the transformKeys array. As we're throwing out refs to its items // this might not be as cheap as suspected. Maybe using the array as a buffer // with a manual incrementation would be better. transformKeys.length = 0; // Track whether we encounter any transform or transformOrigin values. var hasTransform = false; var hasTransformOrigin = false; // Does the calculated transform essentially equal "none"? var transformIsNone = true; /** * Loop over all our latest animated values and decide whether to handle them * as a style or CSS variable. * * Transforms and transform origins are kept seperately for further processing. */ for (var key in latestValues) { var value = latestValues[key]; /** * If this is a CSS variable we don't do any further processing. */ if (isCSSVariable$1(key)) { vars[key] = value; continue; } // Convert the value to its default value type, ie 0 -> "0px" var valueType = numberValueTypes[key]; var valueAsType = getValueAsType(value, valueType); if (isTransformProp(key)) { // If this is a transform, flag to enable further transform processing hasTransform = true; transform[key] = valueAsType; transformKeys.push(key); // If we already know we have a non-default transform, early return if (!transformIsNone) continue; // Otherwise check to see if this is a default transform if (value !== ((_a = valueType.default) !== null && _a !== void 0 ? _a : 0)) transformIsNone = false; } else if (isTransformOriginProp(key)) { transformOrigin[key] = valueAsType; // If this is a transform origin, flag and enable further transform-origin processing hasTransformOrigin = true; } else { style[key] = valueAsType; } } if (hasTransform) { style.transform = buildTransform(state, options, transformIsNone, transformTemplate); } else if (transformTemplate) { style.transform = transformTemplate({}, ""); } else if (!latestValues.transform && style.transform) { style.transform = "none"; } if (hasTransformOrigin) { style.transformOrigin = buildTransformOrigin(transformOrigin); } } var createHtmlRenderState = function () { return { style: {}, transform: {}, transformKeys: [], transformOrigin: {}, vars: {} }; }; function copyRawValuesOnly(target, source, props) { for (var key in source) { if (!isMotionValue(source[key]) && !isForcedMotionValue(key, props)) { target[key] = source[key]; } } } function useInitialMotionValues(_a, visualState, isStatic) { var transformTemplate = _a.transformTemplate; return React.useMemo(function () { var state = createHtmlRenderState(); buildHTMLStyles(state, visualState, { enableHardwareAcceleration: !isStatic }, transformTemplate); var vars = state.vars, style = state.style; return tslib_es6.__assign(tslib_es6.__assign({}, vars), style); }, [visualState]); } function useStyle(props, visualState, isStatic) { var styleProp = props.style || {}; var style = {}; /** * Copy non-Motion Values straight into style */ copyRawValuesOnly(style, styleProp, props); Object.assign(style, useInitialMotionValues(props, visualState, isStatic)); if (props.transformValues) { style = props.transformValues(style); } return style; } function useHTMLProps(props, visualState, isStatic) { // The `any` isn't ideal but it is the type of createElement props argument var htmlProps = {}; var style = useStyle(props, visualState, isStatic); if (Boolean(props.drag) && props.dragListener !== false) { // Disable the ghost element when a user drags htmlProps.draggable = false; // Disable text selection style.userSelect = style.WebkitUserSelect = style.WebkitTouchCallout = "none"; // Disable scrolling on the draggable direction style.touchAction = props.drag === true ? "none" : "pan-".concat(props.drag === "x" ? "y" : "x"); } htmlProps.style = style; return htmlProps; } /** * A list of all valid MotionProps. * * @privateRemarks * This doesn't throw if a `MotionProp` name is missing - it should. */ var validMotionProps = new Set(["initial", "animate", "exit", "style", "variants", "transition", "transformTemplate", "transformValues", "custom", "inherit", "layout", "layoutId", "layoutDependency", "onLayoutAnimationStart", "onLayoutAnimationComplete", "onLayoutMeasure", "onBeforeLayoutMeasure", "onAnimationStart", "onAnimationComplete", "onUpdate", "onDragStart", "onDrag", "onDragEnd", "onMeasureDragConstraints", "onDirectionLock", "onDragTransitionEnd", "drag", "dragControls", "dragListener", "dragConstraints", "dragDirectionLock", "dragSnapToOrigin", "_dragX", "_dragY", "dragElastic", "dragMomentum", "dragPropagation", "dragTransition", "whileDrag", "onPan", "onPanStart", "onPanEnd", "onPanSessionStart", "onTap", "onTapStart", "onTapCancel", "onHoverStart", "onHoverEnd", "whileFocus", "whileTap", "whileHover", "whileInView", "onViewportEnter", "onViewportLeave", "viewport", "layoutScroll"]); /** * Check whether a prop name is a valid `MotionProp` key. * * @param key - Name of the property to check * @returns `true` is key is a valid `MotionProp`. * * @public */ function isValidMotionProp(key) { return validMotionProps.has(key); } var shouldForward = function (key) { return !isValidMotionProp(key); }; function loadExternalIsValidProp(isValidProp) { if (!isValidProp) return; // Explicitly filter our events shouldForward = function (key) { return key.startsWith("on") ? !isValidMotionProp(key) : isValidProp(key); }; } /** * Emotion and Styled Components both allow users to pass through arbitrary props to their components * to dynamically generate CSS. They both use the `@emotion/is-prop-valid` package to determine which * of these should be passed to the underlying DOM node. * * However, when styling a Motion component `styled(motion.div)`, both packages pass through *all* props * as it's seen as an arbitrary component rather than a DOM node. Motion only allows arbitrary props * passed through the `custom` prop so it doesn't *need* the payload or computational overhead of * `@emotion/is-prop-valid`, however to fix this problem we need to use it. * * By making it an optionalDependency we can offer this functionality only in the situations where it's * actually required. */ try { /** * We attempt to import this package but require won't be defined in esm environments, in that case * isPropValid will have to be provided via `MotionContext`. In a 6.0.0 this should probably be removed * in favour of explicit injection. */ loadExternalIsValidProp(require("@emotion/is-prop-valid").default); } catch (_a) { // We don't need to actually do anything here - the fallback is the existing `isPropValid`. } function filterProps(props, isDom, forwardMotionProps) { var filteredProps = {}; for (var key in props) { if (shouldForward(key) || forwardMotionProps === true && isValidMotionProp(key) || !isDom && !isValidMotionProp(key) || // If trying to use native HTML drag events, forward drag listeners props["draggable"] && key.startsWith("onDrag")) { filteredProps[key] = props[key]; } } return filteredProps; } function calcOrigin$1(origin, offset, size) { return typeof origin === "string" ? origin : px.transform(offset + size * origin); } /** * The SVG transform origin defaults are different to CSS and is less intuitive, * so we use the measured dimensions of the SVG to reconcile these. */ function calcSVGTransformOrigin(dimensions, originX, originY) { var pxOriginX = calcOrigin$1(originX, dimensions.x, dimensions.width); var pxOriginY = calcOrigin$1(originY, dimensions.y, dimensions.height); return "".concat(pxOriginX, " ").concat(pxOriginY); } var dashKeys = { offset: "stroke-dashoffset", array: "stroke-dasharray" }; var camelKeys = { offset: "strokeDashoffset", array: "strokeDasharray" }; /** * Build SVG path properties. Uses the path's measured length to convert * our custom pathLength, pathSpacing and pathOffset into stroke-dashoffset * and stroke-dasharray attributes. * * This function is mutative to reduce per-frame GC. */ function buildSVGPath(attrs, length, spacing, offset, useDashCase) { if (spacing === void 0) { spacing = 1; } if (offset === void 0) { offset = 0; } if (useDashCase === void 0) { useDashCase = true; } // Normalise path length by setting SVG attribute pathLength to 1 attrs.pathLength = 1; // We use dash case when setting attributes directly to the DOM node and camel case // when defining props on a React component. var keys = useDashCase ? dashKeys : camelKeys; // Build the dash offset attrs[keys.offset] = px.transform(-offset); // Build the dash array var pathLength = px.transform(length); var pathSpacing = px.transform(spacing); attrs[keys.array] = "".concat(pathLength, " ").concat(pathSpacing); } /** * Build SVG visual attrbutes, like cx and style.transform */ function buildSVGAttrs(state, _a, options, transformTemplate) { var attrX = _a.attrX, attrY = _a.attrY, originX = _a.originX, originY = _a.originY, pathLength = _a.pathLength, _b = _a.pathSpacing, pathSpacing = _b === void 0 ? 1 : _b, _c = _a.pathOffset, pathOffset = _c === void 0 ? 0 : _c, // This is object creation, which we try to avoid per-frame. latest = tslib_es6.__rest(_a, ["attrX", "attrY", "originX", "originY", "pathLength", "pathSpacing", "pathOffset"]); buildHTMLStyles(state, latest, options, transformTemplate); state.attrs = state.style; state.style = {}; var attrs = state.attrs, style = state.style, dimensions = state.dimensions; /** * However, we apply transforms as CSS transforms. So if we detect a transform we take it from attrs * and copy it into style. */ if (attrs.transform) { if (dimensions) style.transform = attrs.transform; delete attrs.transform; } // Parse transformOrigin if (dimensions && (originX !== undefined || originY !== undefined || style.transform)) { style.transformOrigin = calcSVGTransformOrigin(dimensions, originX !== undefined ? originX : 0.5, originY !== undefined ? originY : 0.5); } // Treat x/y not as shortcuts but as actual attributes if (attrX !== undefined) attrs.x = attrX; if (attrY !== undefined) attrs.y = attrY; // Build SVG path if one has been defined if (pathLength !== undefined) { buildSVGPath(attrs, pathLength, pathSpacing, pathOffset, false); } } var createSvgRenderState = function () { return tslib_es6.__assign(tslib_es6.__assign({}, createHtmlRenderState()), { attrs: {} }); }; function useSVGProps(props, visualState) { var visualProps = React.useMemo(function () { var state = createSvgRenderState(); buildSVGAttrs(state, visualState, { enableHardwareAcceleration: false }, props.transformTemplate); return tslib_es6.__assign(tslib_es6.__assign({}, state.attrs), { style: tslib_es6.__assign({}, state.style) }); }, [visualState]); if (props.style) { var rawStyles = {}; copyRawValuesOnly(rawStyles, props.style, props); visualProps.style = tslib_es6.__assign(tslib_es6.__assign({}, rawStyles), visualProps.style); } return visualProps; } function createUseRender(forwardMotionProps) { if (forwardMotionProps === void 0) { forwardMotionProps = false; } var useRender = function (Component, props, projectionId, ref, _a, isStatic) { var latestValues = _a.latestValues; var useVisualProps = isSVGComponent(Component) ? useSVGProps : useHTMLProps; var visualProps = useVisualProps(props, latestValues, isStatic); var filteredProps = filterProps(props, typeof Component === "string", forwardMotionProps); var elementProps = tslib_es6.__assign(tslib_es6.__assign(tslib_es6.__assign({}, filteredProps), visualProps), { ref: ref }); if (projectionId) { elementProps["data-projection-id"] = projectionId; } return /*#__PURE__*/React.createElement(Component, elementProps); }; return useRender; } var CAMEL_CASE_PATTERN = /([a-z])([A-Z])/g; var REPLACE_TEMPLATE = "$1-$2"; /** * Convert camelCase to dash-case properties. */ var camelToDash = function (str) { return str.replace(CAMEL_CASE_PATTERN, REPLACE_TEMPLATE).toLowerCase(); }; function renderHTML(element, _a, styleProp, projection) { var style = _a.style, vars = _a.vars; Object.assign(element.style, style, projection && projection.getProjectionStyles(styleProp)); // Loop over any CSS variables and assign those. for (var key in vars) { element.style.setProperty(key, vars[key]); } } /** * A set of attribute names that are always read/written as camel case. */ var camelCaseAttributes = new Set(["baseFrequency", "diffuseConstant", "kernelMatrix", "kernelUnitLength", "keySplines", "keyTimes", "limitingConeAngle", "markerHeight", "markerWidth", "numOctaves", "targetX", "targetY", "surfaceScale", "specularConstant", "specularExponent", "stdDeviation", "tableValues", "viewBox", "gradientTransform", "pathLength"]); function renderSVG(element, renderState, _styleProp, projection) { renderHTML(element, renderState, undefined, projection); for (var key in renderState.attrs) { element.setAttribute(!camelCaseAttributes.has(key) ? camelToDash(key) : key, renderState.attrs[key]); } } function scrapeMotionValuesFromProps$1(props) { var style = props.style; var newValues = {}; for (var key in style) { if (isMotionValue(style[key]) || isForcedMotionValue(key, props)) { newValues[key] = style[key]; } } return newValues; } function scrapeMotionValuesFromProps(props) { var newValues = scrapeMotionValuesFromProps$1(props); for (var key in props) { if (isMotionValue(props[key])) { var targetKey = key === "x" || key === "y" ? "attr" + key.toUpperCase() : key; newValues[targetKey] = props[key]; } } return newValues; } function isAnimationControls(v) { return typeof v === "object" && typeof v.start === "function"; } var isKeyframesTarget = function (v) { return Array.isArray(v); }; var isCustomValue = function (v) { return Boolean(v && typeof v === "object" && v.mix && v.toValue); }; var resolveFinalValueInKeyframes = function (v) { // TODO maybe throw if v.length - 1 is placeholder token? return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v; }; /** * If the provided value is a MotionValue, this returns the actual value, otherwise just the value itself * * TODO: Remove and move to library */ function resolveMotionValue(value) { var unwrappedValue = isMotionValue(value) ? value.get() : value; return isCustomValue(unwrappedValue) ? unwrappedValue.toValue() : unwrappedValue; } function makeState(_a, props, context, presenceContext) { var scrapeMotionValuesFromProps = _a.scrapeMotionValuesFromProps, createRenderState = _a.createRenderState, onMount = _a.onMount; var state = { latestValues: makeLatestValues(props, context, presenceContext, scrapeMotionValuesFromProps), renderState: createRenderState() }; if (onMount) { state.mount = function (instance) { return onMount(props, instance, state); }; } return state; } var makeUseVisualState = function (config) { return function (props, isStatic) { var context = React.useContext(MotionContext); var presenceContext = React.useContext(useId.PresenceContext); return isStatic ? makeState(config, props, context, presenceContext) : useId.useConstant(function () { return makeState(config, props, context, presenceContext); }); }; }; function makeLatestValues(props, context, presenceContext, scrapeMotionValues) { var values = {}; var blockInitialAnimation = (presenceContext === null || presenceContext === void 0 ? void 0 : presenceContext.initial) === false; var motionValues = scrapeMotionValues(props); for (var key in motionValues) { values[key] = resolveMotionValue(motionValues[key]); } var initial = props.initial, animate = props.animate; var isControllingVariants = checkIfControllingVariants(props); var isVariantNode = checkIfVariantNode(props); if (context && isVariantNode && !isControllingVariants && props.inherit !== false) { initial !== null && initial !== void 0 ? initial : initial = context.initial; animate !== null && animate !== void 0 ? animate : animate = context.animate; } var initialAnimationIsBlocked = blockInitialAnimation || initial === false; var variantToSet = initialAnimationIsBlocked ? animate : initial; if (variantToSet && typeof variantToSet !== "boolean" && !isAnimationControls(variantToSet)) { var list = Array.isArray(variantToSet) ? variantToSet : [variantToSet]; list.forEach(function (definition) { var resolved = resolveVariantFromProps(props, definition); if (!resolved) return; var transitionEnd = resolved.transitionEnd; resolved.tra