: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
// If we're not animating on an externally-provided `MotionValue` we can use the
// component's animation controls which will handle interactions with whileHover (etc),
// otherwise we just have to animate the `MotionValue` itself.
return this.startAxisValueAnimation(axis, inertia);
// Run all animations and then resolve the new drag constraints.
return Promise.all(momentumAnimations).then(onDragTransitionEnd);
startAxisValueAnimation(axis, transition) {
const axisValue = this.getAxisMotionValue(axis);
return axisValue.start(animateMotionValue(axis, axisValue, 0, transition, this.visualElement));
eachAxis((axis) => this.getAxisMotionValue(axis).stop());
eachAxis((axis) => { var _a; return (_a = this.getAxisMotionValue(axis).animation) === null || _a === void 0 ? void 0 : _a.pause(); });
getAnimationState(axis) {
return (_a = this.getAxisMotionValue(axis).animation) === null || _a === void 0 ? void 0 : _a.state;
* Drag works differently depending on which props are provided.
* - If _dragX and _dragY are provided, we output the gesture delta directly to those motion values.
* - Otherwise, we apply the delta to the x/y motion values.
getAxisMotionValue(axis) {
const dragKey = `_drag${axis.toUpperCase()}`;
const props = this.visualElement.getProps();
const externalMotionValue = props[dragKey];
return externalMotionValue
: this.visualElement.getValue(axis, (props.initial
const { drag } = this.getProps();
// If we're not dragging this axis, do an early return.
if (!shouldDrag(axis, drag, this.currentDirection))
const { projection } = this.visualElement;
const axisValue = this.getAxisMotionValue(axis);
if (projection && projection.layout) {
const { min, max } = projection.layout.layoutBox[axis];
axisValue.set(point[axis] - mixNumber(min, max, 0.5));
* When the viewport resizes we want to check if the measured constraints
* have changed and, if so, reposition the element within those new constraints
* relative to where it was before the resize.
scalePositionWithinConstraints() {
if (!this.visualElement.current)
const { drag, dragConstraints } = this.getProps();
const { projection } = this.visualElement;
if (!isRefObject(dragConstraints) || !projection || !this.constraints)
* Stop current animations as there can be visual glitching if we try to do
* Record the relative position of the dragged element relative to the
* constraints box and save as a progress value.
const boxProgress = { x: 0, y: 0 };
const axisValue = this.getAxisMotionValue(axis);
if (axisValue && this.constraints !== false) {
const latest = axisValue.get();
boxProgress[axis] = constraints_calcOrigin({ min: latest, max: latest }, this.constraints[axis]);
* Update the layout of this element and resolve the latest drag constraints
const { transformTemplate } = this.visualElement.getProps();
this.visualElement.current.style.transform = transformTemplate
? transformTemplate({}, "")
projection.root && projection.root.updateScroll();
projection.updateLayout();
this.resolveConstraints();
* For each axis, calculate the current progress of the layout axis
* within the new constraints.
if (!shouldDrag(axis, drag, null))
* Calculate a new transform based on the previous box progress
const axisValue = this.getAxisMotionValue(axis);
const { min, max } = this.constraints[axis];
axisValue.set(mixNumber(min, max, boxProgress[axis]));
if (!this.visualElement.current)
elementDragControls.set(this.visualElement, this);
const element = this.visualElement.current;
* Attach a pointerdown event listener on this DOM element to initiate drag tracking.
const stopPointerListener = addPointerEvent(element, "pointerdown", (event) => {
const { drag, dragListener = true } = this.getProps();
drag && dragListener && this.start(event);
const measureDragConstraints = () => {
const { dragConstraints } = this.getProps();
if (isRefObject(dragConstraints)) {
this.constraints = this.resolveRefConstraints();
const { projection } = this.visualElement;
const stopMeasureLayoutListener = projection.addEventListener("measure", measureDragConstraints);
if (projection && !projection.layout) {
projection.root && projection.root.updateScroll();
projection.updateLayout();
measureDragConstraints();
* Attach a window resize listener to scale the draggable target within its defined
* constraints as the window resizes.
const stopResizeListener = addDomEvent(window, "resize", () => this.scalePositionWithinConstraints());
* If the element's layout changes, calculate the delta and apply that to
* the drag gesture's origin point.
const stopLayoutUpdateListener = projection.addEventListener("didUpdate", (({ delta, hasLayoutChanged }) => {
if (this.isDragging && hasLayoutChanged) {
const motionValue = this.getAxisMotionValue(axis);
this.originPoint[axis] += delta[axis].translate;
motionValue.set(motionValue.get() + delta[axis].translate);
this.visualElement.render();
stopMeasureLayoutListener();
stopLayoutUpdateListener && stopLayoutUpdateListener();
const props = this.visualElement.getProps();
const { drag = false, dragDirectionLock = false, dragPropagation = false, dragConstraints = false, dragElastic = defaultElastic, dragMomentum = true, } = props;
function shouldDrag(direction, drag, currentDirection) {
return ((drag === true || drag === direction) &&
(currentDirection === null || currentDirection === direction));
* Based on an x/y offset determine the current drag direction. If both axis' offsets are lower
* than the provided threshold, return `null`.
* @param offset - The x/y offset from origin.
* @param lockThreshold - (Optional) - the minimum absolute offset before we can determine a drag direction.
function getCurrentDirection(offset, lockThreshold = 10) {
if (Math.abs(offset.y) > lockThreshold) {
else if (Math.abs(offset.x) > lockThreshold) {
;// CONCATENATED MODULE: ./node_modules/framer-motion/dist/es/gestures/drag/index.mjs
class DragGesture extends Feature {
this.removeGroupControls = noop_noop;
this.removeListeners = noop_noop;
this.controls = new VisualElementDragControls(node);
// If we've been provided a DragControls for manual control over the drag gesture,
// subscribe this component to it on mount.
const { dragControls } = this.node.getProps();
this.removeGroupControls = dragControls.subscribe(this.controls);
this.removeListeners = this.controls.addListeners() || noop_noop;
this.removeGroupControls();
;// CONCATENATED MODULE: ./node_modules/framer-motion/dist/es/gestures/pan/index.mjs
const asyncHandler = (handler) => (event, info) => {
frame_frame.postRender(() => handler(event, info));
class PanGesture extends Feature {
this.removePointerDownListener = noop_noop;
onPointerDown(pointerDownEvent) {
this.session = new PanSession(pointerDownEvent, this.createPanHandlers(), {
transformPagePoint: this.node.getTransformPagePoint(),
contextWindow: getContextWindow(this.node),
const { onPanSessionStart, onPanStart, onPan, onPanEnd } = this.node.getProps();
onSessionStart: asyncHandler(onPanSessionStart),
onStart: asyncHandler(onPanStart),
onEnd: (event, info) => {
frame_frame.postRender(() => onPanEnd(event, info));
this.removePointerDownListener = addPointerEvent(this.node.current, "pointerdown", (event) => this.onPointerDown(event));
this.session && this.session.updateHandlers(this.createPanHandlers());
this.removePointerDownListener();
this.session && this.session.end();
;// CONCATENATED MODULE: ./node_modules/framer-motion/dist/es/components/AnimatePresence/use-presence.mjs
* When a component is the child of `AnimatePresence`, it can use `usePresence`
* to access information about whether it's still present in the React tree.
* import { usePresence } from "framer-motion"
* export const Component = () => {
* const [isPresent, safeToRemove] = usePresence()
* !isPresent && setTimeout(safeToRemove, 1000)
* If `isPresent` is `false`, it means that a component has been removed the tree, but
* `AnimatePresence` won't really remove it until `safeToRemove` has been called.
const context = (0,external_React_.useContext)(PresenceContext_PresenceContext);
const { isPresent, onExitComplete, register } = context;
// It's safe to call the following hooks conditionally (after an early return) because the context will always
// either be null or non-null for the lifespan of the component.
const id = (0,external_React_.useId)();
(0,external_React_.useEffect)(() => register(id), []);
const safeToRemove = () => onExitComplete && onExitComplete(id);
return !isPresent && onExitComplete ? [false, safeToRemove] : [true];
* Similar to `usePresence`, except `useIsPresent` simply returns whether or not the component is present.
* There is no `safeToRemove` function.
* import { useIsPresent } from "framer-motion"
* export const Component = () => {
* const isPresent = useIsPresent()
* !isPresent && console.log("I've been removed!")
function useIsPresent() {
return isPresent(useContext(PresenceContext));
function isPresent(context) {
return context === null ? true : context.isPresent;
;// CONCATENATED MODULE: ./node_modules/framer-motion/dist/es/projection/node/state.mjs
* 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.
const globalProjectionState = {
* Global flag as to whether the tree has animated since the last time
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.
;// CONCATENATED MODULE: ./node_modules/framer-motion/dist/es/projection/styles/scale-border-radius.mjs
function pixelsToPercent(pixels, axis) {
if (axis.max === axis.min)
return (pixels / (axis.max - axis.min)) * 100;
* We always correct borderRadius as a percentage rather than pixels to reduce paints.
* For example, if you are projecting a box that is 100px wide with a 10px borderRadius
* into a box that is 200px wide with a 20px borderRadius, that is actually a 10%
* borderRadius in both states. If we animate between the two in pixels that will trigger
* a paint each time. If we animate between the two in percentage we'll avoid a paint.
const correctBorderRadius = {
correct: (latest, node) => {
* If latest is a string, if it's a percentage we can return immediately as it's
* going to be stretched appropriately. Otherwise, if it's a pixel, convert it to a number.
if (typeof latest === "string") {
latest = parseFloat(latest);
* If latest is a number, it's a pixel value. We use the current viewportBox to calculate that
* pixel value as a percentage of each axis
const x = pixelsToPercent(latest, node.target.x);
const y = pixelsToPercent(latest, node.target.y);
;// CONCATENATED MODULE: ./node_modules/framer-motion/dist/es/projection/styles/scale-box-shadow.mjs
const correctBoxShadow = {
correct: (latest, { treeScale, projectionDelta }) => {
const shadow = complex.parse(latest);
// TODO: Doesn't support multiple shadows
const template = complex.createTransformer(latest);
const offset = typeof shadow[0] !== "number" ? 1 : 0;
// Calculate the overall context scale
const xScale = projectionDelta.x.scale * treeScale.x;
const yScale = projectionDelta.y.scale * treeScale.y;
shadow[0 + offset] /= xScale;
shadow[1 + offset] /= yScale;
* Ideally we'd correct x and y scales individually, but because blur and
* spread apply to both we have to take a scale average and apply that instead.
* We could potentially improve the outcome of this by incorporating the ratio between
const averageScale = mixNumber(xScale, yScale, 0.5);
if (typeof shadow[2 + offset] === "number")
shadow[2 + offset] /= averageScale;
if (typeof shadow[3 + offset] === "number")
shadow[3 + offset] /= averageScale;
;// CONCATENATED MODULE: ./node_modules/framer-motion/dist/es/motion/features/layout/MeasureLayout.mjs
class MeasureLayoutWithContext extends external_React_.Component {
* This only mounts projection nodes for components that
* need measuring, we might want to do it for all components
* in order to incorporate transforms
const { visualElement, layoutGroup, switchLayoutGroup, layoutId } = this.props;
const { projection } = visualElement;
addScaleCorrector(defaultScaleCorrectors);
layoutGroup.group.add(projection);
if (switchLayoutGroup && switchLayoutGroup.register && layoutId) {
switchLayoutGroup.register(projection);
projection.root.didUpdate();
projection.addEventListener("animationComplete", () => {
onExitComplete: () => this.safeToRemove(),
globalProjectionState.hasEverUpdated = true;
getSnapshotBeforeUpdate(prevProps) {
const { layoutDependency, visualElement, drag, isPresent } = this.props;
const projection = visualElement.projection;
* TODO: We use this data in relegate to determine whether to
* promote a previous element. There's no guarantee its presence data
* will have updated by this point - if a bug like this arises it will
* have to be that we markForRelegation and then find a new lead some other way,
projection.isPresent = isPresent;