import { cloneVNode, defineComponent, reactive, useSlots, h } from "vue"; import * as PropTypes from "../lib/type/PropTypes"; import Draggable from "../lib/Draggable"; import { vuePropsMake } from "../lib/type/PropTypes"; import { omit } from "lodash"; interface AppProps { name?: string; } const propAppTypes = {}; const defaultAppProps = {}; export const vueAppPropsType = vuePropsMake(propAppTypes, defaultAppProps); const App = defineComponent((props) => { const slots = useSlots(); const state = reactive({ activeDrags: 0, deltaPosition: { x: 0, y: 0, }, controlledPosition: { x: -400, y: 200, }, }); const handleDrag = (e: any, ui: any) => { const { x, y } = state.deltaPosition; state.deltaPosition = { x: x + ui.deltaX, y: y + ui.deltaY, }; }; const onStart = () => { console.log(1) state.activeDrags = ++state.activeDrags; }; const onStop = () => { state.activeDrags = --state.activeDrags; }; const onDrop = (e: any) => { state.activeDrags = --state.activeDrags; if (e.target.classList.contains("drop-target")) { alert("Dropped!"); e.target.classList.remove("hovered"); } }; const onDropAreaMouseEnter = (e: any) => { if (state.activeDrags) { e.target.classList.add("hovered"); } }; const onDropAreaMouseLeave = (e: any) => { e.target.classList.remove("hovered"); }; // For controlled component const adjustXPos = (e: any) => { e.preventDefault(); e.stopPropagation(); const { x, y } = state.controlledPosition; state.controlledPosition = { x: x - 10, y }; }; const adjustYPos = (e: any) => { e.preventDefault(); e.stopPropagation(); const { controlledPosition } = state; const { x, y } = controlledPosition; state.controlledPosition = { x, y: y - 10 }; }; const onControlledDrag = (e: any, position: any) => { const { x, y } = position; state.controlledPosition = { x, y }; }; const onControlledDragStop = (e: any, position: any) => { onControlledDrag(e, position); onStop(); }; return () => { const dragHandlers: any = { onStart: onStart, onStop: onStop }; const { deltaPosition, controlledPosition } = state; return (

React Draggable

Active DragHandlers: {state.activeDrags}

Demo Source

I can be dragged anywhere
} > I can only be dragged horizonally (x axis) } > I can only be dragged vertically (y axis) } > {/*// @ts-ignore*/} false} children={
I don't want to be dragged
} >
I track my deltas
x: {deltaPosition.x.toFixed(0)}, y: {deltaPosition.y.toFixed(0)}
} >
Drag here
You must click my handle to drag me
} >
{/*// @ts-ignore*/}
Drag here
I have long scrollable content with a handle {"\n" + Array(40).fill("x").join("\n")}
} >
Can't drag here
Dragging here works
} >
I snap to a 25 x 25 grid} > I snap to a 50 x 50 grid} > I can only be moved 100px in any direction. } > I can detect drops from the next box. } > I can be dropped onto another box. } >
I can only be moved within my offsetParent.

Both parent padding and child margin work properly.
} > I also can only be moved within my offsetParent.

Both parent padding and child margin work properly.
} > I can only be moved within the confines of the body element. } > I already have an absolute position. } > I use rem instead of{" "} px for my transforms. I also have absolute positioning.

I depend on a CSS hack to avoid double absolute positioning. } > } >
{ "I have a default position of {x: 25, y: 25}, so I'm slightly offset." } } > { "I have a default position based on percents {x: '-10%', y: '-10%'}, so I'm slightly offset." } } > My position can be changed programmatically.
I have a drag handler to sync state.
Adjust x ({controlledPosition.x})
Adjust y ({controlledPosition.y})
} >
My position can be changed programmatically.
I have a dragStop handler to sync state.
Adjust x ({controlledPosition.x})
Adjust y ({controlledPosition.y})
} >
); }; }); App.props = vueAppPropsType; App.name = "App"; export default App; interface RemWrapperProps { children?: any; remBaseline?: number; style?: any; } const propRemWrapperTypes = { children: PropTypes.any, remBaseline: PropTypes.number, style: [PropTypes.object, PropTypes.string], }; const defaultRemWrapperProps = {}; export const vueRemWrapperPropsType = vuePropsMake( propRemWrapperTypes, defaultRemWrapperProps ); const RemWrapper = defineComponent((props) => { const slots = useSlots(); function translateTransformToRem(transform: any, remBaseline = 16) { const convertedValues = transform .replace("translate(", "") .replace(")", "") .split(",") .map((px: any) => px.replace("px", "")) .map((px: any) => parseInt(px, 10) / remBaseline) .map((x: any) => `${x}rem`); const [x, y] = convertedValues; return `translate(${x}, ${y})`; } return () => { const { children, remBaseline = 16, style } = props; const child = children; const editedStyle = { ...child.props.style, ...style, transform: translateTransformToRem(style.transform, remBaseline), }; return cloneVNode(child, { ...child.props, ...omit(props, "children"), style: editedStyle, }); }; }); RemWrapper.props = vueRemWrapperPropsType; RemWrapper.name = "RemWrapper"; export { App as ExampleApp };