Skip to content

Commit

Permalink
Merge pull request #542 from Lemoncode/feature/#501-allow-rearrange-p…
Browse files Browse the repository at this point in the history
…ages-d&d

Feature/#501 allow rearrange pages d&d
  • Loading branch information
brauliodiez authored Nov 17, 2024
2 parents a408fa9 + 06a3163 commit bb6cbb0
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 30 deletions.
1 change: 1 addition & 0 deletions src/core/providers/canvas/canvas.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export interface CanvasContextModel {
setActivePage: (pageId: string) => void;
deletePage: (pageIndex: number) => void;
editPageTitle: (pageIndex: number, newName: string) => void;
swapPages: (id1: string, id2: string) => void;
activePageIndex: number;
isThumbnailContextMenuVisible: boolean;
setIsThumbnailContextMenuVisible: React.Dispatch<
Expand Down
15 changes: 15 additions & 0 deletions src/core/providers/canvas/canvas.provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,20 @@ export const CanvasProvider: React.FC<Props> = props => {
);
};

const swapPages = (id1: string, id2: string) => {
setDocument(lastDocument =>
produce(lastDocument, draft => {
const index1 = draft.pages.findIndex(page => page.id === id1);
const index2 = draft.pages.findIndex(page => page.id === id2);
if (index1 !== -1 && index2 !== -1) {
const temp = draft.pages[index1];
draft.pages[index1] = draft.pages[index2];
draft.pages[index2] = temp;
}
})
);
};

