From 2aeb513342ae1d5ef2bfcd08046dd13846235b03 Mon Sep 17 00:00:00 2001 From: Steve Ruiz Date: Wed, 8 Sep 2021 10:01:45 +0100 Subject: [PATCH] Fix control by props, add control test example --- packages/dev/src/app.tsx | 5 +- packages/dev/src/basic.tsx | 6 + packages/dev/src/controlled.tsx | 93 +++++++++++++++ .../tldraw/src/components/tldraw/tldraw.tsx | 38 +++--- packages/tldraw/src/state/tlstate.ts | 111 +++++++++++------- 5 files changed, 193 insertions(+), 60 deletions(-) create mode 100644 packages/dev/src/basic.tsx create mode 100644 packages/dev/src/controlled.tsx diff --git a/packages/dev/src/app.tsx b/packages/dev/src/app.tsx index 1e6ebd434..5896eb292 100644 --- a/packages/dev/src/app.tsx +++ b/packages/dev/src/app.tsx @@ -1,6 +1,7 @@ import * as React from 'react' -import Editor from './components/editor' +import Controlled from './controlled' +import Basic from './basic' export default function App(): JSX.Element { - return + return } diff --git a/packages/dev/src/basic.tsx b/packages/dev/src/basic.tsx new file mode 100644 index 000000000..3574cae02 --- /dev/null +++ b/packages/dev/src/basic.tsx @@ -0,0 +1,6 @@ +import * as React from 'react' +import Editor from './components/editor' + +export default function BasicUsage(): JSX.Element { + return +} diff --git a/packages/dev/src/controlled.tsx b/packages/dev/src/controlled.tsx new file mode 100644 index 000000000..ae69fc11f --- /dev/null +++ b/packages/dev/src/controlled.tsx @@ -0,0 +1,93 @@ +import * as React from 'react' +import { + TLDraw, + ColorStyle, + DashStyle, + SizeStyle, + TLDrawDocument, + TLDrawShapeType, +} from '@tldraw/tldraw' + +export default function Controlled() { + const [doc, setDoc] = React.useState({ + id: 'doc', + pages: { + page1: { + id: 'page1', + shapes: { + rect1: { + id: 'rect1', + type: TLDrawShapeType.Rectangle, + parentId: 'page1', + name: 'Rectangle', + childIndex: 1, + point: [100, 100], + size: [100, 100], + style: { + dash: DashStyle.Draw, + size: SizeStyle.Medium, + color: ColorStyle.Blue, + }, + }, + rect2: { + id: 'rect2', + parentId: 'page1', + name: 'Rectangle', + childIndex: 2, + type: TLDrawShapeType.Rectangle, + point: [150, 250], + size: [150, 150], + style: { + dash: DashStyle.Draw, + size: SizeStyle.Medium, + color: ColorStyle.Blue, + }, + }, + }, + bindings: {}, + }, + }, + pageStates: { + page1: { + id: 'page1', + selectedIds: ['rect1'], + camera: { + point: [0, 0], + zoom: 1, + }, + }, + }, + }) + + React.useEffect(() => { + const timeout = setTimeout( + () => + setDoc({ + ...doc, + pages: { + ...doc.pages, + page1: { + ...doc.pages.page1, + shapes: { + ...doc.pages.page1.shapes, + rect2: { + ...doc.pages.page1.shapes.rect2, + style: { + ...doc.pages.page1.shapes.rect2.style, + color: ColorStyle.Orange, + }, + }, + }, + }, + }, + }), + 1000 + ) + + return () => { + clearTimeout(timeout) + } + }, []) + + return +} diff --git a/packages/tldraw/src/components/tldraw/tldraw.tsx b/packages/tldraw/src/components/tldraw/tldraw.tsx index c8ba14a29..9b28157da 100644 --- a/packages/tldraw/src/components/tldraw/tldraw.tsx +++ b/packages/tldraw/src/components/tldraw/tldraw.tsx @@ -61,30 +61,26 @@ export function TLDraw({ id, document, currentPageId, onMount, onChange }: TLDra return { tlstate, useSelector: tlstate.useStore } }) - React.useEffect(() => { - if (!document) return - tlstate.loadDocument(document) - }, [document, tlstate]) - - React.useEffect(() => { - if (!currentPageId) return - tlstate.changePage(currentPageId) - }, [currentPageId, tlstate]) - return ( - + ) } -function InnerTldraw() { - useCustomFonts() - +function InnerTldraw({ + currentPageId, + document, +}: { + currentPageId?: string + document?: TLDrawDocument +}) { const { tlstate, useSelector } = useTLDrawContext() + useCustomFonts() + useKeyboardShortcuts() const page = useSelector(pageSelector) @@ -128,6 +124,20 @@ function InnerTldraw() { return {} }, [isDarkMode]) + React.useEffect(() => { + if (!document) return + if (document.id === tlstate.document.id) { + tlstate.updateDocument(document) + } else { + tlstate.loadDocument(document) + } + }, [document, tlstate]) + + React.useEffect(() => { + if (!currentPageId) return + tlstate.changePage(currentPageId) + }, [currentPageId, tlstate]) + return ( diff --git a/packages/tldraw/src/state/tlstate.ts b/packages/tldraw/src/state/tlstate.ts index 7cb8c1596..34107be69 100644 --- a/packages/tldraw/src/state/tlstate.ts +++ b/packages/tldraw/src/state/tlstate.ts @@ -67,7 +67,7 @@ const defaultDocument: TLDrawDocument = { }, } -const initialData: Data = { +const defaultState: Data = { settings: { isPenMode: false, isDarkMode: false, @@ -120,11 +120,11 @@ export class TLDrawState extends StateManager { selectedGroupId?: string constructor( - id = Utils.uniqueId(), + id?: string, onChange?: (tlstate: TLDrawState, data: Data, reason: string) => void, onMount?: (tlstate: TLDrawState) => void ) { - super(initialData, id, 2, (prev, next, prevVersion) => { + super(defaultState, id, 2, (prev, next, prevVersion) => { const state = { ...prev } if (prevVersion === 1) state.settings = { @@ -477,6 +477,69 @@ export class TLDrawState extends StateManager { return this } + /** + * Update the current document. + * @param document + */ + updateDocument = (document: TLDrawDocument, reason = 'updated_document'): this => { + console.log(reason) + + const state = this.state + + const currentPageId = document.pages[this.currentPageId] + ? this.currentPageId + : Object.keys(document.pages)[0] + + this.replaceState( + { + ...this.state, + appState: { + ...this.appState, + currentPageId, + }, + document: { + ...document, + pages: Object.fromEntries( + Object.entries(document.pages) + .sort((a, b) => (a[1].childIndex || 0) - (b[1].childIndex || 0)) + .map(([pageId, page], i) => { + const nextPage = { ...page } + if (!nextPage.name) nextPage.name = `Page ${i + 1}` + return [pageId, nextPage] + }) + ), + pageStates: Object.fromEntries( + Object.entries(document.pageStates).map(([pageId, pageState]) => { + const page = document.pages[pageId] + const nextPageState = { ...pageState } + const keysToCheck = ['bindingId', 'editingId', 'hoveredId', 'pointedId'] as const + + for (const key of keysToCheck) { + if (!page.shapes[key]) { + nextPageState[key] = undefined + } + } + + nextPageState.selectedIds = pageState.selectedIds.filter( + (id) => !!document.pages[pageId].shapes[id] + ) + + return [pageId, nextPageState] + }) + ), + }, + }, + `${reason}:${document.id}` + ) + + console.log( + 'did page change?', + this.state.document.pages['page1'] !== state.document.pages['page1'] + ) + + return this + } + /** * Load a new document. * @param document The document to load @@ -487,47 +550,7 @@ export class TLDrawState extends StateManager { this.clearSelectHistory() this.session = undefined this.selectedGroupId = undefined - - return this.replaceState( - { - ...this.state, - appState: { - ...this.appState, - currentPageId: Object.keys(document.pages)[0], - }, - document: { - ...document, - pages: Object.fromEntries( - Object.entries(document.pages) - .sort((a, b) => (a[1].childIndex || 0) - (b[1].childIndex || 0)) - .map(([id, page], i) => { - return [ - id, - { - ...page, - name: page.name ? page.name : `Page ${i++}`, - }, - ] - }) - ), - pageStates: Object.fromEntries( - Object.entries(document.pageStates).map(([id, pageState]) => { - return [ - id, - { - ...pageState, - bindingId: undefined, - editingId: undefined, - hoveredId: undefined, - pointedId: undefined, - }, - ] - }) - ), - }, - }, - `loaded_document:${document.id}` - ) + return this.updateDocument(document, 'loaded_document') } /**