Skip to content

Commit

Permalink
refactor: use ResizeObserver track selected element size
Browse files Browse the repository at this point in the history
  • Loading branch information
bepyan committed Aug 15, 2024
1 parent 3af5748 commit c64d0fb
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 24 deletions.
Binary file modified bun.lockb
Binary file not shown.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@
"sonner": "^1.5.0",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
"usehooks-ts": "^3.1.0",
"zod": "^3.23.8",
"zustand": "^4.5.4"
},
Expand Down
2 changes: 0 additions & 2 deletions src/components/editor/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ type EditorActionMap = {
elementDetails: EditorElement;
};
CHANGE_CLICKED_ELEMENT: {
elementRef?: React.RefObject<HTMLDivElement>;
elementDetails?: EditorElement;
};
CHANGE_CURRENT_TAB_VALUE: {
Expand Down Expand Up @@ -320,7 +319,6 @@ const actionHandlers: {
return updateEditorHistory(editor, {
...editor.state,
selectedElement: payload.elementDetails ?? emptyElement,
selectedElementRef: payload.elementRef,
currentTabValue: newTabValue,
});
},
Expand Down
44 changes: 29 additions & 15 deletions src/components/editor/elements/element-helper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,41 @@ import {
GripVerticalIcon,
Trash2Icon,
} from "lucide-react";
import { useRef } from "react";
import { useCallback, useEffect, useState } from "react";
import { createPortal } from "react-dom";
import { useResizeObserver } from "usehooks-ts";
import { useEditor } from "~/components/editor/provider";
import { isValidSelectEditorElement } from "~/components/editor/util";
import { Badge } from "~/components/ui/badge";
import { Button } from "~/components/ui/button";
import { cn } from "~/lib/utils";

export default function ElementHelper() {
const { editor, dispatch } = useEditor();
const element = editor.state.selectedElement;
const elementRef = editor.state.selectedElementRef;

const emptyRef = useRef<HTMLDivElement>(null);
const { width, height } = useResizeObserver({
ref: elementRef ?? emptyRef,
box: "border-box",
});
const [layerStyle, setLayerStyle] = useState<React.CSSProperties>({});

const updateLayerStyle = useCallback((id: string) => {
const el = document.querySelector(`[data-element-id="${id}"]`);

if (!el) {
return;
}

const resizeObserver = new ResizeObserver(() => {
const { top, left, width, height } = el.getBoundingClientRect();
setLayerStyle({ top, left, width, height });
});

resizeObserver.observe(el, { box: "border-box" });
return () => {
resizeObserver.unobserve(el);
};
}, []);

useEffect(() => {
return updateLayerStyle(element.id);
}, [element.id, updateLayerStyle]);

const handleMoveUp = (e: React.MouseEvent) => {
e.stopPropagation();
Expand All @@ -34,6 +51,7 @@ export default function ElementHelper() {
elementId: element.id,
},
});
updateLayerStyle(element.id);
};

const handleMoveDown = (e: React.MouseEvent) => {
Expand All @@ -44,6 +62,7 @@ export default function ElementHelper() {
elementId: element.id,
},
});
updateLayerStyle(element.id);
};

const handleDeleteElement = () => {
Expand All @@ -58,14 +77,9 @@ export default function ElementHelper() {
return (
typeof window !== "undefined" &&
createPortal(
!editor.state.isPreviewMode && elementRef?.current && (
!editor.state.isPreviewMode && isValidSelectEditorElement(element) && (
<div
style={{
top: elementRef.current.getBoundingClientRect().top,
left: elementRef.current.getBoundingClientRect().left,
width,
height,
}}
style={layerStyle}
className={cn(
"fixed z-50",
!editor.state.isPreviewMode && "ring-1 ring-primary",
Expand Down
6 changes: 1 addition & 5 deletions src/components/editor/elements/element-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { useRef } from "react";
import { useEditor } from "~/components/editor/provider";
import type { EditorElement } from "~/components/editor/type";
import { cn } from "~/lib/utils";
Expand All @@ -16,15 +15,12 @@ export default function ElementWrapper({
const { editor, dispatch } = useEditor();
const isRoot = element.type === "__body";

const rootRef = useRef<HTMLDivElement>(null);

const handleClick = (e: React.MouseEvent) => {
e.stopPropagation();

dispatch({
type: "CHANGE_CLICKED_ELEMENT",
payload: {
elementRef: rootRef,
elementDetails: element,
},
});
Expand All @@ -33,7 +29,7 @@ export default function ElementWrapper({
return (
<div
{...props}
ref={rootRef}
data-element-id={element.id}
style={element.styles}
className={cn(
"relative w-full transition-all",
Expand Down
1 change: 0 additions & 1 deletion src/components/editor/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export type InferEditorElement<K extends EditorElementType> = Extract<
export type EditorState = {
elements: EditorElement[];
selectedElement: EditorElement;
selectedElementRef?: React.RefObject<HTMLDivElement>;
currentTabValue: EditorTabTypeValue;
device: DeviceType;
isPreviewMode: boolean;
Expand Down

0 comments on commit c64d0fb

Please sign in to comment.