@vimeo/iris
Version:
Vimeo Design System
1,432 lines (1,365 loc) • 298 kB
JavaScript
'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