From 20786108659d74ff9b7d4b004b87b9b422ae46bb Mon Sep 17 00:00:00 2001 From: d9m0n4 Date: Wed, 9 Apr 2025 23:56:58 +0300 Subject: [PATCH] feat(48000116): sync board elements with YJS --- packages/pkg.module.board/Canvas.tsx | 3 + packages/pkg.module.board/CanvasLayer.tsx | 20 +- .../components/SelectedElementToolbar.tsx | 57 +++-- .../components/Shapes/LineShape.tsx | 4 + packages/pkg.module.board/hooks/index.ts | 1 + .../hooks/useElementHandlers.ts | 145 +++++++++++-- packages/pkg.module.board/hooks/useYJS.tsx | 24 +++ packages/pkg.module.board/services/index.ts | 1 + .../pkg.module.board/services/yjsService.ts | 66 ++++++ .../pkg.module.board/store/useBoardStore.ts | 16 +- packages/pkg.module.board/types.ts | 17 +- .../useWhiteboardCollaborative.ts | 194 ++++++++++++++---- 12 files changed, 466 insertions(+), 82 deletions(-) create mode 100644 packages/pkg.module.board/hooks/useYJS.tsx create mode 100644 packages/pkg.module.board/services/index.ts create mode 100644 packages/pkg.module.board/services/yjsService.ts diff --git a/packages/pkg.module.board/Canvas.tsx b/packages/pkg.module.board/Canvas.tsx index a602063e..34dc0542 100644 --- a/packages/pkg.module.board/Canvas.tsx +++ b/packages/pkg.module.board/Canvas.tsx @@ -6,6 +6,7 @@ import { useCanvasHandlers, useZoom } from './hooks'; import { useStage } from './providers'; import { BackgroundLayer, SelectedElementToolbar, Navbar, ZoomMenu } from './components'; import { CanvasLayer } from './CanvasLayer'; +import { useWhiteboardCollaborative } from './useWhiteboardCollaborative'; export const Canvas = () => { const { stageRef } = useStage(); @@ -13,6 +14,8 @@ export const Canvas = () => { const { handleOnWheel, handleMouseUp, handleMouseDown, handleMouseMove, handleDragEnd } = useCanvasHandlers(); + useWhiteboardCollaborative({ roomId: 'test/slate-yjs-demo' }); + const { handleResetZoom, handleZoomIn, handleZoomOut } = useZoom(stageRef); const boardWidth = window.innerWidth; diff --git a/packages/pkg.module.board/CanvasLayer.tsx b/packages/pkg.module.board/CanvasLayer.tsx index 0e8b5d80..6e8eea74 100644 --- a/packages/pkg.module.board/CanvasLayer.tsx +++ b/packages/pkg.module.board/CanvasLayer.tsx @@ -6,11 +6,10 @@ import { useStage } from './providers'; import { useElementHandlers, useIsStageScaling } from './hooks'; export const CanvasLayer = memo(() => { - const { boardElements, selectedElementId, selectedTool, setSelectToolbarPosition } = - useBoardStore(); + const { boardElements, selectedElementId, selectedTool, updateElement } = useBoardStore(); const { layerRef, transformerRef } = useStage(); - const { onChangeTransformerPosition } = useElementHandlers(); + const { handleTransformEnd, handleDragStart, onChangeTransformerPosition } = useElementHandlers(); const { isScaling } = useIsStageScaling(); useEffect(() => { @@ -20,23 +19,20 @@ export const CanvasLayer = memo(() => { transformerRef.current.nodes([selectedNode]); transformerRef.current.getLayer()?.batchDraw(); const box = transformerRef.current.getClientRect(); - setSelectToolbarPosition({ - x: box.x, - y: box.y, - }); + updateElement('toolbar', { x: box.x, y: box.y }); } else { transformerRef.current.nodes([]); } } else { transformerRef.current?.nodes([]); } - }, [layerRef, selectedElementId, setSelectToolbarPosition, transformerRef]); + }, [layerRef, selectedElementId, transformerRef, updateElement]); useEffect(() => { if (!isScaling && selectedElementId) { onChangeTransformerPosition(); } - }, [isScaling, selectedElementId, onChangeTransformerPosition]); + }, [isScaling, onChangeTransformerPosition, selectedElementId]); return ( @@ -47,14 +43,16 @@ export const CanvasLayer = memo(() => { )} diff --git a/packages/pkg.module.board/components/SelectedElementToolbar.tsx b/packages/pkg.module.board/components/SelectedElementToolbar.tsx index 4c4c3300..6edeeaf9 100644 --- a/packages/pkg.module.board/components/SelectedElementToolbar.tsx +++ b/packages/pkg.module.board/components/SelectedElementToolbar.tsx @@ -1,15 +1,52 @@ +import { useEffect, useMemo } from 'react'; import { Button } from '@xipkg/button'; import { Trash, Copy, MoreVert } from '@xipkg/icons'; import { useBoardStore } from '../store'; import { useIsStageScaling } from '../hooks'; +import { ToolbarElement } from '../types'; export const SelectedElementToolbar = () => { - const { selectToolbarPosition, selectedTool, selectedElementId, removeElement, selectElement } = - useBoardStore(); + const { + selectedTool, + selectedElementId, + removeElement, + selectElement, + boardElements, + isElementTransforming, + selectToolbarPosition, + } = useBoardStore(); const { isScaling } = useIsStageScaling(); - if (!selectedElementId || selectedTool !== 'select') { + useEffect(() => { + if (!selectedElementId) { + removeElement('toolbar'); + } + }, [removeElement, selectedElementId]); + + const position = useMemo(() => { + if (selectToolbarPosition && (selectToolbarPosition.x !== 0 || selectToolbarPosition.y !== 0)) { + return selectToolbarPosition; + } + + const toolbarElement = boardElements.find( + (el) => el.id === 'toolbar' && el.type === 'toolbar', + ) as ToolbarElement; + + return toolbarElement ? { x: toolbarElement.x || 0, y: toolbarElement.y || 0 } : { x: 0, y: 0 }; + }, [selectToolbarPosition, boardElements]); + + const handleDelete = useMemo( + () => () => { + if (selectedElementId) { + removeElement(selectedElementId); + selectElement(null); + } + }, + [selectedElementId, removeElement, selectElement], + ); + + if (!selectedElementId || selectedTool !== 'select' || isElementTransforming) { return null; } @@ -17,22 +54,14 @@ export const SelectedElementToolbar = () => {
-