framer-motion
Version:
A simple and powerful JavaScript animation library
1 lines • 9.46 kB
Source Map (JSON)
{"version":3,"file":"Item.mjs","sources":["../../../../src/components/Reorder/Item.tsx"],"sourcesContent":["\"use client\"\n\nimport { isMotionValue, visualElementStore } from \"motion-dom\"\nimport { invariant } from \"motion-utils\"\nimport * as React from \"react\"\nimport {\n forwardRef,\n FunctionComponent,\n useContext,\n useEffect,\n useRef,\n} from \"react\"\nimport { ReorderBoardContext } from \"../../context/ReorderBoardContext\"\nimport { ReorderContext } from \"../../context/ReorderContext\"\nimport type { MotionProxy } from \"../../render/components/create-proxy\"\nimport { HTMLMotionProps } from \"../../render/html/types\"\nimport { useConstant } from \"../../utils/use-constant\"\nimport { useIsomorphicLayoutEffect } from \"../../utils/use-isomorphic-effect\"\nimport { useMotionValue } from \"../../value/use-motion-value\"\nimport { useTransform } from \"../../value/use-transform\"\n\nimport { DefaultItemElement, Grab, ReorderElementTag } from \"./types\"\nimport { autoScrollIfNeeded, resetAutoScrollState } from \"./utils/auto-scroll\"\nimport { mergeRefs } from \"./utils/merge-refs\"\n\nexport interface Props<\n V,\n TagName extends ReorderElementTag = DefaultItemElement\n> {\n /**\n * A HTML element to render this component as. Defaults to `\"li\"`.\n *\n * @public\n */\n as?: TagName\n\n /**\n * The value in the list that this component represents.\n *\n * @public\n */\n value: V\n\n /**\n * A subset of layout options primarily used to disable layout=\"size\"\n *\n * @public\n * @default true\n */\n layout?: true | \"position\"\n}\n\nfunction useDefaultMotionValue(value: any, defaultValue: number = 0) {\n return isMotionValue(value) ? value : useMotionValue(defaultValue)\n}\n\ntype ReorderItemProps<\n V,\n TagName extends ReorderElementTag = DefaultItemElement\n> = Props<V, TagName> &\n Omit<HTMLMotionProps<TagName>, \"value\" | \"layout\"> &\n React.PropsWithChildren<{}>\n\nexport function ReorderItemComponent<\n V,\n TagName extends ReorderElementTag = DefaultItemElement\n>(\n {\n children,\n style = {},\n value,\n as = \"li\" as TagName,\n onDragStart,\n onDrag,\n onDragEnd,\n onKeyDown,\n onFocus,\n onBlur,\n layout = true,\n ...props\n }: ReorderItemProps<V, TagName>,\n externalRef?: React.ForwardedRef<any>\n): React.JSX.Element {\n const context = useContext(ReorderContext)\n const board = useContext(ReorderBoardContext)\n const ref = useRef<HTMLElement | null>(null)\n\n process.env.NODE_ENV !== \"production\" &&\n invariant(\n Boolean(context),\n \"Reorder.Item must be a child of Reorder.Group\",\n \"reorder-item-child\"\n )\n\n /**\n * Render the group's kind of component, `motion` or `m`.\n */\n const Component = useConstant(\n () => context!.proxy[as as keyof MotionProxy]\n ) as FunctionComponent<\n React.PropsWithChildren<HTMLMotionProps<any> & { ref?: React.Ref<any> }>\n >\n\n /**\n * The group's values when this item was grabbed with the keyboard, for\n * Escape to restore. Set only while grabbed. Only key and focus events\n * read it, so it's a ref: a key sent before React renders, as test\n * runners can, still sees the grab.\n */\n const grabbedFrom = useRef<V[]>()\n const point = {\n x: useDefaultMotionValue(style.x),\n y: useDefaultMotionValue(style.y),\n }\n\n const zIndex = useTransform([point.x, point.y], ([latestX, latestY]) =>\n latestX || latestY ? 1 : \"unset\"\n )\n\n const {\n axis,\n registerItem,\n updateOrder,\n groupRef,\n hasReordered,\n isTabStop,\n setActive,\n checkTabStop,\n elements,\n onKeyDown: handleKeyDown,\n } = context!\n\n /**\n * In a Reorder.Board, items can be dragged out of their group.\n */\n const dragAxis = axis === \"xy\" || board ? true : axis\n\n /**\n * Keep this item's element for the group to focus.\n */\n elements.set(value, ref)\n useEffect(() => checkTabStop)\n\n /**\n * A keyboard grab is a drag without a pointer, so it shares whileDrag\n * (but not the drag callbacks, which need a pointer and PanInfo).\n */\n const grab: Grab<V> = (element, from) => {\n grabbedFrom.current = from\n from || board?.end(value)\n visualElementStore\n .get(element)\n ?.animationState?.setActive(\"whileDrag\", !!from)\n }\n\n /**\n * If this item was just moved here from another group, carry on its\n * drag or keyboard grab. A drag carried on is the same gesture, so it\n * doesn't call onDragStart again.\n */\n const isContinued = useRef(false)\n useIsomorphicLayoutEffect(() => {\n board?.receive(context!, value, ref.current!, grab) &&\n (isContinued.current = true)\n }, [])\n\n return (\n <Component\n drag={dragAxis}\n tabIndex={isTabStop(value) ? 0 : -1}\n {...props}\n dragSnapToOrigin\n style={{ ...style, x: point.x, y: point.y, zIndex }}\n layout={layout}\n onDragStart={(event, gesturePoint) => {\n isContinued.current\n ? (isContinued.current = false)\n : onDragStart?.(event, gesturePoint)\n }}\n onDrag={(event, gesturePoint) => {\n const { velocity, point: pointerPoint } = gesturePoint\n const offset = { x: point.x.get(), y: point.y.get() }\n\n board?.drag(context!, value, ref.current!, event) ||\n updateOrder(value, offset, velocity)\n\n /**\n * In a Reorder.Board, scroll on both axes, e.g. across\n * columns as well as down them. Across them, scroll what's\n * around the columns: an item dragged out of its column\n * overflows it, so the column could scroll across too.\n */\n const scrollAxes: Array<\"x\" | \"y\"> = board\n ? [\"x\", \"y\"]\n : [\n axis === \"xy\"\n ? Math.abs(velocity.x) > Math.abs(velocity.y)\n ? \"x\"\n : \"y\"\n : axis,\n ]\n for (const scrollAxis of scrollAxes) {\n autoScrollIfNeeded(\n board && !axis.includes(scrollAxis)\n ? board.column(context!)\n : groupRef.current,\n pointerPoint[scrollAxis],\n scrollAxis,\n velocity[scrollAxis]\n )\n }\n\n onDrag && onDrag(event, gesturePoint)\n }}\n onDragEnd={(event, gesturePoint) => {\n // Runs a frame after release, so after the release's click\n hasReordered.current = false\n resetAutoScrollState()\n board?.end(value)\n onDragEnd && onDragEnd(event, gesturePoint)\n }}\n onKeyDown={(event: React.KeyboardEvent<HTMLElement>) => {\n onKeyDown?.(event as any)\n handleKeyDown(event, value, grabbedFrom.current, grab)\n }}\n /**\n * The item focused last, or holding focus, is the group's tab\n * stop.\n */\n onFocus={(event: React.FocusEvent<HTMLElement>) => {\n onFocus?.(event as any)\n setActive(value)\n }}\n /**\n * A move blurs the item, but that blur is never dispatched:\n * React DOM disables its events while it commits, and restores\n * focus to the item before re-enabling them. So any blur that\n * reaches here is focus really leaving, which drops the item.\n */\n onBlur={(event: React.FocusEvent<HTMLElement>) => {\n onBlur?.(event as any)\n grabbedFrom.current && grab(event.currentTarget)\n }}\n onLayoutMeasure={(measured) => {\n registerItem(value, measured, ref)\n }}\n ref={mergeRefs(ref, externalRef)}\n ignoreStrict\n >\n {children}\n </Component>\n )\n}\n\nexport const ReorderItem = /*@__PURE__*/ forwardRef(ReorderItemComponent) as <\n V,\n TagName extends ReorderElementTag = DefaultItemElement\n>(\n props: ReorderItemProps<V, TagName> & { ref?: React.ForwardedRef<any> }\n) => ReturnType<typeof ReorderItemComponent>\n"],"names":[],"mappings":";;;;;;;;;;;;;;AAoDA;AACI;AACJ;;AA6BI;AACA;AACA;AAEA;;AAOA;;AAEG;AACH;AAMA;;;;;AAKG;AACH;AACA;AACI;AACA;;AAGJ;;AAiBA;;AAEG;AACH;AAEA;;AAEG;AACH;AACA;AAEA;;;AAGG;AACH;AACI;AACA;;;;AAIJ;AAEA;;;;AAIG;AACH;;AAEI;AACI;;AAGR;AASY;AACI;;;;;AAOJ;AACI;AAEJ;;;;;AAKG;;AAEC;AACA;AACM;AACI;AACI;AACA;AACJ;;AAEd;;AAGY;AACA;;AAOZ;;;AAIA;AACA;AACA;AACA;AACJ;AAEI;;;AAGJ;;;AAGG;AACH;AACI;;;AAGJ;;;;;AAKG;AACH;AACI;;AAEJ;AAEI;AACJ;AAOZ;AAEO;;"}