UNPKG

framer-motion

Version:

A simple and powerful JavaScript animation library

1 lines 5.69 kB
{"version":3,"file":"Group.mjs","sources":["../../../../src/components/Reorder/Group.tsx"],"sourcesContent":["\"use client\"\n\nimport { invariant } from \"motion-utils\"\nimport * as React from \"react\"\nimport { forwardRef, FunctionComponent, JSX, useEffect, useRef } from \"react\"\nimport { ReorderContext } from \"../../context/ReorderContext\"\nimport { motion } from \"../../render/components/motion/proxy\"\nimport { HTMLMotionProps } from \"../../render/html/types\"\nimport { useConstant } from \"../../utils/use-constant\"\nimport {\n DefaultGroupElement,\n ItemData,\n ReorderContextProps,\n ReorderElementTag,\n} from \"./types\"\nimport { checkReorder } from \"./utils/check-reorder\"\n\nexport interface Props<\n V,\n TagName extends ReorderElementTag = DefaultGroupElement\n> {\n /**\n * A HTML element to render this component as. Defaults to `\"ul\"`.\n *\n * @public\n */\n as?: TagName\n\n /**\n * The axis to reorder along. By default, items will be draggable on this axis.\n * To make draggable on both axes, set `<Reorder.Item drag />`\n *\n * @public\n */\n axis?: \"x\" | \"y\"\n\n /**\n * A callback to fire with the new value order. For instance, if the values\n * are provided as a state from `useState`, this could be the set state function.\n *\n * @public\n */\n onReorder: (newOrder: V[]) => void\n\n /**\n * The latest values state.\n *\n * ```jsx\n * function Component() {\n * const [items, setItems] = useState([0, 1, 2])\n *\n * return (\n * <Reorder.Group values={items} onReorder={setItems}>\n * {items.map((item) => <Reorder.Item key={item} value={item} />)}\n * </Reorder.Group>\n * )\n * }\n * ```\n *\n * @public\n */\n values: V[]\n}\n\ntype ReorderGroupProps<\n V,\n TagName extends ReorderElementTag = DefaultGroupElement\n> = Props<V, TagName> &\n Omit<HTMLMotionProps<TagName>, \"values\"> &\n React.PropsWithChildren<{}>\n\nexport function ReorderGroupComponent<\n V,\n TagName extends ReorderElementTag = DefaultGroupElement\n>(\n {\n children,\n as = \"ul\" as TagName,\n axis = \"y\",\n onReorder,\n values,\n ...props\n }: ReorderGroupProps<V, TagName>,\n externalRef?: React.ForwardedRef<any>\n): JSX.Element {\n const Component = useConstant(\n () => motion[as as keyof typeof motion]\n ) as FunctionComponent<\n React.PropsWithChildren<HTMLMotionProps<any> & { ref?: React.Ref<any> }>\n >\n\n const order: ItemData<V>[] = []\n const isReordering = useRef(false)\n const groupRef = useRef<Element>(null)\n\n invariant(\n Boolean(values),\n \"Reorder.Group must be provided a values prop\",\n \"reorder-values\"\n )\n\n const context: ReorderContextProps<V> = {\n axis,\n groupRef,\n registerItem: (value, layout) => {\n // If the entry was already added, update it rather than adding it again\n const idx = order.findIndex((entry) => value === entry.value)\n if (idx !== -1) {\n order[idx].layout = layout[axis]\n } else {\n order.push({ value: value, layout: layout[axis] })\n }\n order.sort(compareMin)\n },\n updateOrder: (item, offset, velocity) => {\n if (isReordering.current) return\n\n const newOrder = checkReorder(order, item, offset, velocity)\n\n if (order !== newOrder) {\n isReordering.current = true\n onReorder(\n newOrder\n .map(getValue)\n .filter((value) => values.indexOf(value) !== -1)\n )\n }\n },\n }\n\n useEffect(() => {\n isReordering.current = false\n })\n\n // Combine refs if external ref is provided\n const setRef = (element: Element | null) => {\n ;(groupRef as React.MutableRefObject<Element | null>).current = element\n if (typeof externalRef === \"function\") {\n externalRef(element)\n } else if (externalRef) {\n ;(\n externalRef as React.MutableRefObject<Element | null>\n ).current = element\n }\n }\n\n /**\n * Disable browser scroll anchoring on the group container.\n * When items reorder, scroll anchoring can cause the browser to adjust\n * the scroll position, which interferes with drag position calculations.\n */\n const groupStyle = {\n overflowAnchor: \"none\" as const,\n ...props.style,\n }\n\n return (\n <Component {...props} style={groupStyle} ref={setRef} ignoreStrict>\n <ReorderContext.Provider value={context}>\n {children}\n </ReorderContext.Provider>\n </Component>\n )\n}\n\nexport const ReorderGroup = /*@__PURE__*/ forwardRef(ReorderGroupComponent) as <\n V,\n TagName extends ReorderElementTag = DefaultGroupElement\n>(\n props: ReorderGroupProps<V, TagName> & { ref?: React.ForwardedRef<any> }\n) => ReturnType<typeof ReorderGroupComponent>\n\nfunction getValue<V>(item: ItemData<V>) {\n return item.value\n}\n\nfunction compareMin<V>(a: ItemData<V>, b: ItemData<V>) {\n return a.layout.min - b.layout.min\n}\n"],"names":[],"mappings":";;;;;;;;;AAuEM;AAcF;;AAOA;AACA;;AAQA;;;AAGI;;AAEI;AACA;;;;AAGI;;AAEJ;;;;;AAKA;AAEA;AACI;AACA;;AAGS;;;;;AAOjB;AACJ;;AAGA;AACM;AACF;;;;AAIQ;;AAGZ;AAEA;;;;AAIG;AACH;AACI;;;AAIJ;AAOJ;AAEa;AAOb;;AAEA;AAEA;;AAEA;;"}