diff --git a/apps/examples/src/examples/rendering-shape-changes/README.md b/apps/examples/src/examples/rendering-shape-changes/README.md new file mode 100644 index 000000000..a6f95b86d --- /dev/null +++ b/apps/examples/src/examples/rendering-shape-changes/README.md @@ -0,0 +1,9 @@ +--- +title: Rendering shapes change +component: ./RenderingShapesChangeExample.tsx +category: basic +--- + +--- + +Do something when the rendering shapes change. diff --git a/apps/examples/src/examples/rendering-shape-changes/RenderingShapesChangeExample.tsx b/apps/examples/src/examples/rendering-shape-changes/RenderingShapesChangeExample.tsx new file mode 100644 index 000000000..25160d531 --- /dev/null +++ b/apps/examples/src/examples/rendering-shape-changes/RenderingShapesChangeExample.tsx @@ -0,0 +1,27 @@ +import { useCallback } from 'react' +import { TLShape, Tldraw } from 'tldraw' +import 'tldraw/tldraw.css' +import { useChangedShapesReactor } from './useRenderingShapesChange' + +const components = { + InFrontOfTheCanvas: () => { + const onShapesChanged = useCallback((info: { culled: TLShape[]; restored: TLShape[] }) => { + // eslint-disable-next-line no-console + for (const shape of info.culled) console.log('culled: ' + shape.id) + // eslint-disable-next-line no-console + for (const shape of info.restored) console.log('restored: ' + shape.id) + }, []) + + useChangedShapesReactor(onShapesChanged) + + return null + }, +} + +export default function RenderingShapesChangeExample() { + return ( +
+ +
+ ) +} diff --git a/apps/examples/src/examples/rendering-shape-changes/useRenderingShapesChange.ts b/apps/examples/src/examples/rendering-shape-changes/useRenderingShapesChange.ts new file mode 100644 index 000000000..ae181b28d --- /dev/null +++ b/apps/examples/src/examples/rendering-shape-changes/useRenderingShapesChange.ts @@ -0,0 +1,42 @@ +import { useEffect, useRef } from 'react' +import { TLShape, react, useEditor } from 'tldraw' + +export function useChangedShapesReactor( + cb: (info: { culled: TLShape[]; restored: TLShape[] }) => void +) { + const editor = useEditor() + const rPrevShapes = useRef(editor.getRenderingShapes()) + + useEffect(() => { + return react('when rendering shapes change', () => { + const after = editor.getRenderingShapes() + const before = rPrevShapes.current + + const culled: TLShape[] = [] + const restored: TLShape[] = [] + + const beforeToVisit = new Set(before) + + for (const afterInfo of after) { + const beforeInfo = before.find((s) => s.id === afterInfo.id) + if (!beforeInfo) { + continue + } else { + if (afterInfo.isCulled && !beforeInfo.isCulled) { + culled.push(afterInfo.shape) + } else if (!afterInfo.isCulled && beforeInfo.isCulled) { + restored.push(afterInfo.shape) + } + beforeToVisit.delete(beforeInfo) + } + } + + rPrevShapes.current = after + + cb({ + culled, + restored, + }) + }) + }, [cb, editor]) +}