diff --git a/client/src/features/page/Page.style.ts b/client/src/features/page/Page.style.ts
index 563c3748..8483df13 100644
--- a/client/src/features/page/Page.style.ts
+++ b/client/src/features/page/Page.style.ts
@@ -26,11 +26,97 @@ export const pageHeader = css({
},
});
-export const resizeHandle = css({
+const baseResizeHandle = css({
+ zIndex: 1,
position: "absolute",
- right: "-10px",
- bottom: "-10px",
- width: "32px",
- height: "32px",
- cursor: "se-resize",
});
+
+export const resizeHandles = {
+ top: cx(
+ baseResizeHandle,
+ css({
+ top: "-5px",
+ left: "5px",
+ right: "5px",
+ height: "10px",
+ cursor: "n-resize",
+ }),
+ ),
+
+ bottom: cx(
+ baseResizeHandle,
+ css({
+ left: "5px",
+ right: "5px",
+ bottom: "-5px",
+ height: "10px",
+ cursor: "s-resize",
+ }),
+ ),
+
+ left: cx(
+ baseResizeHandle,
+ css({
+ top: "5px",
+ left: "-5px",
+ bottom: "5px",
+ width: "10px",
+ cursor: "w-resize",
+ }),
+ ),
+
+ right: cx(
+ baseResizeHandle,
+ css({
+ top: "5px",
+ right: "-5px",
+ bottom: "5px",
+ width: "10px",
+ cursor: "e-resize",
+ }),
+ ),
+
+ topLeft: cx(
+ baseResizeHandle,
+ css({
+ top: "-10px",
+ left: "-10px",
+ width: "24px",
+ height: "24px",
+ cursor: "nw-resize",
+ }),
+ ),
+
+ topRight: cx(
+ baseResizeHandle,
+ css({
+ top: "-10px",
+ right: "-10px",
+ width: "24px",
+ height: "24px",
+ cursor: "ne-resize",
+ }),
+ ),
+
+ bottomLeft: cx(
+ baseResizeHandle,
+ css({
+ left: "-10px",
+ bottom: "-10px",
+ width: "24px",
+ height: "24px",
+ cursor: "sw-resize",
+ }),
+ ),
+
+ bottomRight: cx(
+ baseResizeHandle,
+ css({
+ right: "-10px",
+ bottom: "-10px",
+ width: "24px",
+ height: "24px",
+ cursor: "se-resize",
+ }),
+ ),
+};
diff --git a/client/src/features/page/Page.tsx b/client/src/features/page/Page.tsx
index 23bf3d15..a376a5fd 100644
--- a/client/src/features/page/Page.tsx
+++ b/client/src/features/page/Page.tsx
@@ -2,12 +2,10 @@ import { serializedEditorDataProps } from "@noctaCrdt/Interfaces";
import { motion, AnimatePresence } from "framer-motion";
import { Editor } from "@features/editor/Editor";
import { Page as PageType } from "@src/types/page";
-import { pageAnimation, resizeHandleAnimation } from "./Page.animation";
-import { pageContainer, pageHeader, resizeHandle } from "./Page.style";
-
+import { pageContainer, pageHeader, resizeHandles } from "./Page.style";
import { PageControlButton } from "./components/PageControlButton/PageControlButton";
import { PageTitle } from "./components/PageTitle/PageTitle";
-import { usePage } from "./hooks/usePage";
+import { DIRECTIONS, usePage } from "./hooks/usePage";
interface PageProps extends PageType {
handlePageSelect: ({ pageId, isSidebar }: { pageId: string; isSidebar?: boolean }) => void;
@@ -45,17 +43,12 @@ export const Page = ({
return (
-
-
-
+ {DIRECTIONS.map((direction) => (
+ pageResize(e, direction)}
+ />
+ ))}
+
);
};
diff --git a/client/src/features/page/hooks/usePage.ts b/client/src/features/page/hooks/usePage.ts
index 61cbf7b5..088d6831 100644
--- a/client/src/features/page/hooks/usePage.ts
+++ b/client/src/features/page/hooks/usePage.ts
@@ -5,6 +5,18 @@ import { useIsSidebarOpen } from "@stores/useSidebarStore";
import { Position, Size } from "@src/types/page";
const PADDING = SPACING.MEDIUM * 2;
+export const DIRECTIONS = [
+ "top",
+ "bottom",
+ "left",
+ "right",
+ "topLeft",
+ "topRight",
+ "bottomLeft",
+ "bottomRight",
+] as const;
+
+type Direction = (typeof DIRECTIONS)[number];
export const usePage = ({ x, y }: Position) => {
const [position, setPosition] = useState({ x, y });
@@ -68,28 +80,117 @@ export const usePage = ({ x, y }: Position) => {
document.addEventListener("pointerup", handleDragEnd);
};
- const pageResize = (e: React.MouseEvent) => {
+ const pageResize = (e: React.MouseEvent, direction: Direction) => {
e.preventDefault();
const startX = e.clientX;
const startY = e.clientY;
const startWidth = size.width;
const startHeight = size.height;
+ const startPosition = { x: position.x, y: position.y };
const resize = (e: MouseEvent) => {
const deltaX = e.clientX - startX;
const deltaY = e.clientY - startY;
- const newWidth = Math.max(
- PAGE.MIN_WIDTH,
- Math.min(startWidth + deltaX, window.innerWidth - position.x - getSidebarWidth() - PADDING),
- );
-
- const newHeight = Math.max(
- PAGE.MIN_HEIGHT,
- Math.min(startHeight + deltaY, window.innerHeight - position.y - PADDING),
- );
+ let newWidth = startWidth;
+ let newHeight = startHeight;
+ let newX = startPosition.x;
+ let newY = startPosition.y;
+
+ switch (direction) {
+ case "right": {
+ newWidth = Math.min(
+ window.innerWidth - startPosition.x - getSidebarWidth() - PADDING,
+ Math.max(PAGE.MIN_WIDTH, startWidth + deltaX),
+ );
+ break;
+ }
+
+ case "left": {
+ newWidth = Math.min(
+ startPosition.x + startWidth,
+ Math.max(PAGE.MIN_WIDTH, startWidth - deltaX),
+ );
+ newX = Math.max(0, startPosition.x + startWidth - newWidth);
+ break;
+ }
+
+ case "bottom": {
+ newHeight = Math.min(
+ window.innerHeight - startPosition.y - PADDING,
+ Math.max(PAGE.MIN_HEIGHT, startHeight + deltaY),
+ );
+ break;
+ }
+
+ case "top": {
+ newHeight = Math.min(
+ startPosition.y + startHeight,
+ Math.max(PAGE.MIN_HEIGHT, startHeight - deltaY),
+ );
+ newY = Math.max(0, startPosition.y + startHeight - newHeight);
+ break;
+ }
+
+ case "topLeft": {
+ newHeight = Math.min(
+ startPosition.y + startHeight,
+ Math.max(PAGE.MIN_HEIGHT, startHeight - deltaY),
+ );
+ newY = Math.max(0, startPosition.y + startHeight - newHeight);
+
+ newWidth = Math.min(
+ startPosition.x + startWidth,
+ Math.max(PAGE.MIN_WIDTH, startWidth - deltaX),
+ );
+ newX = Math.max(0, startPosition.x + startWidth - newWidth);
+ break;
+ }
+
+ case "topRight": {
+ newHeight = Math.min(
+ startPosition.y + startHeight,
+ Math.max(PAGE.MIN_HEIGHT, startHeight - deltaY),
+ );
+ newY = Math.max(0, startPosition.y + startHeight - newHeight);
+
+ newWidth = Math.min(
+ window.innerWidth - startPosition.x - getSidebarWidth() - PADDING,
+ Math.max(PAGE.MIN_WIDTH, startWidth + deltaX),
+ );
+ break;
+ }
+
+ case "bottomLeft": {
+ newHeight = Math.min(
+ window.innerHeight - startPosition.y - PADDING,
+ Math.max(PAGE.MIN_HEIGHT, startHeight + deltaY),
+ );
+
+ newWidth = Math.min(
+ startPosition.x + startWidth,
+ Math.max(PAGE.MIN_WIDTH, startWidth - deltaX),
+ );
+ newX = Math.max(0, startPosition.x + startWidth - newWidth);
+ break;
+ }
+
+ case "bottomRight": {
+ newHeight = Math.min(
+ window.innerHeight - startPosition.y - PADDING,
+ Math.max(PAGE.MIN_HEIGHT, startHeight + deltaY),
+ );
+
+ newWidth = Math.min(
+ window.innerWidth - startPosition.x - getSidebarWidth() - PADDING,
+ Math.max(PAGE.MIN_WIDTH, startWidth + deltaX),
+ );
+ break;
+ }
+ }
setSize({ width: newWidth, height: newHeight });
+ setPosition({ x: newX, y: newY });
};
const stopResize = () => {