UNPKG

framer-motion

Version:

A simple and powerful JavaScript animation library

136 lines (133 loc) • 6.28 kB
"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