Fix control by props, add control test example

This commit is contained in:
Steve Ruiz 2021-09-08 10:01:45 +01:00
parent 8c2c8d8c93
commit 2aeb513342
5 changed files with 193 additions and 60 deletions

View file

@ -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 <Editor />
return <Controlled />
}

View file

@ -0,0 +1,6 @@
import * as React from 'react'
import Editor from './components/editor'
export default function BasicUsage(): JSX.Element {
return <Editor />
}

View file

@ -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<TLDrawDocument>({
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 <TLDraw document={doc} />
}

View file

@ -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 (
<TLDrawContext.Provider value={context}>
<IdProvider>
<InnerTldraw />
<InnerTldraw currentPageId={currentPageId} document={document} />
</IdProvider>
</TLDrawContext.Provider>
)
}
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 (
<Layout>
<ContextMenu>

View file

@ -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<Data> {
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<Data> {
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<Data> {
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')
}
/**