Fix control by props, add control test example
This commit is contained in:
parent
8c2c8d8c93
commit
2aeb513342
5 changed files with 193 additions and 60 deletions
|
@ -1,6 +1,7 @@
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import Editor from './components/editor'
|
import Controlled from './controlled'
|
||||||
|
import Basic from './basic'
|
||||||
|
|
||||||
export default function App(): JSX.Element {
|
export default function App(): JSX.Element {
|
||||||
return <Editor />
|
return <Controlled />
|
||||||
}
|
}
|
||||||
|
|
6
packages/dev/src/basic.tsx
Normal file
6
packages/dev/src/basic.tsx
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import * as React from 'react'
|
||||||
|
import Editor from './components/editor'
|
||||||
|
|
||||||
|
export default function BasicUsage(): JSX.Element {
|
||||||
|
return <Editor />
|
||||||
|
}
|
93
packages/dev/src/controlled.tsx
Normal file
93
packages/dev/src/controlled.tsx
Normal 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} />
|
||||||
|
}
|
|
@ -61,30 +61,26 @@ export function TLDraw({ id, document, currentPageId, onMount, onChange }: TLDra
|
||||||
return { tlstate, useSelector: tlstate.useStore }
|
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 (
|
return (
|
||||||
<TLDrawContext.Provider value={context}>
|
<TLDrawContext.Provider value={context}>
|
||||||
<IdProvider>
|
<IdProvider>
|
||||||
<InnerTldraw />
|
<InnerTldraw currentPageId={currentPageId} document={document} />
|
||||||
</IdProvider>
|
</IdProvider>
|
||||||
</TLDrawContext.Provider>
|
</TLDrawContext.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function InnerTldraw() {
|
function InnerTldraw({
|
||||||
useCustomFonts()
|
currentPageId,
|
||||||
|
document,
|
||||||
|
}: {
|
||||||
|
currentPageId?: string
|
||||||
|
document?: TLDrawDocument
|
||||||
|
}) {
|
||||||
const { tlstate, useSelector } = useTLDrawContext()
|
const { tlstate, useSelector } = useTLDrawContext()
|
||||||
|
|
||||||
|
useCustomFonts()
|
||||||
|
|
||||||
useKeyboardShortcuts()
|
useKeyboardShortcuts()
|
||||||
|
|
||||||
const page = useSelector(pageSelector)
|
const page = useSelector(pageSelector)
|
||||||
|
@ -128,6 +124,20 @@ function InnerTldraw() {
|
||||||
return {}
|
return {}
|
||||||
}, [isDarkMode])
|
}, [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 (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<ContextMenu>
|
<ContextMenu>
|
||||||
|
|
|
@ -67,7 +67,7 @@ const defaultDocument: TLDrawDocument = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialData: Data = {
|
const defaultState: Data = {
|
||||||
settings: {
|
settings: {
|
||||||
isPenMode: false,
|
isPenMode: false,
|
||||||
isDarkMode: false,
|
isDarkMode: false,
|
||||||
|
@ -120,11 +120,11 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
selectedGroupId?: string
|
selectedGroupId?: string
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
id = Utils.uniqueId(),
|
id?: string,
|
||||||
onChange?: (tlstate: TLDrawState, data: Data, reason: string) => void,
|
onChange?: (tlstate: TLDrawState, data: Data, reason: string) => void,
|
||||||
onMount?: (tlstate: TLDrawState) => void
|
onMount?: (tlstate: TLDrawState) => void
|
||||||
) {
|
) {
|
||||||
super(initialData, id, 2, (prev, next, prevVersion) => {
|
super(defaultState, id, 2, (prev, next, prevVersion) => {
|
||||||
const state = { ...prev }
|
const state = { ...prev }
|
||||||
if (prevVersion === 1)
|
if (prevVersion === 1)
|
||||||
state.settings = {
|
state.settings = {
|
||||||
|
@ -477,6 +477,69 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
return this
|
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.
|
* Load a new document.
|
||||||
* @param document The document to load
|
* @param document The document to load
|
||||||
|
@ -487,47 +550,7 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
this.clearSelectHistory()
|
this.clearSelectHistory()
|
||||||
this.session = undefined
|
this.session = undefined
|
||||||
this.selectedGroupId = undefined
|
this.selectedGroupId = undefined
|
||||||
|
return this.updateDocument(document, 'loaded_document')
|
||||||
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}`
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue