From 7f8b1386322b311fb5ef0d9a2cc1767a8e339cde Mon Sep 17 00:00:00 2001 From: 01zulfi <85733202+01zulfi@users.noreply.github.com> Date: Tue, 18 Aug 2026 16:22:04 +0500 Subject: [PATCH] web: drag/drop tab on sidebar to move note to notebook/tag/color Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com> --- apps/web/src/app.tsx | 3 +- apps/web/src/components/editor/action-bar.tsx | 87 ++++++++++++++++--- .../src/components/navigation-menu/index.tsx | 15 ++++ 3 files changed, 93 insertions(+), 12 deletions(-) diff --git a/apps/web/src/app.tsx b/apps/web/src/app.tsx index fa527b14f..2a15d8f8a 100644 --- a/apps/web/src/app.tsx +++ b/apps/web/src/app.tsx @@ -211,8 +211,7 @@ function DesktopAppContents() { minSize={50} maxSize={isTablet ? 0 : 500} style={{ - overflow: "initial", - zIndex: 3 + overflow: "initial" }} > { + const session = useEditorStore + .getState() + .getSession(tab.sessionId); + if ( + !session || + !("note" in session) || + session.type === "deleted" + ) { + return; + } + + const noteId = session.note.id; + if (!noteId) return; + + const dataTransfer = new DataTransfer(); + setDragData(dataTransfer, "note", [noteId]); + dispatchSyntheticDropToNavMenuItem( + point.x, + point.y, + dataTransfer + ); + }} renderItem={({ item: tab, index: i }) => { const session = useEditorStore .getState() @@ -714,16 +740,28 @@ function Tab(props: TabProps) { ); } +const VERTICAL_DRAG_OUT_THRESHOLD = 30; + +function isDraggedOutVertically(event: DragMoveEvent | DragEndEvent) { + return Math.abs(event.delta.y) >= VERTICAL_DRAG_OUT_THRESHOLD; +} + +const restrictToHorizontalUnlessDraggedOut: Modifier = ({ transform }) => + Math.abs(transform.y) < VERTICAL_DRAG_OUT_THRESHOLD + ? { ...transform, y: 0 } + : transform; + type ReorderableListProps = { items: T[]; renderItem: (props: { item: T; index: number }) => JSX.Element | null; moveItem: (from: number, to: number) => void; + onDropOutside: (item: T, point: { x: number; y: number }) => void; }; function ReorderableList( props: ReorderableListProps ) { - const { items, renderItem: Item, moveItem } = props; + const { items, renderItem: Item, moveItem, onDropOutside } = props; const sensors = useSensors( useSensor(MouseSensor, { activationConstraint: { @@ -735,6 +773,10 @@ function ReorderableList( }) ); const [activeItem, setActiveItem] = useState(); + const pointerPosition = useRef<{ x: number; y: number } | null>(null); + const pointerMoveListener = useRef<((event: PointerEvent) => void) | null>( + null + ); return ( ( // onDragCancel={(event) => {}} onDragStart={(event) => { setActiveItem(items.find((i) => i.id === event.active.id)); + + pointerPosition.current = null; + const onPointerMove = (event: PointerEvent) => { + pointerPosition.current = { x: event.clientX, y: event.clientY }; + }; + pointerMoveListener.current = onPointerMove; + window.addEventListener("pointermove", onPointerMove); }} onDragEnd={(event) => { const { active, over } = event; - const overId = over?.id as string; - if (overId && active.id !== overId) { - const transitionItems = items.slice(); - const newIndex = transitionItems.findIndex((i) => i.id === overId); - const oldIndex = transitionItems.findIndex((i) => i.id === active.id); - moveItem(oldIndex, newIndex); + if (isDraggedOutVertically(event)) { + const item = items.find((i) => i.id === active.id); + if (item && pointerPosition.current) { + onDropOutside(item, pointerPosition.current); + } + } else { + const overId = over?.id as string; + if (overId && active.id !== overId) { + const transitionItems = items.slice(); + const newIndex = transitionItems.findIndex((i) => i.id === overId); + const oldIndex = transitionItems.findIndex( + (i) => i.id === active.id + ); + moveItem(oldIndex, newIndex); + } } setActiveItem(undefined); + + if (pointerMoveListener.current) { + window.removeEventListener( + "pointermove", + pointerMoveListener.current + ); + pointerMoveListener.current = null; + } }} measuring={{ droppable: { strategy: MeasuringStrategy.Always } }} - modifiers={[restrictToHorizontalAxis]} + modifiers={[restrictToHorizontalUnlessDraggedOut]} > {items.map((item, index) => ( @@ -767,6 +833,7 @@ function ReorderableList( ))}