const pasteShapes = (shapes: ShapeModel[]) => {
const newShapes: ShapeModel[] = shapes.map(shape => {
shape.id = uuidv4();
Expand Down Expand Up @@ -307,6 +321,7 @@ export const CanvasProvider: React.FC<Props> = props => {
setActivePage,
deletePage,
editPageTitle,
swapPages,
activePageIndex: document.activePageIndex,
isThumbnailContextMenuVisible,
setIsThumbnailContextMenuVisible,
Expand Down
56 changes: 29 additions & 27 deletions src/pods/canvas/use-monitor-shape.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,38 +24,40 @@ export const useMonitorShape = (
if (!destination) return;
invariant(destination);

const type = source.data.type as ShapeType;
if (source.data.type !== 'thumbPage') {
const type = source.data.type as ShapeType;

const screenPosition =
extractScreenCoordinatesFromPragmaticLocation(location);
const screenPosition =
extractScreenCoordinatesFromPragmaticLocation(location);

let positionX = 0;
let positionY = 0;
if (screenPosition) {
invariant(dropRef.current);
const { x: divRelativeX, y: divRelativeY } =
portScreenPositionToDivCoordinates(
dropRef.current as HTMLDivElement,
screenPosition
);
let positionX = 0;
let positionY = 0;
if (screenPosition) {
invariant(dropRef.current);
const { x: divRelativeX, y: divRelativeY } =
portScreenPositionToDivCoordinates(
dropRef.current as HTMLDivElement,
screenPosition
);

invariant(stageRef.current);
const stage = stageRef.current;
const { scrollLeft, scrollTop } = getScrollFromDiv(
dropRef as unknown as React.MutableRefObject<HTMLDivElement>
);
const konvaCoord = convertFromDivElementCoordsToKonvaCoords(stage, {
screenPosition,
relativeDivPosition: { x: divRelativeX, y: divRelativeY },
scroll: { x: scrollLeft, y: scrollTop },
});
invariant(stageRef.current);
const stage = stageRef.current;
const { scrollLeft, scrollTop } = getScrollFromDiv(
dropRef as unknown as React.MutableRefObject<HTMLDivElement>
);
const konvaCoord = convertFromDivElementCoordsToKonvaCoords(stage, {
screenPosition,
relativeDivPosition: { x: divRelativeX, y: divRelativeY },
scroll: { x: scrollLeft, y: scrollTop },
});

positionX =
konvaCoord.x -
calculateShapeOffsetToXDropCoordinate(konvaCoord.x, type);
positionY = konvaCoord.y;
positionX =
konvaCoord.x -
calculateShapeOffsetToXDropCoordinate(konvaCoord.x, type);
positionY = konvaCoord.y;
}
addNewShape(type, positionX, positionY);
}
addNewShape(type, positionX, positionY);
},
});
}, []);
Expand Down
1 change: 0 additions & 1 deletion src/pods/canvas/use-multiple-selection-shape.hook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ export const useMultipleSelectionShapeHook = (
e: KonvaEventObject<MouseEvent> | KonvaEventObject<TouchEvent>
) => {
const transformerRect = transformerRef.current?.getClientRect();
console.log(transformerRect);
const mousePosition = e.target?.getStage()?.getPointerPosition() ?? {
x: 0,
y: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ export const ThumbPageContextMenu: React.FunctionComponent<
duplicatePage(pageIndex);
break;
case ContextButtonType.Rename:
console.log('Rename');
setPageTitleBeingEdited(pageIndex);
break;
case ContextButtonType.Delete:
Expand Down
58 changes: 58 additions & 0 deletions src/pods/thumb-pages/components/drag-drop-thumb.hook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useCanvasContext } from '@/core/providers';
import {
draggable,
dropTargetForElements,
} from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import { useEffect, useState } from 'react';
import invariant from 'tiny-invariant';

export const useDragDropThumb = (
divRef: React.RefObject<HTMLDivElement>,
pageIndex: number
) => {
const { fullDocument } = useCanvasContext();
const page = fullDocument.pages[pageIndex];
const [dragging, setDragging] = useState<boolean>(false);
const [isDraggedOver, setIsDraggedOver] = useState(false);

// Drag
useEffect(() => {
const el = divRef.current;
invariant(el);
return draggable({
element: el,
getInitialData: () => ({
pageId: page.id, //fullDocument.pages[pageIndex].id,
type: 'thumbPage',
}),
onDragStart: () => {
setDragging(true);
},
onDrop: () => setDragging(false),
});
}, [divRef.current, pageIndex, fullDocument.pages]);

// Drop
useEffect(() => {
const el = divRef.current;
invariant(el);

return dropTargetForElements({
element: el,
getData: () => ({
pageId: page.id, //fullDocument.pages[pageIndex].id,
type: 'thumbPage',
}),
onDragEnter: () => setIsDraggedOver(true),
onDragLeave: () => setIsDraggedOver(false),
onDrop: () => {
setIsDraggedOver(false);
},
});
}, [divRef.current, pageIndex, fullDocument.pages]);

return {
dragging,
isDraggedOver,
};
};
10 changes: 9 additions & 1 deletion src/pods/thumb-pages/components/thumb-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import { ThumbPageContextMenu } from './context-menu';
import { useContextMenu } from '../use-context-menu-thumb.hook';
import { CaretDown } from '@/common/components/icons';
import classes from './thumb-page.module.css';

import React from 'react';
import { useDragDropThumb } from './drag-drop-thumb.hook';

interface Props {
pageIndex: number;
Expand All @@ -19,9 +21,9 @@ interface Props {
}

export const ThumbPage: React.FunctionComponent<Props> = props => {
const { fullDocument, activePageIndex } = useCanvasContext();
const { pageIndex, onSetActivePage, setPageTitleBeingEdited, isVisible } =
props;
const { fullDocument, activePageIndex } = useCanvasContext();
const page = fullDocument.pages[pageIndex];
const shapes = page.shapes;
const fakeShapeRefs = useRef<ShapeRefs>({});
Expand All @@ -35,6 +37,8 @@ export const ThumbPage: React.FunctionComponent<Props> = props => {
const divRef = useRef<HTMLDivElement>(null);
const [key, setKey] = React.useState(0);

const { dragging, isDraggedOver } = useDragDropThumb(divRef, pageIndex);

const handleResizeAndForceRedraw = () => {
const newCanvaSize = {
width: divRef.current?.clientWidth || 1,
Expand Down Expand Up @@ -85,6 +89,10 @@ export const ThumbPage: React.FunctionComponent<Props> = props => {
className={classes.container}
onClick={() => onSetActivePage(page.id)}
onContextMenu={handleShowContextMenu}
style={{
opacity: dragging ? 0.4 : 1,
background: isDraggedOver ? 'lightblue' : 'white',
}}
key={key}
>
<div className={classes.noclick}>
Expand Down
25 changes: 25 additions & 0 deletions src/pods/thumb-pages/monitor-drop-thumb.hook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useEffect } from 'react';
import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import { useCanvasContext } from '@/core/providers';

export const useMonitorDropThumb = () => {
const { fullDocument, swapPages } = useCanvasContext();

// Monitor
useEffect(() => {
return monitorForElements({
onDrop({ source, location }) {
const destination = location.current.dropTargets[0];
if (!destination || source.data.pageId === destination.data.pageId) {
return;
}
if (destination.data.type === 'thumbPage') {
swapPages(
String(source.data.pageId),
String(destination.data.pageId)
);
}
},
});
}, [fullDocument.pages]);
};
3 changes: 3 additions & 0 deletions src/pods/thumb-pages/thumb-pages.pod.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import classes from './thumb-pages.module.css';
import { useCanvasContext } from '@/core/providers';
import { PageTitleInlineEdit, ThumbPage } from './components';
import { PlusIcon } from '@/common/components/icons';
import { useMonitorDropThumb } from './monitor-drop-thumb.hook';

interface Props {
isVisible: boolean;
Expand All @@ -24,6 +25,8 @@ export const ThumbPagesPod: React.FC<Props> = props => {
setActivePage(pageId);
};

useMonitorDropThumb();

return (
<div className={classes.container}>
{fullDocument.pages.map((page, index) => (
Expand Down

0 comments on commit bb6cbb0

Please sign in to comment.