framer-motion
Version:
A simple and powerful JavaScript animation library
136 lines (133 loc) • 6.28 kB
JavaScript
"use client";
import { jsx } from 'react/jsx-runtime';
import { isMotionValue, visualElementStore } from 'motion-dom';
import { invariant } from 'motion-utils';
import { forwardRef, useContext, useRef, useEffect } from 'react';
import { ReorderBoardContext } from '../../context/ReorderBoardContext.mjs';
import { ReorderContext } from '../../context/ReorderContext.mjs';
import { useConstant } from '../../utils/use-constant.mjs';
import { useIsomorphicLayoutEffect } from '../../utils/use-isomorphic-effect.mjs';
import { useMotionValue } from '../../value/use-motion-value.mjs';
import { useTransform } from '../../value/use-transform.mjs';
import { resetAutoScrollState, autoScrollIfNeeded } from './utils/auto-scroll.mjs';
import { mergeRefs } from './utils/merge-refs.mjs';
function useDefaultMotionValue(value, defaultValue = 0) {
return isMotionValue(value) ? value : useMotionValue(defaultValue);
}
function ReorderItemComponent({ children, style = {}, value, as = "li", onDragStart, onDrag, onDragEnd, onKeyDown, onFocus, onBlur, layout = true, ...props }, externalRef) {
const context = useContext(ReorderContext);
const board = useContext(ReorderBoardContext);
const ref = useRef(null);
process.env.NODE_ENV !== "production" &&
invariant(Boolean(context), "Reorder.Item must be a child of Reorder.Group", "reorder-item-child");
/**
* Render the group's kind of component, `motion` or `m`.
*/
const Component = useConstant(() => context.proxy[as]);
/**
* The group's values when this item was grabbed with the keyboard, for
* Escape to restore. Set only while grabbed. Only key and focus events
* read it, so it's a ref: a key sent before React renders, as test
* runners can, still sees the grab.
*/
const grabbedFrom = useRef();
const point = {
x: useDefaultMotionValue(style.x),
y: useDefaultMotionValue(style.y),
};
const zIndex = useTransform([point.x, point.y], ([latestX, latestY]) => latestX || latestY ? 1 : "unset");
const { axis, registerItem, updateOrder, groupRef, hasReordered, isTabStop, setActive, checkTabStop, elements, onKeyDown: handleKeyDown, } = context;
/**
* In a Reorder.Board, items can be dragged out of their group.
*/
const dragAxis = axis === "xy" || board ? true : axis;
/**
* Keep this item's element for the group to focus.
*/
elements.set(value, ref);
useEffect(() => checkTabStop);
/**
* A keyboard grab is a drag without a pointer, so it shares whileDrag
* (but not the drag callbacks, which need a pointer and PanInfo).
*/
const grab = (element, from) => {
grabbedFrom.current = from;
from || board?.end(value);
visualElementStore
.get(element)
?.animationState?.setActive("whileDrag", !!from);
};
/**
* If this item was just moved here from another group, carry on its
* drag or keyboard grab. A drag carried on is the same gesture, so it
* doesn't call onDragStart again.
*/
const isContinued = useRef(false);
useIsomorphicLayoutEffect(() => {
board?.receive(context, value, ref.current, grab) &&
(isContinued.current = true);
}, []);
return (jsx(Component, { drag: dragAxis, tabIndex: isTabStop(value) ? 0 : -1, ...props, dragSnapToOrigin: true, style: { ...style, x: point.x, y: point.y, zIndex }, layout: layout, onDragStart: (event, gesturePoint) => {
isContinued.current
? (isContinued.current = false)
: onDragStart?.(event, gesturePoint);
}, onDrag: (event, gesturePoint) => {
const { velocity, point: pointerPoint } = gesturePoint;
const offset = { x: point.x.get(), y: point.y.get() };
board?.drag(context, value, ref.current, event) ||
updateOrder(value, offset, velocity);
/**
* In a Reorder.Board, scroll on both axes, e.g. across
* columns as well as down them. Across them, scroll what's
* around the columns: an item dragged out of its column
* overflows it, so the column could scroll across too.
*/
const scrollAxes = board
? ["x", "y"]
: [
axis === "xy"
? Math.abs(velocity.x) > Math.abs(velocity.y)
? "x"
: "y"
: axis,
];
for (const scrollAxis of scrollAxes) {
autoScrollIfNeeded(board && !axis.includes(scrollAxis)
? board.column(context)
: groupRef.current, pointerPoint[scrollAxis], scrollAxis, velocity[scrollAxis]);
}
onDrag && onDrag(event, gesturePoint);
}, onDragEnd: (event, gesturePoint) => {
// Runs a frame after release, so after the release's click
hasReordered.current = false;
resetAutoScrollState();
board?.end(value);
onDragEnd && onDragEnd(event, gesturePoint);
}, onKeyDown: (event) => {
onKeyDown?.(event);
handleKeyDown(event, value, grabbedFrom.current, grab);
},
/**
* The item focused last, or holding focus, is the group's tab
* stop.
*/
onFocus: (event) => {
onFocus?.(event);
setActive(value);
},
/**
* A move blurs the item, but that blur is never dispatched:
* React DOM disables its events while it commits, and restores
* focus to the item before re-enabling them. So any blur that
* reaches here is focus really leaving, which drops the item.
*/
onBlur: (event) => {
onBlur?.(event);
grabbedFrom.current && grab(event.currentTarget);
}, onLayoutMeasure: (measured) => {
registerItem(value, measured, ref);
}, ref: mergeRefs(ref, externalRef), ignoreStrict: true, children: children }));
}
const ReorderItem = /*@__PURE__*/ forwardRef(ReorderItemComponent);
export { ReorderItem, ReorderItemComponent };
//# sourceMappingURL=Item.mjs.map