Allow for resets when id changes

pull/81/head
Steve Ruiz 2021-09-08 11:16:10 +01:00
rodzic 2aeb513342
commit 4e13b0e07b
15 zmienionych plików z 509 dodań i 244 usunięć

Wyświetl plik

@ -26,7 +26,7 @@ interface CanvasProps<T extends TLShape> {
meta?: Record<string, unknown> meta?: Record<string, unknown>
} }
export const Canvas = React.memo(function Canvas<T extends TLShape>({ export function Canvas<T extends TLShape>({
page, page,
pageState, pageState,
meta, meta,
@ -66,4 +66,4 @@ export const Canvas = React.memo(function Canvas<T extends TLShape>({
</svg> </svg>
</div> </div>
) )
}) }

Wyświetl plik

@ -18,12 +18,7 @@ interface PageProps<T extends TLShape> {
/** /**
* The Page component renders the current page. * The Page component renders the current page.
* */
* ### Example
*
*```ts
* example
*``` */
export function Page<T extends TLShape>({ export function Page<T extends TLShape>({
page, page,
pageState, pageState,

Wyświetl plik

@ -10,10 +10,15 @@ import type {
TLBinding, TLBinding,
} from '../../types' } from '../../types'
import { Canvas } from '../canvas' import { Canvas } from '../canvas'
import { useTLTheme, TLContext } from '../../hooks' import { useTLTheme, TLContext, TLContextType } from '../../hooks'
export interface RendererProps<T extends TLShape, M extends Record<string, unknown>> export interface RendererProps<T extends TLShape, M extends Record<string, unknown>>
extends Partial<TLCallbacks> { extends Partial<TLCallbacks> {
/**
* An id representing the current document. Changing the id will
* update the context and trigger a re-render.
*/
id?: string
/** /**
* An object containing instances of your shape classes. * An object containing instances of your shape classes.
*/ */
@ -52,6 +57,8 @@ export interface RendererProps<T extends TLShape, M extends Record<string, unkno
* An object of custom options that should be passed to rendered shapes. * An object of custom options that should be passed to rendered shapes.
*/ */
meta?: M meta?: M
// Temp
onTest?: () => void
} }
/** /**
@ -63,6 +70,7 @@ export interface RendererProps<T extends TLShape, M extends Record<string, unkno
* @returns * @returns
*/ */
export function Renderer<T extends TLShape, M extends Record<string, unknown>>({ export function Renderer<T extends TLShape, M extends Record<string, unknown>>({
id,
shapeUtils, shapeUtils,
page, page,
pageState, pageState,
@ -82,13 +90,31 @@ export function Renderer<T extends TLShape, M extends Record<string, unknown>>({
rPageState.current = pageState rPageState.current = pageState
}, [pageState]) }, [pageState])
const [context] = React.useState(() => ({ const rId = React.useRef(id)
const [context, setContext] = React.useState<TLContextType>(() => ({
id,
callbacks: rest, callbacks: rest,
shapeUtils, shapeUtils,
rScreenBounds, rScreenBounds,
rPageState, rPageState,
})) }))
React.useEffect(() => {
if (id !== rId.current) {
rest.onTest?.()
setContext({
id,
callbacks: rest,
shapeUtils,
rScreenBounds,
rPageState,
})
rId.current = id
}
}, [id])
return ( return (
<TLContext.Provider value={context}> <TLContext.Provider value={context}>
<Canvas <Canvas

Wyświetl plik

@ -4,55 +4,53 @@ import type { IShapeTreeNode } from '+types'
import { RenderedShape } from './rendered-shape' import { RenderedShape } from './rendered-shape'
import { EditingTextShape } from './editing-text-shape' import { EditingTextShape } from './editing-text-shape'
export const Shape = React.memo( export const Shape = <M extends Record<string, unknown>>({
<M extends Record<string, unknown>>({ shape,
shape, isEditing,
isEditing, isBinding,
isBinding, isHovered,
isHovered, isSelected,
isSelected, isCurrentParent,
isCurrentParent, meta,
meta, }: IShapeTreeNode<M>) => {
}: IShapeTreeNode<M>) => { const { shapeUtils } = useTLContext()
const { shapeUtils } = useTLContext() const events = useShapeEvents(shape.id, isCurrentParent)
const events = useShapeEvents(shape.id, isCurrentParent) const utils = shapeUtils[shape.type]
const utils = shapeUtils[shape.type]
const center = utils.getCenter(shape) const center = utils.getCenter(shape)
const rotation = (shape.rotation || 0) * (180 / Math.PI) const rotation = (shape.rotation || 0) * (180 / Math.PI)
const transform = `rotate(${rotation}, ${center}) translate(${shape.point})` const transform = `rotate(${rotation}, ${center}) translate(${shape.point})`
return ( return (
<g <g
className={isCurrentParent ? 'tl-shape-group tl-current-parent' : 'tl-shape-group'} className={isCurrentParent ? 'tl-shape-group tl-current-parent' : 'tl-shape-group'}
id={shape.id} id={shape.id}
transform={transform} transform={transform}
{...events} {...events}
> >
{isEditing && utils.isEditableText ? ( {isEditing && utils.isEditableText ? (
<EditingTextShape <EditingTextShape
shape={shape} shape={shape}
isBinding={false} isBinding={false}
isCurrentParent={false} isCurrentParent={false}
isEditing={true} isEditing={true}
isHovered={isHovered} isHovered={isHovered}
isSelected={isSelected} isSelected={isSelected}
utils={utils} utils={utils}
meta={meta} meta={meta}
/> />
) : ( ) : (
<RenderedShape <RenderedShape
shape={shape} shape={shape}
utils={utils} utils={utils}
isBinding={isBinding} isBinding={isBinding}
isCurrentParent={isCurrentParent} isCurrentParent={isCurrentParent}
isEditing={isEditing} isEditing={isEditing}
isHovered={isHovered} isHovered={isHovered}
isSelected={isSelected} isSelected={isSelected}
meta={meta} meta={meta}
/> />
)} )}
</g> </g>
) )
} }
)

Wyświetl plik

@ -1,10 +1,10 @@
import * as React from 'react' import * as React from 'react'
import { inputs } from '+inputs' import { inputs } from '+inputs'
import { useTLContext } from './useTLContext'
import { Utils } from '+utils' import { Utils } from '+utils'
import { TLContext } from '+hooks'
export function useShapeEvents(id: string, disable = false) { export function useShapeEvents(id: string, disable = false) {
const { rPageState, rScreenBounds, callbacks } = useTLContext() const { rPageState, rScreenBounds, callbacks } = React.useContext(TLContext)
const onPointerDown = React.useCallback( const onPointerDown = React.useCallback(
(e: React.PointerEvent) => { (e: React.PointerEvent) => {

Wyświetl plik

@ -2,6 +2,7 @@ import * as React from 'react'
import type { TLCallbacks, TLShape, TLBounds, TLPageState, TLShapeUtils } from '+types' import type { TLCallbacks, TLShape, TLBounds, TLPageState, TLShapeUtils } from '+types'
export interface TLContextType { export interface TLContextType {
id?: string
callbacks: Partial<TLCallbacks> callbacks: Partial<TLCallbacks>
shapeUtils: TLShapeUtils<TLShape> shapeUtils: TLShapeUtils<TLShape>
rPageState: React.MutableRefObject<TLPageState> rPageState: React.MutableRefObject<TLPageState>

Wyświetl plik

@ -1,7 +1,8 @@
import * as React from 'react' import * as React from 'react'
import Controlled from './controlled' import Controlled from './controlled'
import Basic from './basic' import Basic from './basic'
import NewId from './newId'
export default function App(): JSX.Element { export default function App(): JSX.Element {
return <Controlled /> return <NewId />
} }

Wyświetl plik

@ -0,0 +1,14 @@
import * as React from 'react'
import { TLDraw } from '@tldraw/tldraw'
export default function NewId() {
const [id, setId] = React.useState('example')
React.useEffect(() => {
const timeout = setTimeout(() => setId('example2'), 2000)
return () => clearTimeout(timeout)
}, [])
return <TLDraw id={id} />
}

Wyświetl plik

@ -359,8 +359,6 @@ function MoveToPageMenu(): JSX.Element | null {
if (sorted.length === 0) return null if (sorted.length === 0) return null
console.log(sorted)
return ( return (
<ContextMenuRoot> <ContextMenuRoot>
<RadixContextMenu.TriggerItem as={RowButton} bp={breakpoints}> <RadixContextMenu.TriggerItem as={RowButton} bp={breakpoints}>

Wyświetl plik

@ -51,29 +51,41 @@ export interface TLDrawProps {
} }
export function TLDraw({ id, document, currentPageId, onMount, onChange }: TLDrawProps) { export function TLDraw({ id, document, currentPageId, onMount, onChange }: TLDrawProps) {
const [sId, setSId] = React.useState(id)
const [tlstate, setTlstate] = React.useState(() => new TLDrawState(id)) const [tlstate, setTlstate] = React.useState(() => new TLDrawState(id))
const [context, setContext] = React.useState(() => ({ tlstate, useSelector: tlstate.useStore }))
React.useEffect(() => { React.useEffect(() => {
setTlstate(new TLDrawState(id, onChange, onMount)) if (id === sId) return
}, [id]) // If a new id is loaded, replace the entire state
const newState = new TLDrawState(id, onChange, onMount)
setTlstate(newState)
setContext({ tlstate: newState, useSelector: newState.useStore })
setSId(id)
}, [sId, id])
const [context] = React.useState(() => { // Use the `key` to ensure that new selector hooks are made when the id changes
return { tlstate, useSelector: tlstate.useStore }
})
return ( return (
<TLDrawContext.Provider value={context}> <TLDrawContext.Provider value={context}>
<IdProvider> <IdProvider>
<InnerTldraw currentPageId={currentPageId} document={document} /> <InnerTldraw
key={sId || 'tldraw'}
id={sId}
currentPageId={currentPageId}
document={document}
/>
</IdProvider> </IdProvider>
</TLDrawContext.Provider> </TLDrawContext.Provider>
) )
} }
function InnerTldraw({ function InnerTldraw({
id,
currentPageId, currentPageId,
document, document,
}: { }: {
id?: string
currentPageId?: string currentPageId?: string
document?: TLDrawDocument document?: TLDrawDocument
}) { }) {
@ -138,10 +150,16 @@ function InnerTldraw({
tlstate.changePage(currentPageId) tlstate.changePage(currentPageId)
}, [currentPageId, tlstate]) }, [currentPageId, tlstate])
React.useEffect(() => {
'Id Changed!'
console.log(id, tlstate.id)
}, [id])
return ( return (
<Layout> <Layout>
<ContextMenu> <ContextMenu>
<Renderer <Renderer
id={id}
page={page} page={page}
pageState={pageState} pageState={pageState}
shapeUtils={tldrawShapeUtils} shapeUtils={tldrawShapeUtils}

Wyświetl plik

@ -7,14 +7,14 @@ const statusSelector = (s: Data) => s.appState.status.current
const activeToolSelector = (s: Data) => s.appState.activeTool const activeToolSelector = (s: Data) => s.appState.activeTool
export function StatusBar(): JSX.Element | null { export function StatusBar(): JSX.Element | null {
const { useSelector } = useTLDrawContext() const { tlstate, useSelector } = useTLDrawContext()
const status = useSelector(statusSelector) const status = useSelector(statusSelector)
const activeTool = useSelector(activeToolSelector) const activeTool = useSelector(activeToolSelector)
return ( return (
<StatusBarContainer size={{ '@sm': 'small' }}> <StatusBarContainer size={{ '@sm': 'small' }}>
<Section> <Section>
{activeTool} | {status} {tlstate.id} | {activeTool} | {status}
</Section> </Section>
</StatusBarContainer> </StatusBarContainer>
) )

Wyświetl plik

@ -32,11 +32,15 @@ export const ToolsPanel = React.memo((): JSX.Element => {
const isDebugMode = useSelector(isDebugModeSelector) const isDebugMode = useSelector(isDebugModeSelector)
console.log(activeTool)
const selectSelectTool = React.useCallback(() => { const selectSelectTool = React.useCallback(() => {
console.log(tlstate.id)
tlstate.selectTool('select') tlstate.selectTool('select')
}, [tlstate]) }, [tlstate])
const selectDrawTool = React.useCallback(() => { const selectDrawTool = React.useCallback(() => {
console.log(tlstate.id)
tlstate.selectTool(TLDrawShapeType.Draw) tlstate.selectTool(TLDrawShapeType.Draw)
}, [tlstate]) }, [tlstate])

Wyświetl plik

@ -29,195 +29,390 @@ export function useKeyboardShortcuts() {
/* ---------------------- Tools --------------------- */ /* ---------------------- Tools --------------------- */
useHotkeys('v,1', () => { useHotkeys(
tlstate.selectTool('select') 'v,1',
}) () => {
tlstate.selectTool('select')
},
undefined,
[tlstate]
)
useHotkeys('d,2', () => { useHotkeys(
tlstate.selectTool(TLDrawShapeType.Draw) 'd,2',
}) () => {
tlstate.selectTool(TLDrawShapeType.Draw)
},
undefined,
[tlstate]
)
useHotkeys('r,3', () => { useHotkeys(
tlstate.selectTool(TLDrawShapeType.Rectangle) 'r,3',
}) () => {
tlstate.selectTool(TLDrawShapeType.Rectangle)
},
undefined,
[tlstate]
)
useHotkeys('e,4', () => { useHotkeys(
tlstate.selectTool(TLDrawShapeType.Ellipse) 'e,4',
}) () => {
tlstate.selectTool(TLDrawShapeType.Ellipse)
},
undefined,
[tlstate]
)
useHotkeys('a,5', () => { useHotkeys(
tlstate.selectTool(TLDrawShapeType.Arrow) 'a,5',
}) () => {
tlstate.selectTool(TLDrawShapeType.Arrow)
},
undefined,
[tlstate]
)
useHotkeys('t,6', () => { useHotkeys(
tlstate.selectTool(TLDrawShapeType.Text) 't,6',
}) () => {
tlstate.selectTool(TLDrawShapeType.Text)
},
undefined,
[tlstate]
)
/* ---------------------- Misc ---------------------- */ /* ---------------------- Misc ---------------------- */
// Save // Save
useHotkeys('ctrl+s,command+s', () => { useHotkeys(
tlstate.saveProject() 'ctrl+s,command+s',
}) () => {
tlstate.saveProject()
},
undefined,
[tlstate]
)
// Undo Redo // Undo Redo
useHotkeys('command+z,ctrl+z', () => { useHotkeys(
tlstate.undo() 'command+z,ctrl+z',
}) () => {
tlstate.undo()
},
undefined,
[tlstate]
)
useHotkeys('ctrl+shift-z,command+shift+z', () => { useHotkeys(
tlstate.redo() 'ctrl+shift-z,command+shift+z',
}) () => {
tlstate.redo()
},
undefined,
[tlstate]
)
// Undo Redo // Undo Redo
useHotkeys('command+u,ctrl+u', () => { useHotkeys(
tlstate.undoSelect() 'command+u,ctrl+u',
}) () => {
tlstate.undoSelect()
},
undefined,
[tlstate]
)
useHotkeys('ctrl+shift-u,command+shift+u', () => { useHotkeys(
tlstate.redoSelect() 'ctrl+shift-u,command+shift+u',
}) () => {
tlstate.redoSelect()
},
undefined,
[tlstate]
)
/* -------------------- Commands -------------------- */ /* -------------------- Commands -------------------- */
// Camera // Camera
useHotkeys('ctrl+=,command+=', (e) => { useHotkeys(
tlstate.zoomIn() 'ctrl+=,command+=',
e.preventDefault() (e) => {
}) tlstate.zoomIn()
e.preventDefault()
},
undefined,
[tlstate]
)
useHotkeys('ctrl+-,command+-', (e) => { useHotkeys(
tlstate.zoomOut() 'ctrl+-,command+-',
e.preventDefault() (e) => {
}) tlstate.zoomOut()
e.preventDefault()
},
undefined,
[tlstate]
)
useHotkeys('shift+1', () => { useHotkeys(
tlstate.zoomToFit() 'shift+1',
}) () => {
tlstate.zoomToFit()
},
undefined,
[tlstate]
)
useHotkeys('shift+2', () => { useHotkeys(
tlstate.zoomToSelection() 'shift+2',
}) () => {
tlstate.zoomToSelection()
},
undefined,
[tlstate]
)
useHotkeys('shift+0', () => { useHotkeys(
tlstate.zoomToActual() 'shift+0',
}) () => {
tlstate.zoomToActual()
},
undefined,
[tlstate]
)
// Duplicate // Duplicate
useHotkeys('ctrl+d,command+d', (e) => { useHotkeys(
tlstate.duplicate() 'ctrl+d,command+d',
e.preventDefault() (e) => {
}) tlstate.duplicate()
e.preventDefault()
},
undefined,
[tlstate]
)
// Flip // Flip
useHotkeys('shift+h', () => { useHotkeys(
tlstate.flipHorizontal() 'shift+h',
}) () => {
tlstate.flipHorizontal()
},
undefined,
[tlstate]
)
useHotkeys('shift+v', () => { useHotkeys(
tlstate.flipVertical() 'shift+v',
}) () => {
tlstate.flipVertical()
},
undefined,
[tlstate]
)
// Cancel // Cancel
useHotkeys('escape', () => { useHotkeys(
tlstate.cancel() 'escape',
}) () => {
tlstate.cancel()
},
undefined,
[tlstate]
)
// Delete // Delete
useHotkeys('backspace', () => { useHotkeys(
tlstate.delete() 'backspace',
}) () => {
tlstate.delete()
},
undefined,
[tlstate]
)
// Select All // Select All
useHotkeys('command+a,ctrl+a', () => { useHotkeys(
tlstate.selectAll() 'command+a,ctrl+a',
}) () => {
tlstate.selectAll()
},
undefined,
[tlstate]
)
// Nudge // Nudge
useHotkeys('up', () => { useHotkeys(
tlstate.nudge([0, -1], false) 'up',
}) () => {
tlstate.nudge([0, -1], false)
},
undefined,
[tlstate]
)
useHotkeys('right', () => { useHotkeys(
tlstate.nudge([1, 0], false) 'right',
}) () => {
tlstate.nudge([1, 0], false)
},
undefined,
[tlstate]
)
useHotkeys('down', () => { useHotkeys(
tlstate.nudge([0, 1], false) 'down',
}) () => {
tlstate.nudge([0, 1], false)
},
undefined,
[tlstate]
)
useHotkeys('left', () => { useHotkeys(
tlstate.nudge([-1, 0], false) 'left',
}) () => {
tlstate.nudge([-1, 0], false)
},
undefined,
[tlstate]
)
useHotkeys('shift+up', () => { useHotkeys(
tlstate.nudge([0, -1], true) 'shift+up',
}) () => {
tlstate.nudge([0, -1], true)
},
undefined,
[tlstate]
)
useHotkeys('shift+right', () => { useHotkeys(
tlstate.nudge([1, 0], true) 'shift+right',
}) () => {
tlstate.nudge([1, 0], true)
},
undefined,
[tlstate]
)
useHotkeys('shift+down', () => { useHotkeys(
tlstate.nudge([0, 1], true) 'shift+down',
}) () => {
tlstate.nudge([0, 1], true)
},
undefined,
[tlstate]
)
useHotkeys('shift+left', () => { useHotkeys(
tlstate.nudge([-1, 0], true) 'shift+left',
}) () => {
tlstate.nudge([-1, 0], true)
},
undefined,
[tlstate]
)
// Copy & Paste // Copy & Paste
useHotkeys('command+c,ctrl+c', () => { useHotkeys(
tlstate.copy() 'command+c,ctrl+c',
}) () => {
tlstate.copy()
},
undefined,
[tlstate]
)
useHotkeys('command+v,ctrl+v', () => { useHotkeys(
tlstate.paste() 'command+v,ctrl+v',
}) () => {
tlstate.paste()
},
undefined,
[tlstate]
)
// Group & Ungroup // Group & Ungroup
useHotkeys('command+g,ctrl+g', (e) => { useHotkeys(
tlstate.group() 'command+g,ctrl+g',
e.preventDefault() (e) => {
}) tlstate.group()
e.preventDefault()
},
undefined,
[tlstate]
)
useHotkeys('command+shift+g,ctrl+shift+g', (e) => { useHotkeys(
tlstate.ungroup() 'command+shift+g,ctrl+shift+g',
e.preventDefault() (e) => {
}) tlstate.ungroup()
e.preventDefault()
},
undefined,
[tlstate]
)
// Move // Move
useHotkeys('[', () => { useHotkeys(
tlstate.moveBackward() '[',
}) () => {
tlstate.moveBackward()
},
undefined,
[tlstate]
)
useHotkeys(']', () => { useHotkeys(
tlstate.moveForward() ']',
}) () => {
tlstate.moveForward()
},
undefined,
[tlstate]
)
useHotkeys('shift+[', () => { useHotkeys(
tlstate.moveToBack() 'shift+[',
}) () => {
tlstate.moveToBack()
},
undefined,
[tlstate]
)
useHotkeys('shift+]', () => { useHotkeys(
tlstate.moveToFront() 'shift+]',
}) () => {
tlstate.moveToFront()
},
undefined,
[tlstate]
)
useHotkeys('command+shift+backspace', (e) => { useHotkeys(
tlstate.resetDocument() 'command+shift+backspace',
e.preventDefault() (e) => {
}) tlstate.resetDocument()
e.preventDefault()
},
undefined,
[tlstate]
)
} }

Wyświetl plik

@ -445,4 +445,27 @@ describe('TLDrawState', () => {
expect(tlstate2.shapes.length).toBe(1) expect(tlstate2.shapes.length).toBe(1)
}) })
describe('when the document prop changes', () => {
it.todo('updates the document if the new id is the same as the old one')
it.todo('replaces the document if the ids are different')
})
/*
We want to be able to use the `document` property to update the
document without blowing out the current state. For example, we
may want to patch in changes that occurred from another user.
When the `document` prop changes in the TLDraw component, we want
to update the document in a way that preserves the identity of as
much as possible, while still protecting against invalid states.
If this isn't possible, then we should guide the developer to
instead use a helper like `patchDocument` to update the document.
If the `id` property of the new document is the same as the
previous document, then we call `updateDocument`. Otherwise, we
call `replaceDocument`, which does a harder reset of the state's
internal state.
*/
}) })

Wyświetl plik

@ -97,6 +97,10 @@ const defaultState: Data = {
} }
export class TLDrawState extends StateManager<Data> { export class TLDrawState extends StateManager<Data> {
get id() {
return this._idbId
}
private _onChange?: (tlstate: TLDrawState, data: Data, reason: string) => void private _onChange?: (tlstate: TLDrawState, data: Data, reason: string) => void
private _onMount?: (tlstate: TLDrawState) => void private _onMount?: (tlstate: TLDrawState) => void
@ -482,62 +486,50 @@ export class TLDrawState extends StateManager<Data> {
* @param document * @param document
*/ */
updateDocument = (document: TLDrawDocument, reason = 'updated_document'): this => { updateDocument = (document: TLDrawDocument, reason = 'updated_document'): this => {
console.log(reason) const prevState = this.state
const state = this.state const nextState = { ...prevState, document: { ...prevState.document } }
const currentPageId = document.pages[this.currentPageId] if (!document.pages[this.currentPageId]) {
? this.currentPageId nextState.appState = {
: Object.keys(document.pages)[0] ...prevState.appState,
currentPageId: Object.keys(document.pages)[0],
}
}
this.replaceState( let i = 1
{
...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) { for (const nextPage of Object.values(document.pages)) {
if (!page.shapes[key]) { if (nextPage !== prevState.document.pages[nextPage.id]) {
nextPageState[key] = undefined nextState.document.pages[nextPage.id] = nextPage
}
}
nextPageState.selectedIds = pageState.selectedIds.filter( if (!nextPage.name) {
(id) => !!document.pages[pageId].shapes[id] nextState.document.pages[nextPage.id].name = `Page ${i + 1}`
) i++
}
}
}
return [pageId, nextPageState] for (const nextPageState of Object.values(document.pageStates)) {
}) if (nextPageState !== prevState.document.pageStates[nextPageState.id]) {
), nextState.document.pageStates[nextPageState.id] = nextPageState
},
},
`${reason}:${document.id}`
)
console.log( const nextPage = document.pages[nextPageState.id]
'did page change?', const keysToCheck = ['bindingId', 'editingId', 'hoveredId', 'pointedId'] as const
this.state.document.pages['page1'] !== state.document.pages['page1']
)
return this for (const key of keysToCheck) {
if (!nextPage.shapes[key]) {
nextPageState[key] = undefined
}
}
nextPageState.selectedIds = nextPageState.selectedIds.filter(
(id) => !!document.pages[nextPage.id].shapes[id]
)
}
}
return this.replaceState(nextState, `${reason}:${document.id}`)
} }
/** /**