-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
216ac6e
commit 33819d9
Showing
5 changed files
with
93 additions
and
81 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"basehub": patch | ||
--- | ||
|
||
Refresh on draft mode activation, styling corrections, full drag handle. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
148 changes: 80 additions & 68 deletions
148
packages/basehub/src/next/toolbar/components/drag-handle.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,83 +1,95 @@ | ||
import * as React from "react"; | ||
import s from "../toolbar.module.scss"; | ||
|
||
export const DragHandle = ({ | ||
onDrag, | ||
onDragStart, | ||
onDragEnd, | ||
children, | ||
}: { | ||
onDrag: ({ x, y }: { x: number; y: number }) => void; | ||
onDragStart: () => void; | ||
onDragEnd: () => void; | ||
children: React.ReactNode; | ||
}) => { | ||
const [isDragging, setIsDragging] = React.useState(false); | ||
const initialPointer = React.useRef({ x: 0, y: 0 }); | ||
const initialToolbar = React.useRef({ x: 0, y: 0 }); | ||
export type DragHandle = { hasDragged: boolean }; | ||
|
||
const handleDrag = React.useCallback( | ||
(e: PointerEvent) => { | ||
if (!isDragging) return; | ||
export const DragHandle = React.forwardRef( | ||
( | ||
{ | ||
onDrag, | ||
children, | ||
}: { | ||
onDrag: ({ x, y }: { x: number; y: number }) => void; | ||
children: React.ReactNode; | ||
}, | ||
ref | ||
) => { | ||
const [isDragging, setIsDragging] = React.useState(false); | ||
const initialPointer = React.useRef({ x: 0, y: 0 }); | ||
const initialToolbar = React.useRef({ x: 0, y: 0 }); | ||
const hasDragged = React.useRef(false); | ||
|
||
const deltaX = e.clientX - initialPointer.current.x; | ||
const deltaY = e.clientY - initialPointer.current.y; | ||
const newToolbarX = initialToolbar.current.x + deltaX; | ||
const newToolbarY = initialToolbar.current.y + deltaY; | ||
React.useImperativeHandle(ref, () => ({ | ||
hasDragged: hasDragged.current, | ||
})); | ||
|
||
onDrag({ x: newToolbarX, y: newToolbarY }); | ||
}, | ||
[isDragging, onDrag] | ||
); | ||
const handleDrag = React.useCallback( | ||
(e: PointerEvent) => { | ||
if (!isDragging) return; | ||
|
||
React.useLayoutEffect(() => { | ||
if (!isDragging) return; | ||
const deltaX = e.clientX - initialPointer.current.x; | ||
const deltaY = e.clientY - initialPointer.current.y; | ||
const newToolbarX = initialToolbar.current.x + deltaX; | ||
const newToolbarY = initialToolbar.current.y + deltaY; | ||
|
||
window.addEventListener("pointermove", handleDrag); | ||
// set hasDragged to true if the pointer has moved more than 5 px from the initial position | ||
if (Math.abs(deltaX) > 2 || Math.abs(deltaY) > 2) { | ||
hasDragged.current = true; | ||
} | ||
|
||
return () => { | ||
window.removeEventListener("pointermove", handleDrag); | ||
}; | ||
}, [isDragging, onDrag, handleDrag]); | ||
onDrag({ x: newToolbarX, y: newToolbarY }); | ||
}, | ||
[isDragging, onDrag] | ||
); | ||
|
||
React.useLayoutEffect(() => { | ||
// disable drag on pointer up | ||
if (!isDragging) return; | ||
React.useLayoutEffect(() => { | ||
if (!isDragging) return; | ||
|
||
const handlePointerUp = () => { | ||
setIsDragging(false); | ||
onDragEnd(); | ||
}; | ||
window.addEventListener("pointermove", handleDrag); | ||
|
||
window.addEventListener("pointerup", handlePointerUp); | ||
return () => { | ||
window.removeEventListener("pointermove", handleDrag); | ||
}; | ||
}, [isDragging, onDrag, handleDrag]); | ||
|
||
return () => { | ||
window.removeEventListener("pointerup", handlePointerUp); | ||
}; | ||
}, [isDragging, onDragEnd]); | ||
React.useLayoutEffect(() => { | ||
// disable drag on pointer up | ||
if (!isDragging) { | ||
hasDragged.current = false; | ||
return | ||
} | ||
|
||
return ( | ||
<span | ||
className={`${s.dragHandle} ${isDragging ? s.dragging : ""}`} | ||
onPointerDown={(e) => { | ||
e.stopPropagation(); | ||
const handle = e.currentTarget as HTMLSpanElement | null; | ||
if (!handle) return; | ||
initialPointer.current = { x: e.clientX, y: e.clientY }; | ||
const rect = handle.getBoundingClientRect(); | ||
initialToolbar.current.x = rect.left; | ||
initialToolbar.current.y = rect.top; | ||
setIsDragging(true); | ||
onDragStart(); | ||
}} | ||
onPointerUp={() => { | ||
const handlePointerUp = () => { | ||
setIsDragging(false); | ||
if (isDragging) { | ||
onDragEnd(); | ||
} | ||
}} | ||
> | ||
{children} | ||
</span> | ||
); | ||
}; | ||
}; | ||
|
||
window.addEventListener("pointerup", handlePointerUp); | ||
|
||
return () => { | ||
window.removeEventListener("pointerup", handlePointerUp); | ||
}; | ||
}, [isDragging]); | ||
|
||
return ( | ||
<span | ||
draggable | ||
className={`${s.dragHandle} ${isDragging ? s.dragging : ""}`} | ||
onPointerDown={(e) => { | ||
e.stopPropagation(); | ||
const handle = e.currentTarget as HTMLSpanElement | null; | ||
if (!handle) return; | ||
initialPointer.current = { x: e.clientX, y: e.clientY }; | ||
const rect = handle.getBoundingClientRect(); | ||
initialToolbar.current.x = rect.left; | ||
initialToolbar.current.y = rect.top; | ||
setIsDragging(true); | ||
}} | ||
onPointerUp={() => { | ||
setIsDragging(false); | ||
}} | ||
> | ||
{children} | ||
</span> | ||
); | ||
} | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters