motion-dom
Version:
1 lines • 9.74 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","sources":["../../../../src/gestures/drag/index.ts"],"sourcesContent":["import type { BoundingBox } from \"motion-utils\"\nimport type { InertiaOptions } from \"../../animation/types\"\nimport type { MotionNodeOptions } from \"../../node/types\"\nimport { visualElementStore } from \"../../render/store\"\nimport type { VisualElement } from \"../../render/VisualElement\"\nimport { activeScope, addToScope } from \"../../scope/active\"\nimport { motionValue, type MotionValue } from \"../../value\"\nimport {\n resolveElements,\n type ElementOrSelector,\n} from \"../../utils/resolve-elements\"\nimport { acquireDragNode } from \"./projection\"\nimport type { DragConstraintsFunction, DragElastic, DragHandler } from \"./types\"\nimport { VisualElementDragControls } from \"./VisualElementDragControls\"\n\n/**\n * @public\n */\nexport interface DragOptions {\n /**\n * Restrict dragging to a single axis. By default an element can be\n * dragged on both axes.\n */\n axis?: \"x\" | \"y\"\n\n /**\n * The permitted draggable area. Either `top`/`left`/`right`/`bottom`\n * distances in pixels, relative to the element's origin, an element\n * the draggable element must stay within, or a function that returns\n * a `DragPointConstraint` to move the element itself.\n */\n constraints?: Partial<BoundingBox> | Element | DragConstraintsFunction\n\n /**\n * How far, from `0` to `1`, the element can move outside its\n * constraints. Can be set per edge. Defaults to `0.35`.\n */\n elastic?: DragElastic\n\n /**\n * Whether to carry the gesture's velocity into an inertia animation\n * on release. Defaults to `true`.\n */\n momentum?: boolean\n\n /**\n * Options for the inertia animation that runs on release.\n */\n transition?: InertiaOptions\n\n /**\n * Animate back to the origin on release.\n */\n snapToOrigin?: boolean | \"x\" | \"y\"\n\n /**\n * Lock dragging to the first axis the pointer moves along.\n */\n directionLock?: boolean\n\n /**\n * Allow drag gestures to start on parent elements that are also\n * draggable. By default, only the innermost draggable element moves.\n */\n propagation?: boolean\n\n /**\n * Motion values to write the drag to, instead of the element's\n * `x` and `y`. When either is set, the element itself doesn't move,\n * so drag can drive other values, like a dial's rotation.\n *\n * ```javascript\n * const x = motionValue(0)\n * x.on(\"change\", (v) => (dial.style.rotate = v + \"deg\"))\n * drag(dial, { axis: \"x\", x })\n * ```\n */\n x?: MotionValue<number>\n y?: MotionValue<number>\n\n onStart?: DragHandler\n onMove?: DragHandler\n onEnd?: DragHandler\n onDirectionLock?: (axis: \"x\" | \"y\") => void\n onTransitionEnd?: () => void\n\n /**\n * Called when element constraints are measured. Return a new\n * `BoundingBox` to override them.\n */\n onMeasureConstraints?: (constraints: BoundingBox) => BoundingBox | void\n}\n\nconst toProps = ({\n axis,\n constraints,\n elastic,\n momentum,\n transition,\n snapToOrigin,\n directionLock,\n propagation,\n onStart,\n onMove,\n onEnd,\n onDirectionLock,\n onTransitionEnd,\n onMeasureConstraints,\n x,\n y,\n}: DragOptions): MotionNodeOptions => ({\n drag: axis || true,\n _dragX: x || (y && motionValue(0)),\n _dragY: y || (x && motionValue(0)),\n dragConstraints: constraints,\n dragElastic: elastic,\n dragMomentum: momentum,\n dragTransition: transition,\n dragSnapToOrigin: snapToOrigin,\n dragDirectionLock: directionLock,\n dragPropagation: propagation,\n onDragStart: onStart,\n onDrag: onMove,\n onDragEnd: onEnd,\n onDirectionLock,\n onDragTransitionEnd: onTransitionEnd,\n onMeasureDragConstraints: onMeasureConstraints,\n})\n\nconst dragStyles = [\n \"user-select\",\n \"-webkit-user-select\",\n \"-webkit-touch-callout\",\n \"touch-action\",\n]\n\n/**\n * Apply the styles React's drag prop applies, so touch input drags\n * instead of panning the page, and text selection and native drag (ghost\n * image) don't interrupt the gesture. Returns a function that restores\n * the element's previous values.\n */\nfunction applyDragStyles(element: HTMLElement, axis?: \"x\" | \"y\") {\n const { style } = element\n const draggable = element.getAttribute(\"draggable\")\n const prev = dragStyles.map((key) => style.getPropertyValue(key))\n const touchAction = axis ? \"pan-\" + (axis === \"x\" ? \"y\" : \"x\") : \"none\"\n\n dragStyles.forEach((key, i) =>\n style.setProperty(key, i < 3 ? \"none\" : touchAction)\n )\n element.setAttribute(\"draggable\", \"false\")\n\n return () => {\n dragStyles.forEach((key, i) => style.setProperty(key, prev[i]))\n draggable === null\n ? element.removeAttribute(\"draggable\")\n : element.setAttribute(\"draggable\", draggable)\n }\n}\n\n/**\n * Make elements draggable. Returns a function that removes the gesture.\n *\n * ```javascript\n * const stop = drag(\"#box\", { axis: \"x\", constraints: { left: 0, right: 300 } })\n * ```\n *\n * The element's `x` and `y` are motion values shared with `animate()`\n * and `animateLayout()`, so these can animate the element after a drag.\n *\n * @public\n */\nexport function drag(\n elementOrSelector: ElementOrSelector,\n options: DragOptions = {}\n): VoidFunction {\n const props = toProps(options)\n const scope = activeScope.current\n\n /**\n * Run callbacks in the active scope, so work they start is collected.\n */\n if (scope) {\n for (const key in props) {\n const value = props[key as keyof MotionNodeOptions]\n if (typeof value === \"function\") {\n ;(props as any)[key] = scope.bind(value)\n }\n }\n }\n\n const cleanups = resolveElements(elementOrSelector).map((element) => {\n scope?.track(element)\n const [node, release] = acquireDragNode(element as HTMLElement)\n\n /**\n * Measure layout even when not animating it, as constraints and\n * layout-shift correction are computed from it.\n */\n node.setOptions({ alwaysMeasureLayout: true })\n\n const controls = new VisualElementDragControls(\n visualElementStore.get(element) as VisualElement<HTMLElement>,\n () => props\n )\n const removeListeners = controls.addListeners()\n const removeStyles = applyDragStyles(\n element as HTMLElement,\n options.axis\n )\n\n return () => {\n removeStyles()\n removeListeners?.()\n controls.cancel()\n controls.stopAnimation()\n release()\n }\n })\n\n return addToScope(() => cleanups.forEach((cleanup) => cleanup()))\n}\n"],"names":[],"mappings":"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"}