diff --git a/README.md b/README.md index 7ffec82fd..965593ab2 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,21 @@ function App() { } ``` -You can control the `TLDraw` component through props: +### Persisting the State + +You can use the `id` to persist the state in a user's browser storage. + +```tsx +import { TLDraw } from '@tldraw/tldraw' + +function App() { + return +} +``` + +### Controlling the Component through Props + +You can control the `TLDraw` component through its props. ```tsx import { TLDraw, TLDrawDocument } from '@tldraw/tldraw' @@ -44,42 +58,65 @@ function App() { } ``` -Or imperatively through the `TLDrawState` instance: +### Controlling the Component through the TLDrawState API + +You can also control the `TLDraw` component imperatively through the `TLDrawState` API. ```tsx import { TLDraw, TLDrawState } from '@tldraw/tldraw' function App() { - const handleMount = React.useCallback((tlstate: TLDrawState) => { - const myDocument: TLDrawDocument = {} - - tlstate.loadDocument(myDocument).selectAll() + const handleMount = React.useCallback((state: TLDrawState) => { + state.selectAll() }, []) return } ``` +Internally, the `TLDraw` component's user interface uses this API to make changes to the component's state. See the `TLDrawState` section for more on this API. + +### Responding to Changes + +You can respond to changes and user actions using the `onChange` callback. + +```tsx +import { TLDraw, TLDrawState } from '@tldraw/tldraw' + +function App() { + const handleChange = React.useCallback((state: TLDrawState, reason: string) => {}, []) + + return +} +``` + +Internally, the `TLDraw` component's user interface uses this API to make changes to the component's state. See the `TLDrawState` section for more on this API. + ## Documentation ### `TLDraw` The `TLDraw` React component is the [tldraw](https://tldraw.com) editor exported as a standalone component. You can control the editor through props, or through the `TLDrawState`'s imperative API. **All props are optional.** -| Prop | Type | Description | -| --------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `id` | `string` | An id under which to persist the component's state. | -| `document` | `TLDrawDocument` | An initial [`TLDrawDocument`](#tldrawdocument) object. | -| `currentPageId` | `string` | A current page id, referencing the `TLDrawDocument` object provided via the `document` prop. | -| `onMount` | `Function` | A callback function that will be called when the editor first mounts, receiving the current `TLDrawState`. | -| `onChange` | `Function` | A callback function that will be called whenever the `TLDrawState` updates. The update will include the current `TLDrawState` and the reason for the change. | -| `onUserChange` | `Function` | A callback function that will be fired when the user's "presence" information changes. | -| `autofocus` | `boolean` | Whether the editor should immediately receive focus. Defaults to true. | -| `showMenu` | `boolean` | Whether to show the menu. | -| `showPages` | `boolean` | Whether to show the pages menu. | -| `showStyles` | `boolean` | Whether to show the styles menu. | -| `showTools` | `boolean` | Whether to show the tools. | -| `showUI` | `boolean` | Whether to show any UI other than the canvas. | +| Prop | Type | Description | +| --------------- | ---------------- | -------------------------------------------------------------------------------------------- | +| `id` | `string` | An id under which to persist the component's state. | +| `document` | `TLDrawDocument` | An initial [`TLDrawDocument`](#tldrawdocument) object. | +| `currentPageId` | `string` | A current page id, referencing the `TLDrawDocument` object provided via the `document` prop. | +| `onMount` | `Function` | Called when the editor first mounts, receiving the current `TLDrawState`. | +| `onPatch` | `Function` | Called when the state is updated via a patch. | +| `onCommand` | `Function` | Called when the state is updated via a command. | +| `onPersist` | `Function` | Called when the state is persisted after an action. | +| `onChange` | `Function` | Called when the `TLDrawState` updates for any reason. | +| `onUndo` | `Function` | Called when the `TLDrawState` updates after an undo. | +| `onRedo` | `Function` | Called when the `TLDrawState` updates after a redo. | +| `onUserChange` | `Function` | Called when the user's "presence" information changes. | +| `autofocus` | `boolean` | Whether the editor should immediately receive focus. Defaults to true. | +| `showMenu` | `boolean` | Whether to show the menu. | +| `showPages` | `boolean` | Whether to show the pages menu. | +| `showStyles` | `boolean` | Whether to show the styles menu. | +| `showTools` | `boolean` | Whether to show the tools. | +| `showUI` | `boolean` | Whether to show any UI other than the canvas. | ### `TLDrawDocument` @@ -215,6 +252,72 @@ A binding is a connection **from** one shape and **to** another shape. At the mo | `distance` | `number` | The distance from the bound point. | | `point` | `number[]` | A normalized point representing the bound point. | +### `TLDrawState` API + +You can change the `TLDraw` component's state through an imperative API called `TLDrawState`. To access this API, use the `onMount` callback, or any of the component's callback props, like `onPersist`. + +```tsx +import { TLDraw, TLDrawState } from '@tldraw/tldraw' + +function App() { + const handleMount = React.useCallback((state: TLDrawState) => { + state.selectAll() + }, []) + + return +} +``` + +The `TLDrawState` API is too large to document here. To view documentation for the API, build the documentation by running `yarn docs` from the root folder and open the file at `/packages/tldraw/docs/classes/TLDrawState.html` in your browser. + +Here are some useful methods: + +| Method | Description | +| ----------------- | ----------- | +| `loadDocument` | | +| `select` | | +| `selectAll` | | +| `selectNone` | | +| `delete` | | +| `deleteAll` | | +| `deletePage` | | +| `changePage` | | +| `cut` | | +| `copy` | | +| `paste` | | +| `copyJson` | | +| `copySvg` | | +| `undo` | | +| `redo` | | +| `zoomIn` | | +| `zoomOut` | | +| `zoomToContent` | | +| `zoomToSelection` | | +| `zoomToFit` | | +| `zoomTo` | | +| `resetZoom` | | +| `setCamera` | | +| `resetCamera` | | +| `align` | | +| `distribute` | | +| `stretch` | | +| `nudge` | | +| `duplicate` | | +| `flipHorizontal` | | +| `flipVertical` | | +| `rotate` | | +| `style` | | +| `group` | | +| `ungroup` | | +| `createShapes` | | +| `updateShapes` | | +| `updateDocument` | | +| `updateUsers` | | +| `removeUser` | | +| `setSetting` | | +| `selectTool` | | +| `cancel` | | + ## Local Development - Run `yarn` to install dependencies. diff --git a/electron/src/renderer/app.tsx b/electron/src/renderer/app.tsx index 550614773..e75c79863 100644 --- a/electron/src/renderer/app.tsx +++ b/electron/src/renderer/app.tsx @@ -6,67 +6,67 @@ import type { Message, TLApi } from 'src/types' export default function App(): JSX.Element { const rTLDrawState = React.useRef() - // When the editor mounts, save the tlstate instance in a ref. + // When the editor mounts, save the state instance in a ref. const handleMount = React.useCallback((tldr: TLDrawState) => { rTLDrawState.current = tldr }, []) React.useEffect(() => { function handleEvent(message: Message) { - const tlstate = rTLDrawState.current - if (!tlstate) return + const state = rTLDrawState.current + if (!state) return switch (message.type) { case 'resetZoom': { - tlstate.resetZoom() + state.resetZoom() break } case 'zoomIn': { - tlstate.zoomIn() + state.zoomIn() break } case 'zoomOut': { - tlstate.zoomOut() + state.zoomOut() break } case 'zoomToFit': { - tlstate.zoomToFit() + state.zoomToFit() break } case 'zoomToSelection': { - tlstate.zoomToSelection() + state.zoomToSelection() break } case 'undo': { - tlstate.undo() + state.undo() break } case 'redo': { - tlstate.redo() + state.redo() break } case 'cut': { - tlstate.cut() + state.cut() break } case 'copy': { - tlstate.copy() + state.copy() break } case 'paste': { - tlstate.paste() + state.paste() break } case 'delete': { - tlstate.delete() + state.delete() break } case 'selectAll': { - tlstate.selectAll() + state.selectAll() break } case 'selectNone': { - tlstate.selectNone() + state.selectNone() break } } diff --git a/example/.env.local b/example/.env similarity index 100% rename from example/.env.local rename to example/.env diff --git a/example/esbuild.config.mjs b/example/esbuild.config.mjs deleted file mode 100644 index 4eab50ff4..000000000 --- a/example/esbuild.config.mjs +++ /dev/null @@ -1,41 +0,0 @@ -/* eslint-disable no-undef */ -import fs from 'fs' -import esbuild from 'esbuild' -import serve, { error, log } from 'create-serve' - -if (!fs.existsSync('./dist')) { - fs.mkdirSync('./dist') -} - -fs.copyFile('./src/index.html', './dist/index.html', (err) => { - if (err) throw err -}) - -esbuild - .build({ - entryPoints: ['src/index.tsx'], - outfile: 'dist/index.js', - minify: false, - bundle: true, - sourcemap: true, - incremental: true, - format: 'esm', - target: 'esnext', - define: { - 'process.env.LIVEBLOCKS_PUBLIC_API_KEY': process.env.LIVEBLOCKS_PUBLIC_API_KEY, - 'process.env.NODE_ENV': '"development"', - }, - watch: { - onRebuild(err) { - serve.update() - err ? error('❌ Failed') : log('✅ Updated') - }, - }, - }) - .catch(() => process.exit(1)) - -serve.start({ - port: 5420, - root: './dist', - live: true, -}) diff --git a/example/package.json b/example/package.json index 30d03adcc..9645e25f0 100644 --- a/example/package.json +++ b/example/package.json @@ -24,7 +24,9 @@ "@types/react-router-dom": "^5.1.8", "concurrently": "6.0.1", "create-serve": "1.0.1", + "dotenv": "^10.0.0", "esbuild": "^0.13.8", + "esbuild-envfile-plugin": "^1.0.1", "esbuild-serve": "^1.0.1", "react": ">=16.8", "react-dom": "^16.8 || ^17.0", diff --git a/example/scripts/dev.mjs b/example/scripts/dev.mjs index 9d78eba0c..b9e79090d 100644 --- a/example/scripts/dev.mjs +++ b/example/scripts/dev.mjs @@ -1,6 +1,9 @@ /* eslint-disable no-undef */ import fs from 'fs' import esbuildServe from 'esbuild-serve' +import dotenv from 'dotenv' + +dotenv.config() async function main() { if (!fs.existsSync('./dist')) { @@ -11,6 +14,8 @@ async function main() { if (err) throw err }) + console.log(process.env.LIVEBLOCKS_PUBLIC_API_KEY) + try { await esbuildServe( { @@ -20,9 +25,11 @@ async function main() { bundle: true, sourcemap: true, incremental: true, - target: ['chrome58', 'firefox57', 'safari11', 'edge18'], + format: 'cjs', + target: 'es6', define: { 'process.env.NODE_ENV': '"development"', + 'process.env.LIVEBLOCKS_PUBLIC_API_KEY': `"${process.env.LIVEBLOCKS_PUBLIC_API_KEY}"`, }, watch: { onRebuild(err) { diff --git a/example/src/components/editor.tsx b/example/src/components/editor.tsx index 0286fa6c4..f24539ed2 100644 --- a/example/src/components/editor.tsx +++ b/example/src/components/editor.tsx @@ -11,7 +11,7 @@ export default function Editor(props: TLDrawProps): JSX.Element { props.onMount?.(state) // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - window.tlstate = state + window.state = state }, []) const onSignIn = React.useCallback((state: TLDrawState) => { diff --git a/example/src/controlled.tsx b/example/src/controlled.tsx index 98e1fa483..c31a4f7bf 100644 --- a/example/src/controlled.tsx +++ b/example/src/controlled.tsx @@ -101,8 +101,8 @@ export default function Controlled() { } }, []) - const handleChange = React.useCallback((tlstate) => { - rDocument.current = tlstate.document + const handleChange = React.useCallback((state) => { + rDocument.current = state.document }, []) return diff --git a/example/src/imperative.tsx b/example/src/imperative.tsx index 51477c6ff..97fd18cc3 100644 --- a/example/src/imperative.tsx +++ b/example/src/imperative.tsx @@ -4,6 +4,7 @@ import { ColorStyle, TLDraw, TLDrawShapeType, TLDrawState } from '@tldraw/tldraw export default function Imperative(): JSX.Element { const rTLDrawState = React.useRef() + const handleMount = React.useCallback((state: TLDrawState) => { rTLDrawState.current = state @@ -29,17 +30,24 @@ export default function Imperative(): JSX.Element { React.useEffect(() => { let i = 0 const interval = setInterval(() => { - const tlstate = rTLDrawState.current! - const rect1 = tlstate.getShape('rect1') + const state = rTLDrawState.current! + const rect1 = state.getShape('rect1') if (!rect1) { - // clearInterval(interval) + state.createShapes({ + id: 'rect1', + type: TLDrawShapeType.Rectangle, + name: 'Rectangle', + childIndex: 1, + point: [0, 0], + size: [100, 100], + }) return } const color = i % 2 ? ColorStyle.Red : ColorStyle.Blue - tlstate.patchShapes({ + state.patchShapes({ id: 'rect1', style: { ...rect1.style, diff --git a/example/src/multiplayer/multiplayer.tsx b/example/src/multiplayer/multiplayer.tsx index a148e2a2d..443faa88d 100644 --- a/example/src/multiplayer/multiplayer.tsx +++ b/example/src/multiplayer/multiplayer.tsx @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import * as React from 'react' -import { TLDraw, TLDrawState, TLDrawDocument, TLDrawUser, Data } from '@tldraw/tldraw' +import { TLDraw, TLDrawState, TLDrawDocument, TLDrawUser } from '@tldraw/tldraw' import { createClient, Presence } from '@liveblocks/client' import { LiveblocksProvider, RoomProvider, useErrorListener, useObject } from '@liveblocks/react' import { Utils } from '@tldraw/core' @@ -9,19 +9,17 @@ interface TLDrawUserPresence extends Presence { user: TLDrawUser } -const publicAPIKey = 'pk_live_1LJGGaqBSNLjLT-4Jalkl-U9' - const client = createClient({ - publicApiKey: publicAPIKey, + publicApiKey: process.env.LIVEBLOCKS_PUBLIC_API_KEY || '', throttle: 80, }) -const ROOM_ID = 'mp-test-2' +const roomId = 'mp-test-2' export function Multiplayer() { return ( - + @@ -33,7 +31,7 @@ function TLDrawWrapper() { const [error, setError] = React.useState() - const [tlstate, setTlstate] = React.useState() + const [state, setstate] = React.useState() useErrorListener((err) => setError(err)) @@ -41,55 +39,38 @@ function TLDrawWrapper() { uuid: docId, document: { ...TLDrawState.defaultDocument, - id: 'test-room', + id: roomId, }, }) - // Put the tlstate into the window, for debugging. - const handleMount = React.useCallback((tlstate: TLDrawState) => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - window.tlstate = tlstate - - tlstate.loadRoom(ROOM_ID) - - setTlstate(tlstate) - }, []) - - const handleChange = React.useCallback( - (_tlstate: TLDrawState, state: Data, reason: string) => { - // If the client updates its document, update the room's document - if (reason.startsWith('command') || reason.startsWith('undo') || reason.startsWith('redo')) { - doc?.update({ uuid: docId, document: state.document }) - } - - // When the client updates its presence, update the room - // if (state.room && (reason === 'patch:room:self:update' || reason === 'patch:selected')) { - // const room = client.getRoom(ROOM_ID) - // if (!room) return - // const { userId, users } = state.room - // room.updatePresence({ id: userId, user: users[userId] }) - // } + // Put the state into the window, for debugging. + const handleMount = React.useCallback( + (state: TLDrawState) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + window.state = state + state.loadRoom(roomId) + setstate(state) }, - [docId, doc] + [roomId] ) React.useEffect(() => { - const room = client.getRoom(ROOM_ID) + const room = client.getRoom(roomId) if (!room) return if (!doc) return - if (!tlstate) return - if (!tlstate.state.room) return + if (!state) return + if (!state.state.room) return // Update the user's presence with the user from state - const { users, userId } = tlstate.state.room + const { users, userId } = state.state.room room.updatePresence({ id: userId, user: users[userId] }) // Subscribe to presence changes; when others change, update the state room.subscribe('others', (others) => { - tlstate.updateUsers( + state.updateUsers( others .toArray() .filter((other) => other.presence) @@ -100,30 +81,26 @@ function TLDrawWrapper() { room.subscribe('event', (event) => { if (event.event?.name === 'exit') { - tlstate.removeUser(event.event.userId) + state.removeUser(event.event.userId) } }) function handleDocumentUpdates() { if (!doc) return - if (!tlstate) return - if (!tlstate.state.room) return + if (!state) return + if (!state.state.room) return const docObject = doc.toObject() // Only merge the change if it caused by someone else if (docObject.uuid !== docId) { - tlstate.mergeDocument(docObject.document) + state.mergeDocument(docObject.document) } else { - tlstate.updateUsers( - Object.values(tlstate.state.room.users).map((user) => { - // const activeShapes = user.activeShapes - // .map((shape) => docObject.document.pages[tlstate.currentPageId].shapes[shape.id]) - // .filter(Boolean) + state.updateUsers( + Object.values(state.state.room.users).map((user) => { return { ...user, - // activeShapes: activeShapes, - selectedIds: user.selectedIds, // activeShapes.map((shape) => shape.id), + selectedIds: user.selectedIds, } }) ) @@ -131,8 +108,8 @@ function TLDrawWrapper() { } function handleExit() { - if (!(tlstate && tlstate.state.room)) return - room?.broadcastEvent({ name: 'exit', userId: tlstate.state.room.userId }) + if (!(state && state.state.room)) return + room?.broadcastEvent({ name: 'exit', userId: state.state.room.userId }) } window.addEventListener('beforeunload', handleExit) @@ -141,18 +118,29 @@ function TLDrawWrapper() { doc.subscribe(handleDocumentUpdates) // Load the shared document - tlstate.loadDocument(doc.toObject().document) + const newDocument = doc.toObject().document + + if (newDocument) { + state.loadDocument(newDocument) + } return () => { window.removeEventListener('beforeunload', handleExit) doc.unsubscribe(handleDocumentUpdates) } - }, [doc, docId, tlstate]) + }, [doc, docId, state]) + + const handlePersist = React.useCallback( + (state: TLDrawState) => { + doc?.update({ uuid: docId, document: state.document }) + }, + [docId, doc] + ) const handleUserChange = React.useCallback( - (tlstate: TLDrawState, user: TLDrawUser) => { - const room = client.getRoom(ROOM_ID) - room?.updatePresence({ id: tlstate.state.room?.userId, user }) + (state: TLDrawState, user: TLDrawUser) => { + const room = client.getRoom(roomId) + room?.updatePresence({ id: state.state.room?.userId, user }) }, [client] ) @@ -165,7 +153,7 @@ function TLDrawWrapper() {
diff --git a/example/tsconfig.tsbuildinfo b/example/tsconfig.tsbuildinfo index 039bd6d5c..dec7891d1 100644 --- a/example/tsconfig.tsbuildinfo +++ b/example/tsconfig.tsbuildinfo @@ -396,7 +396,7 @@ "signature": "1444afb2d3c50b5a15354934187d75bc9a7ca2d10bf20fe9c79cbcd1f8548549", "affectsGlobalScope": false }, - "../tldraw/dist/types/state/tlstate.d.ts": { + "../tldraw/dist/types/state/state.d.ts": { "version": "3a31dc5b6306ee6cff5e03e4a3ab1eda22f07231bc207f77807915e7c01a96a9", "signature": "3a31dc5b6306ee6cff5e03e4a3ab1eda22f07231bc207f77807915e7c01a96a9", "affectsGlobalScope": false @@ -1245,9 +1245,9 @@ "../tldraw/dist/types/types.d.ts" ], "../tldraw/dist/types/state/index.d.ts": [ - "../tldraw/dist/types/state/tlstate.d.ts" + "../tldraw/dist/types/state/state.d.ts" ], - "../tldraw/dist/types/state/tlstate.d.ts": [ + "../tldraw/dist/types/state/state.d.ts": [ "../../node_modules/rko/dist/types/index.d.ts", "../core/dist/types/index.d.ts", "../tldraw/dist/types/types.d.ts" @@ -1621,9 +1621,9 @@ "../tldraw/dist/types/types.d.ts" ], "../tldraw/dist/types/state/index.d.ts": [ - "../tldraw/dist/types/state/tlstate.d.ts" + "../tldraw/dist/types/state/state.d.ts" ], - "../tldraw/dist/types/state/tlstate.d.ts": [ + "../tldraw/dist/types/state/state.d.ts": [ "../../node_modules/rko/dist/types/index.d.ts", "../core/dist/types/index.d.ts", "../tldraw/dist/types/types.d.ts" @@ -1792,7 +1792,7 @@ "../tldraw/dist/types/shape/shape-styles.d.ts", "../tldraw/dist/types/shape/shape-utils.d.ts", "../tldraw/dist/types/state/index.d.ts", - "../tldraw/dist/types/state/tlstate.d.ts", + "../tldraw/dist/types/state/state.d.ts", "../tldraw/dist/types/types.d.ts" ] }, diff --git a/packages/tldraw/CHANGELOG.md b/packages/tldraw/CHANGELOG.md index b6c00afb0..0480652f0 100644 --- a/packages/tldraw/CHANGELOG.md +++ b/packages/tldraw/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.1.6 +## 0.1.4 - UI bug fixes. diff --git a/packages/tldraw/README.md b/packages/tldraw/README.md index 7ffec82fd..5f23103ea 100644 --- a/packages/tldraw/README.md +++ b/packages/tldraw/README.md @@ -32,7 +32,21 @@ function App() { } ``` -You can control the `TLDraw` component through props: +### Persisting the State + +You can use the `id` to persist the state in a user's browser storage. + +```tsx +import { TLDraw } from '@tldraw/tldraw' + +function App() { + return +} +``` + +### Controlling the Component through Props + +You can control the `TLDraw` component through its props. ```tsx import { TLDraw, TLDrawDocument } from '@tldraw/tldraw' @@ -44,22 +58,40 @@ function App() { } ``` -Or imperatively through the `TLDrawState` instance: +### Controlling the Component through the TLDrawState API + +You can also control the `TLDraw` component imperatively through the `TLDrawState` API. ```tsx import { TLDraw, TLDrawState } from '@tldraw/tldraw' function App() { - const handleMount = React.useCallback((tlstate: TLDrawState) => { - const myDocument: TLDrawDocument = {} - - tlstate.loadDocument(myDocument).selectAll() + const handleMount = React.useCallback((state: TLDrawState) => { + state.selectAll() }, []) return } ``` +Internally, the `TLDraw` component's user interface uses this API to make changes to the component's state. See the `TLDrawState` section for more on this API. + +### Responding to Changes + +You can respond to changes and user actions using the `onChange` callback. + +```tsx +import { TLDraw, TLDrawState } from '@tldraw/tldraw' + +function App() { + const handleChange = React.useCallback((state: TLDrawState, reason: string) => {}, []) + + return +} +``` + +Internally, the `TLDraw` component's user interface uses this API to make changes to the component's state. See the `TLDrawState` section for more on this API. + ## Documentation ### `TLDraw` diff --git a/packages/tldraw/package.json b/packages/tldraw/package.json index 664c33939..4272a9ca2 100644 --- a/packages/tldraw/package.json +++ b/packages/tldraw/package.json @@ -53,7 +53,7 @@ "@tldraw/vec": "^0.1.3", "perfect-freehand": "^1.0.16", "react-hotkeys-hook": "^3.4.0", - "rko": "^0.6.0", + "rko": "^0.6.2", "tslib": "^2.3.1" }, "devDependencies": { diff --git a/packages/tldraw/src/TLDraw.tsx b/packages/tldraw/src/TLDraw.tsx index 201244abb..e937aeea4 100644 --- a/packages/tldraw/src/TLDraw.tsx +++ b/packages/tldraw/src/TLDraw.tsx @@ -2,8 +2,8 @@ import * as React from 'react' import { IdProvider } from '@radix-ui/react-id' import { Renderer } from '@tldraw/core' import { styled, dark } from '~styles' -import { Data, TLDrawDocument, TLDrawStatus, TLDrawUser } from '~types' -import { TLDrawState } from '~state' +import { TLDrawSnapshot, TLDrawDocument, TLDrawStatus, TLDrawUser } from '~types' +import { TLDrawCallbacks, TLDrawState } from '~state' import { TLDrawContext, TLDrawContextType, @@ -19,9 +19,9 @@ import { ContextMenu } from '~components/ContextMenu' import { FocusButton } from '~components/FocusButton/FocusButton' // Selectors -const isInSelectSelector = (s: Data) => s.appState.activeTool === 'select' +const isInSelectSelector = (s: TLDrawSnapshot) => s.appState.activeTool === 'select' -const isHideBoundsShapeSelector = (s: Data) => { +const isHideBoundsShapeSelector = (s: TLDrawSnapshot) => { const { shapes } = s.document.pages[s.appState.currentPageId] const { selectedIds } = s.document.pageStates[s.appState.currentPageId] return ( @@ -30,17 +30,17 @@ const isHideBoundsShapeSelector = (s: Data) => { ) } -const pageSelector = (s: Data) => s.document.pages[s.appState.currentPageId] +const pageSelector = (s: TLDrawSnapshot) => s.document.pages[s.appState.currentPageId] -const snapLinesSelector = (s: Data) => s.appState.snapLines +const snapLinesSelector = (s: TLDrawSnapshot) => s.appState.snapLines -const usersSelector = (s: Data) => s.room?.users +const usersSelector = (s: TLDrawSnapshot) => s.room?.users -const pageStateSelector = (s: Data) => s.document.pageStates[s.appState.currentPageId] +const pageStateSelector = (s: TLDrawSnapshot) => s.document.pageStates[s.appState.currentPageId] -const settingsSelector = (s: Data) => s.settings +const settingsSelector = (s: TLDrawSnapshot) => s.settings -export interface TLDrawProps { +export interface TLDrawProps extends TLDrawCallbacks { /** * (optional) If provided, the component will load / persist state under this key. */ @@ -100,12 +100,6 @@ export interface TLDrawProps { * (optional) A callback to run when the component mounts. */ onMount?: (state: TLDrawState) => void - - /** - * (optional) A callback to run when the component's state changes. - */ - onChange?: TLDrawState['_onChange'] - /** * (optional) A callback to run when the user creates a new project through the menu or through a keyboard shortcut. */ @@ -130,10 +124,35 @@ export interface TLDrawProps { * (optional) A callback to run when the user signs out via the menu. */ onSignOut?: (state: TLDrawState) => void + /** * (optional) A callback to run when the user creates a new project. */ onUserChange?: (state: TLDrawState, user: TLDrawUser) => void + /** + * (optional) A callback to run when the component's state changes. + */ + onChange?: (state: TLDrawState, reason?: string) => void + /** + * (optional) A callback to run when the state is patched. + */ + onPatch?: (state: TLDrawState, reason?: string) => void + /** + * (optional) A callback to run when the state is changed with a command. + */ + onCommand?: (state: TLDrawState, reason?: string) => void + /** + * (optional) A callback to run when the state is persisted. + */ + onPersist?: (state: TLDrawState) => void + /** + * (optional) A callback to run when the user undos. + */ + onUndo?: (state: TLDrawState) => void + /** + * (optional) A callback to run when the user redos. + */ + onRedo?: (state: TLDrawState) => void } export function TLDraw({ @@ -157,57 +176,85 @@ export function TLDraw({ onOpenProject, onSignOut, onSignIn, + onUndo, + onRedo, + onPersist, + onPatch, + onCommand, }: TLDrawProps) { const [sId, setSId] = React.useState(id) - const [tlstate, setTlstate] = React.useState( - () => new TLDrawState(id, onMount, onChange, onUserChange) - ) + const [state, setState] = React.useState(() => new TLDrawState(id)) + const [context, setContext] = React.useState(() => ({ - tlstate, - useSelector: tlstate.useStore, - callbacks: { - onNewProject, - onSaveProject, - onSaveProjectAs, - onOpenProject, - onSignIn, - onSignOut, - }, + state, + useSelector: state.useStore, })) React.useEffect(() => { if (id === sId) return - // If a new id is loaded, replace the entire state + const newState = new TLDrawState(id) + setSId(id) - const newState = new TLDrawState(id, onMount, onChange, onUserChange) - setTlstate(newState) + setContext((ctx) => ({ ...ctx, - tlstate: newState, + state: newState, useSelector: newState.useStore, })) + + setState(newState) }, [sId, id]) - // Update the callbacks when any callback changes React.useEffect(() => { - setContext((ctx) => ({ - ...ctx, - callbacks: { - onNewProject, - onSaveProject, - onSaveProjectAs, - onOpenProject, - onSignIn, - onSignOut, - }, - })) - }, [onNewProject, onSaveProject, onSaveProjectAs, onOpenProject, onSignIn, onSignOut]) + state.readOnly = readOnly + }, [state, readOnly]) React.useEffect(() => { - tlstate.readOnly = readOnly - }, [tlstate, readOnly]) + if (!document) return + + if (document.id === state.document.id) { + state.updateDocument(document) + } else { + state.loadDocument(document) + } + }, [document, state]) + + React.useEffect(() => { + state.callbacks = { + onMount, + onChange, + onUserChange, + onNewProject, + onSaveProject, + onSaveProjectAs, + onOpenProject, + onSignOut, + onSignIn, + onUndo, + onRedo, + onPatch, + onCommand, + onPersist, + } + }, [ + state, + onMount, + onChange, + onUserChange, + onNewProject, + onSaveProject, + onSaveProjectAs, + onOpenProject, + onSignOut, + onSignIn, + onUndo, + onRedo, + onPatch, + onCommand, + onPersist, + ]) // Use the `key` to ensure that new selector hooks are made when the id changes return ( @@ -217,7 +264,6 @@ export function TLDraw({ key={sId || 'tldraw'} id={sId} currentPageId={currentPageId} - document={document} autofocus={autofocus} showPages={showPages} showMenu={showMenu} @@ -243,10 +289,9 @@ interface InnerTLDrawProps { showUI: boolean showTools: boolean readOnly: boolean - document?: TLDrawDocument } -function InnerTldraw({ +const InnerTldraw = React.memo(function InnerTldraw({ id, currentPageId, autofocus, @@ -257,9 +302,8 @@ function InnerTldraw({ showTools, readOnly, showUI, - document, }: InnerTLDrawProps) { - const { tlstate, useSelector } = useTLDrawContext() + const { state, useSelector } = useTLDrawContext() const rWrapper = React.useRef(null) @@ -277,11 +321,11 @@ function InnerTldraw({ const isHideBoundsShape = useSelector(isHideBoundsShapeSelector) - const isInSession = tlstate.session !== undefined + const isInSession = state.session !== undefined // Hide bounds when not using the select tool, or when the only selected shape has handles const hideBounds = - (isInSession && tlstate.session?.constructor.name !== 'BrushSession') || + (isInSession && state.session?.constructor.name !== 'BrushSession') || !isSelecting || isHideBoundsShape || !!pageState.editingId @@ -291,7 +335,7 @@ function InnerTldraw({ // Hide indicators when not using the select tool, or when in session const hideIndicators = - (isInSession && tlstate.appState.status !== TLDrawStatus.Brushing) || !isSelecting + (isInSession && state.appState.status !== TLDrawStatus.Brushing) || !isSelecting // Custom rendering meta, with dark mode for shapes const meta = React.useMemo(() => ({ isDarkMode: settings.isDarkMode }), [settings.isDarkMode]) @@ -312,22 +356,10 @@ function InnerTldraw({ return {} }, [settings.isDarkMode]) - React.useEffect(() => { - if (!document) return - - if (document.id === tlstate.document.id) { - console.log('updating') - tlstate.updateDocument(document) - } else { - console.log('loading') - tlstate.loadDocument(document) - } - }, [document, tlstate]) - React.useEffect(() => { if (!currentPageId) return - tlstate.changePage(currentPageId) - }, [currentPageId, tlstate]) + state.changePage(currentPageId) + }, [currentPageId, state]) return ( @@ -341,7 +373,7 @@ function InnerTldraw({ pageState={pageState} snapLines={snapLines} users={users} - userId={tlstate.state.room?.userId} + userId={state.state.room?.userId} theme={theme} meta={meta} hideBounds={hideBounds} @@ -350,61 +382,61 @@ function InnerTldraw({ hideBindingHandles={!settings.showBindingHandles} hideCloneHandles={!settings.showCloneHandles} hideRotateHandles={!settings.showRotateHandles} - onPinchStart={tlstate.onPinchStart} - onPinchEnd={tlstate.onPinchEnd} - onPinch={tlstate.onPinch} - onPan={tlstate.onPan} - onZoom={tlstate.onZoom} - onPointerDown={tlstate.onPointerDown} - onPointerMove={tlstate.onPointerMove} - onPointerUp={tlstate.onPointerUp} - onPointCanvas={tlstate.onPointCanvas} - onDoubleClickCanvas={tlstate.onDoubleClickCanvas} - onRightPointCanvas={tlstate.onRightPointCanvas} - onDragCanvas={tlstate.onDragCanvas} - onReleaseCanvas={tlstate.onReleaseCanvas} - onPointShape={tlstate.onPointShape} - onDoubleClickShape={tlstate.onDoubleClickShape} - onRightPointShape={tlstate.onRightPointShape} - onDragShape={tlstate.onDragShape} - onHoverShape={tlstate.onHoverShape} - onUnhoverShape={tlstate.onUnhoverShape} - onReleaseShape={tlstate.onReleaseShape} - onPointBounds={tlstate.onPointBounds} - onDoubleClickBounds={tlstate.onDoubleClickBounds} - onRightPointBounds={tlstate.onRightPointBounds} - onDragBounds={tlstate.onDragBounds} - onHoverBounds={tlstate.onHoverBounds} - onUnhoverBounds={tlstate.onUnhoverBounds} - onReleaseBounds={tlstate.onReleaseBounds} - onPointBoundsHandle={tlstate.onPointBoundsHandle} - onDoubleClickBoundsHandle={tlstate.onDoubleClickBoundsHandle} - onRightPointBoundsHandle={tlstate.onRightPointBoundsHandle} - onDragBoundsHandle={tlstate.onDragBoundsHandle} - onHoverBoundsHandle={tlstate.onHoverBoundsHandle} - onUnhoverBoundsHandle={tlstate.onUnhoverBoundsHandle} - onReleaseBoundsHandle={tlstate.onReleaseBoundsHandle} - onPointHandle={tlstate.onPointHandle} - onDoubleClickHandle={tlstate.onDoubleClickHandle} - onRightPointHandle={tlstate.onRightPointHandle} - onDragHandle={tlstate.onDragHandle} - onHoverHandle={tlstate.onHoverHandle} - onUnhoverHandle={tlstate.onUnhoverHandle} - onReleaseHandle={tlstate.onReleaseHandle} - onError={tlstate.onError} - onRenderCountChange={tlstate.onRenderCountChange} - onShapeChange={tlstate.onShapeChange} - onShapeBlur={tlstate.onShapeBlur} - onShapeClone={tlstate.onShapeClone} - onBoundsChange={tlstate.updateBounds} - onKeyDown={tlstate.onKeyDown} - onKeyUp={tlstate.onKeyUp} + onPinchStart={state.onPinchStart} + onPinchEnd={state.onPinchEnd} + onPinch={state.onPinch} + onPan={state.onPan} + onZoom={state.onZoom} + onPointerDown={state.onPointerDown} + onPointerMove={state.onPointerMove} + onPointerUp={state.onPointerUp} + onPointCanvas={state.onPointCanvas} + onDoubleClickCanvas={state.onDoubleClickCanvas} + onRightPointCanvas={state.onRightPointCanvas} + onDragCanvas={state.onDragCanvas} + onReleaseCanvas={state.onReleaseCanvas} + onPointShape={state.onPointShape} + onDoubleClickShape={state.onDoubleClickShape} + onRightPointShape={state.onRightPointShape} + onDragShape={state.onDragShape} + onHoverShape={state.onHoverShape} + onUnhoverShape={state.onUnhoverShape} + onReleaseShape={state.onReleaseShape} + onPointBounds={state.onPointBounds} + onDoubleClickBounds={state.onDoubleClickBounds} + onRightPointBounds={state.onRightPointBounds} + onDragBounds={state.onDragBounds} + onHoverBounds={state.onHoverBounds} + onUnhoverBounds={state.onUnhoverBounds} + onReleaseBounds={state.onReleaseBounds} + onPointBoundsHandle={state.onPointBoundsHandle} + onDoubleClickBoundsHandle={state.onDoubleClickBoundsHandle} + onRightPointBoundsHandle={state.onRightPointBoundsHandle} + onDragBoundsHandle={state.onDragBoundsHandle} + onHoverBoundsHandle={state.onHoverBoundsHandle} + onUnhoverBoundsHandle={state.onUnhoverBoundsHandle} + onReleaseBoundsHandle={state.onReleaseBoundsHandle} + onPointHandle={state.onPointHandle} + onDoubleClickHandle={state.onDoubleClickHandle} + onRightPointHandle={state.onRightPointHandle} + onDragHandle={state.onDragHandle} + onHoverHandle={state.onHoverHandle} + onUnhoverHandle={state.onUnhoverHandle} + onReleaseHandle={state.onReleaseHandle} + onError={state.onError} + onRenderCountChange={state.onRenderCountChange} + onShapeChange={state.onShapeChange} + onShapeBlur={state.onShapeBlur} + onShapeClone={state.onShapeClone} + onBoundsChange={state.updateBounds} + onKeyDown={state.onKeyDown} + onKeyUp={state.onKeyUp} /> {showUI && ( {settings.isFocusMode ? ( - + ) : ( <> ) -} +}) const OneOff = React.memo(function OneOff({ focusableRef, diff --git a/packages/tldraw/src/components/ContextMenu/ContextMenu.tsx b/packages/tldraw/src/components/ContextMenu/ContextMenu.tsx index a026c037e..acea35bb4 100644 --- a/packages/tldraw/src/components/ContextMenu/ContextMenu.tsx +++ b/packages/tldraw/src/components/ContextMenu/ContextMenu.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import { styled } from '~styles' import * as RadixContextMenu from '@radix-ui/react-context-menu' import { useTLDrawContext } from '~hooks' -import { Data, AlignType, DistributeType, StretchType } from '~types' +import { TLDrawSnapshot, AlignType, DistributeType, StretchType } from '~types' import { AlignBottomIcon, AlignCenterHorizontallyIcon, @@ -21,21 +21,21 @@ import { CMTriggerButton } from './CMTriggerButton' import { Divider } from '~components/Divider' import { MenuContent } from '~components/MenuContent' -const has1SelectedIdsSelector = (s: Data) => { +const has1SelectedIdsSelector = (s: TLDrawSnapshot) => { return s.document.pageStates[s.appState.currentPageId].selectedIds.length > 0 } -const has2SelectedIdsSelector = (s: Data) => { +const has2SelectedIdsSelector = (s: TLDrawSnapshot) => { return s.document.pageStates[s.appState.currentPageId].selectedIds.length > 1 } -const has3SelectedIdsSelector = (s: Data) => { +const has3SelectedIdsSelector = (s: TLDrawSnapshot) => { return s.document.pageStates[s.appState.currentPageId].selectedIds.length > 2 } -const isDebugModeSelector = (s: Data) => { +const isDebugModeSelector = (s: TLDrawSnapshot) => { return s.settings.isDebugMode } -const hasGroupSelectedSelector = (s: Data) => { +const hasGroupSelectedSelector = (s: TLDrawSnapshot) => { return s.document.pageStates[s.appState.currentPageId].selectedIds.some( (id) => s.document.pages[s.appState.currentPageId].shapes[id].children !== undefined ) @@ -48,7 +48,7 @@ interface ContextMenuProps { } export const ContextMenu = ({ children }: ContextMenuProps): JSX.Element => { - const { tlstate, useSelector } = useTLDrawContext() + const { state, useSelector } = useTLDrawContext() const hasSelection = useSelector(has1SelectedIdsSelector) const hasTwoOrMore = useSelector(has2SelectedIdsSelector) const hasThreeOrMore = useSelector(has3SelectedIdsSelector) @@ -58,64 +58,64 @@ export const ContextMenu = ({ children }: ContextMenuProps): JSX.Element => { const rContent = React.useRef(null) const handleFlipHorizontal = React.useCallback(() => { - tlstate.flipHorizontal() - }, [tlstate]) + state.flipHorizontal() + }, [state]) const handleFlipVertical = React.useCallback(() => { - tlstate.flipVertical() - }, [tlstate]) + state.flipVertical() + }, [state]) const handleDuplicate = React.useCallback(() => { - tlstate.duplicate() - }, [tlstate]) + state.duplicate() + }, [state]) const handleGroup = React.useCallback(() => { - tlstate.group() - }, [tlstate]) + state.group() + }, [state]) const handleMoveToBack = React.useCallback(() => { - tlstate.moveToBack() - }, [tlstate]) + state.moveToBack() + }, [state]) const handleMoveBackward = React.useCallback(() => { - tlstate.moveBackward() - }, [tlstate]) + state.moveBackward() + }, [state]) const handleMoveForward = React.useCallback(() => { - tlstate.moveForward() - }, [tlstate]) + state.moveForward() + }, [state]) const handleMoveToFront = React.useCallback(() => { - tlstate.moveToFront() - }, [tlstate]) + state.moveToFront() + }, [state]) const handleDelete = React.useCallback(() => { - tlstate.delete() - }, [tlstate]) + state.delete() + }, [state]) const handleCopyJson = React.useCallback(() => { - tlstate.copyJson() - }, [tlstate]) + state.copyJson() + }, [state]) const handleCopy = React.useCallback(() => { - tlstate.copy() - }, [tlstate]) + state.copy() + }, [state]) const handlePaste = React.useCallback(() => { - tlstate.paste() - }, [tlstate]) + state.paste() + }, [state]) const handleCopySvg = React.useCallback(() => { - tlstate.copySvg() - }, [tlstate]) + state.copySvg() + }, [state]) const handleUndo = React.useCallback(() => { - tlstate.undo() - }, [tlstate]) + state.undo() + }, [state]) const handleRedo = React.useCallback(() => { - tlstate.redo() - }, [tlstate]) + state.redo() + }, [state]) return ( @@ -207,47 +207,47 @@ function AlignDistributeSubMenu({ hasTwoOrMore: boolean hasThreeOrMore: boolean }) { - const { tlstate } = useTLDrawContext() + const { state } = useTLDrawContext() const alignTop = React.useCallback(() => { - tlstate.align(AlignType.Top) - }, [tlstate]) + state.align(AlignType.Top) + }, [state]) const alignCenterVertical = React.useCallback(() => { - tlstate.align(AlignType.CenterVertical) - }, [tlstate]) + state.align(AlignType.CenterVertical) + }, [state]) const alignBottom = React.useCallback(() => { - tlstate.align(AlignType.Bottom) - }, [tlstate]) + state.align(AlignType.Bottom) + }, [state]) const stretchVertically = React.useCallback(() => { - tlstate.stretch(StretchType.Vertical) - }, [tlstate]) + state.stretch(StretchType.Vertical) + }, [state]) const distributeVertically = React.useCallback(() => { - tlstate.distribute(DistributeType.Vertical) - }, [tlstate]) + state.distribute(DistributeType.Vertical) + }, [state]) const alignLeft = React.useCallback(() => { - tlstate.align(AlignType.Left) - }, [tlstate]) + state.align(AlignType.Left) + }, [state]) const alignCenterHorizontal = React.useCallback(() => { - tlstate.align(AlignType.CenterHorizontal) - }, [tlstate]) + state.align(AlignType.CenterHorizontal) + }, [state]) const alignRight = React.useCallback(() => { - tlstate.align(AlignType.Right) - }, [tlstate]) + state.align(AlignType.Right) + }, [state]) const stretchHorizontally = React.useCallback(() => { - tlstate.stretch(StretchType.Horizontal) - }, [tlstate]) + state.stretch(StretchType.Horizontal) + }, [state]) const distributeHorizontally = React.useCallback(() => { - tlstate.distribute(DistributeType.Horizontal) - }, [tlstate]) + state.distribute(DistributeType.Horizontal) + }, [state]) return ( @@ -311,11 +311,11 @@ const StyledGridContent = styled(MenuContent, { /* ------------------ Move to Page ------------------ */ -const currentPageIdSelector = (s: Data) => s.appState.currentPageId -const documentPagesSelector = (s: Data) => s.document.pages +const currentPageIdSelector = (s: TLDrawSnapshot) => s.appState.currentPageId +const documentPagesSelector = (s: TLDrawSnapshot) => s.document.pages function MoveToPageMenu(): JSX.Element | null { - const { tlstate, useSelector } = useTLDrawContext() + const { state, useSelector } = useTLDrawContext() const currentPageId = useSelector(currentPageIdSelector) const documentPages = useSelector(documentPagesSelector) @@ -334,7 +334,7 @@ function MoveToPageMenu(): JSX.Element | null { tlstate.moveToPage(id)} + onSelect={() => state.moveToPage(id)} > {name || `Page ${i}`} diff --git a/packages/tldraw/src/components/ToolsPanel/ActionButton.tsx b/packages/tldraw/src/components/ToolsPanel/ActionButton.tsx index e6f31aa17..4aec1cb51 100644 --- a/packages/tldraw/src/components/ToolsPanel/ActionButton.tsx +++ b/packages/tldraw/src/components/ToolsPanel/ActionButton.tsx @@ -3,7 +3,7 @@ import { Tooltip } from '~components/Tooltip/Tooltip' import * as DropdownMenu from '@radix-ui/react-dropdown-menu' import { useTLDrawContext } from '~hooks' import { styled } from '~styles' -import { AlignType, Data, DistributeType, StretchType } from '~types' +import { AlignType, TLDrawSnapshot, DistributeType, StretchType } from '~types' import { ArrowDownIcon, ArrowUpIcon, @@ -33,22 +33,22 @@ import { TrashIcon } from '~components/icons' import { IconButton } from '~components/IconButton' import { ToolButton } from '~components/ToolButton' -const selectedShapesCountSelector = (s: Data) => +const selectedShapesCountSelector = (s: TLDrawSnapshot) => s.document.pageStates[s.appState.currentPageId].selectedIds.length -const isAllLockedSelector = (s: Data) => { +const isAllLockedSelector = (s: TLDrawSnapshot) => { const page = s.document.pages[s.appState.currentPageId] const { selectedIds } = s.document.pageStates[s.appState.currentPageId] return selectedIds.every((id) => page.shapes[id].isLocked) } -const isAllAspectLockedSelector = (s: Data) => { +const isAllAspectLockedSelector = (s: TLDrawSnapshot) => { const page = s.document.pages[s.appState.currentPageId] const { selectedIds } = s.document.pageStates[s.appState.currentPageId] return selectedIds.every((id) => page.shapes[id].isAspectRatioLocked) } -const isAllGroupedSelector = (s: Data) => { +const isAllGroupedSelector = (s: TLDrawSnapshot) => { const page = s.document.pages[s.appState.currentPageId] const selectedShapes = s.document.pageStates[s.appState.currentPageId].selectedIds.map( (id) => page.shapes[id] @@ -62,18 +62,18 @@ const isAllGroupedSelector = (s: Data) => { ) } -const hasSelectionClickor = (s: Data) => { +const hasSelectionClickor = (s: TLDrawSnapshot) => { const { selectedIds } = s.document.pageStates[s.appState.currentPageId] return selectedIds.length > 0 } -const hasMultipleSelectionClickor = (s: Data) => { +const hasMultipleSelectionClickor = (s: TLDrawSnapshot) => { const { selectedIds } = s.document.pageStates[s.appState.currentPageId] return selectedIds.length > 1 } export function ActionButton(): JSX.Element { - const { tlstate, useSelector } = useTLDrawContext() + const { state, useSelector } = useTLDrawContext() const isAllLocked = useSelector(isAllLockedSelector) @@ -86,84 +86,84 @@ export function ActionButton(): JSX.Element { const hasMultipleSelection = useSelector(hasMultipleSelectionClickor) const handleRotate = React.useCallback(() => { - tlstate.rotate() - }, [tlstate]) + state.rotate() + }, [state]) const handleDuplicate = React.useCallback(() => { - tlstate.duplicate() - }, [tlstate]) + state.duplicate() + }, [state]) const handleToggleLocked = React.useCallback(() => { - tlstate.toggleLocked() - }, [tlstate]) + state.toggleLocked() + }, [state]) const handleToggleAspectRatio = React.useCallback(() => { - tlstate.toggleAspectRatioLocked() - }, [tlstate]) + state.toggleAspectRatioLocked() + }, [state]) const handleGroup = React.useCallback(() => { - tlstate.group() - }, [tlstate]) + state.group() + }, [state]) const handleMoveToBack = React.useCallback(() => { - tlstate.moveToBack() - }, [tlstate]) + state.moveToBack() + }, [state]) const handleMoveBackward = React.useCallback(() => { - tlstate.moveBackward() - }, [tlstate]) + state.moveBackward() + }, [state]) const handleMoveForward = React.useCallback(() => { - tlstate.moveForward() - }, [tlstate]) + state.moveForward() + }, [state]) const handleMoveToFront = React.useCallback(() => { - tlstate.moveToFront() - }, [tlstate]) + state.moveToFront() + }, [state]) const handleDelete = React.useCallback(() => { - tlstate.delete() - }, [tlstate]) + state.delete() + }, [state]) const alignTop = React.useCallback(() => { - tlstate.align(AlignType.Top) - }, [tlstate]) + state.align(AlignType.Top) + }, [state]) const alignCenterVertical = React.useCallback(() => { - tlstate.align(AlignType.CenterVertical) - }, [tlstate]) + state.align(AlignType.CenterVertical) + }, [state]) const alignBottom = React.useCallback(() => { - tlstate.align(AlignType.Bottom) - }, [tlstate]) + state.align(AlignType.Bottom) + }, [state]) const stretchVertically = React.useCallback(() => { - tlstate.stretch(StretchType.Vertical) - }, [tlstate]) + state.stretch(StretchType.Vertical) + }, [state]) const distributeVertically = React.useCallback(() => { - tlstate.distribute(DistributeType.Vertical) - }, [tlstate]) + state.distribute(DistributeType.Vertical) + }, [state]) const alignLeft = React.useCallback(() => { - tlstate.align(AlignType.Left) - }, [tlstate]) + state.align(AlignType.Left) + }, [state]) const alignCenterHorizontal = React.useCallback(() => { - tlstate.align(AlignType.CenterHorizontal) - }, [tlstate]) + state.align(AlignType.CenterHorizontal) + }, [state]) const alignRight = React.useCallback(() => { - tlstate.align(AlignType.Right) - }, [tlstate]) + state.align(AlignType.Right) + }, [state]) const stretchHorizontally = React.useCallback(() => { - tlstate.stretch(StretchType.Horizontal) - }, [tlstate]) + state.stretch(StretchType.Horizontal) + }, [state]) const distributeHorizontally = React.useCallback(() => { - tlstate.distribute(DistributeType.Horizontal) - }, [tlstate]) + state.distribute(DistributeType.Horizontal) + }, [state]) const selectedShapesCount = useSelector(selectedShapesCountSelector) diff --git a/packages/tldraw/src/components/ToolsPanel/BackToContent.tsx b/packages/tldraw/src/components/ToolsPanel/BackToContent.tsx index 90507855e..3508ebeed 100644 --- a/packages/tldraw/src/components/ToolsPanel/BackToContent.tsx +++ b/packages/tldraw/src/components/ToolsPanel/BackToContent.tsx @@ -1,16 +1,16 @@ import * as React from 'react' import { styled } from '~styles' -import type { Data } from '~types' +import type { TLDrawSnapshot } from '~types' import { useTLDrawContext } from '~hooks' import { RowButton } from '~components/RowButton' import { MenuContent } from '~components/MenuContent' -const isEmptyCanvasSelector = (s: Data) => +const isEmptyCanvasSelector = (s: TLDrawSnapshot) => Object.keys(s.document.pages[s.appState.currentPageId].shapes).length > 0 && s.appState.isEmptyCanvas export const BackToContent = React.memo(function BackToContent() { - const { tlstate, useSelector } = useTLDrawContext() + const { state, useSelector } = useTLDrawContext() const isEmptyCanvas = useSelector(isEmptyCanvasSelector) @@ -18,7 +18,7 @@ export const BackToContent = React.memo(function BackToContent() { return ( - Back to content + Back to content ) }) diff --git a/packages/tldraw/src/components/ToolsPanel/LockButton.tsx b/packages/tldraw/src/components/ToolsPanel/LockButton.tsx index f387c80fe..284c02a2b 100644 --- a/packages/tldraw/src/components/ToolsPanel/LockButton.tsx +++ b/packages/tldraw/src/components/ToolsPanel/LockButton.tsx @@ -3,18 +3,18 @@ import { LockClosedIcon, LockOpen1Icon } from '@radix-ui/react-icons' import { Tooltip } from '~components/Tooltip' import { useTLDrawContext } from '~hooks' import { ToolButton } from '~components/ToolButton' -import type { Data } from '~types' +import type { TLDrawSnapshot } from '~types' -const isToolLockedSelector = (s: Data) => s.appState.isToolLocked +const isToolLockedSelector = (s: TLDrawSnapshot) => s.appState.isToolLocked export function LockButton(): JSX.Element { - const { tlstate, useSelector } = useTLDrawContext() + const { state, useSelector } = useTLDrawContext() const isToolLocked = useSelector(isToolLockedSelector) return ( - + {isToolLocked ? : } diff --git a/packages/tldraw/src/components/ToolsPanel/PrimaryTools.tsx b/packages/tldraw/src/components/ToolsPanel/PrimaryTools.tsx index 12f4944fa..bd2d673bf 100644 --- a/packages/tldraw/src/components/ToolsPanel/PrimaryTools.tsx +++ b/packages/tldraw/src/components/ToolsPanel/PrimaryTools.tsx @@ -8,45 +8,45 @@ import { SquareIcon, TextIcon, } from '@radix-ui/react-icons' -import { Data, TLDrawShapeType } from '~types' +import { TLDrawSnapshot, TLDrawShapeType } from '~types' import { useTLDrawContext } from '~hooks' import { ToolButtonWithTooltip } from '~components/ToolButton' import { Panel } from '~components/Panel' -const activeToolSelector = (s: Data) => s.appState.activeTool +const activeToolSelector = (s: TLDrawSnapshot) => s.appState.activeTool export const PrimaryTools = React.memo(function PrimaryTools(): JSX.Element { - const { tlstate, useSelector } = useTLDrawContext() + const { state, useSelector } = useTLDrawContext() const activeTool = useSelector(activeToolSelector) const selectSelectTool = React.useCallback(() => { - tlstate.selectTool('select') - }, [tlstate]) + state.selectTool('select') + }, [state]) const selectDrawTool = React.useCallback(() => { - tlstate.selectTool(TLDrawShapeType.Draw) - }, [tlstate]) + state.selectTool(TLDrawShapeType.Draw) + }, [state]) const selectRectangleTool = React.useCallback(() => { - tlstate.selectTool(TLDrawShapeType.Rectangle) - }, [tlstate]) + state.selectTool(TLDrawShapeType.Rectangle) + }, [state]) const selectEllipseTool = React.useCallback(() => { - tlstate.selectTool(TLDrawShapeType.Ellipse) - }, [tlstate]) + state.selectTool(TLDrawShapeType.Ellipse) + }, [state]) const selectArrowTool = React.useCallback(() => { - tlstate.selectTool(TLDrawShapeType.Arrow) - }, [tlstate]) + state.selectTool(TLDrawShapeType.Arrow) + }, [state]) const selectTextTool = React.useCallback(() => { - tlstate.selectTool(TLDrawShapeType.Text) - }, [tlstate]) + state.selectTool(TLDrawShapeType.Text) + }, [state]) const selectStickyTool = React.useCallback(() => { - tlstate.selectTool(TLDrawShapeType.Sticky) - }, [tlstate]) + state.selectTool(TLDrawShapeType.Sticky) + }, [state]) return ( diff --git a/packages/tldraw/src/components/ToolsPanel/StatusBar.tsx b/packages/tldraw/src/components/ToolsPanel/StatusBar.tsx index fc77e2aea..156e72e3f 100644 --- a/packages/tldraw/src/components/ToolsPanel/StatusBar.tsx +++ b/packages/tldraw/src/components/ToolsPanel/StatusBar.tsx @@ -1,11 +1,11 @@ import * as React from 'react' import { useTLDrawContext } from '~hooks' -import type { Data } from '~types' +import type { TLDrawSnapshot } from '~types' import { styled } from '~styles' import { breakpoints } from '~components/breakpoints' -const statusSelector = (s: Data) => s.appState.status -const activeToolSelector = (s: Data) => s.appState.activeTool +const statusSelector = (s: TLDrawSnapshot) => s.appState.status +const activeToolSelector = (s: TLDrawSnapshot) => s.appState.activeTool export function StatusBar(): JSX.Element | null { const { useSelector } = useTLDrawContext() diff --git a/packages/tldraw/src/components/ToolsPanel/ToolsPanel.tsx b/packages/tldraw/src/components/ToolsPanel/ToolsPanel.tsx index ed3a3f57a..14742f490 100644 --- a/packages/tldraw/src/components/ToolsPanel/ToolsPanel.tsx +++ b/packages/tldraw/src/components/ToolsPanel/ToolsPanel.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { styled } from '~styles' -import type { Data } from '~types' +import type { TLDrawSnapshot } from '~types' import { useTLDrawContext } from '~hooks' import { StatusBar } from './StatusBar' import { BackToContent } from './BackToContent' @@ -8,7 +8,7 @@ import { PrimaryTools } from './PrimaryTools' import { ActionButton } from './ActionButton' import { LockButton } from './LockButton' -const isDebugModeSelector = (s: Data) => s.settings.isDebugMode +const isDebugModeSelector = (s: TLDrawSnapshot) => s.settings.isDebugMode export const ToolsPanel = React.memo(function ToolsPanel(): JSX.Element { const { useSelector } = useTLDrawContext() diff --git a/packages/tldraw/src/components/TopPanel/ColorMenu.tsx b/packages/tldraw/src/components/TopPanel/ColorMenu.tsx index 0cb613c99..6032eba38 100644 --- a/packages/tldraw/src/components/TopPanel/ColorMenu.tsx +++ b/packages/tldraw/src/components/TopPanel/ColorMenu.tsx @@ -5,14 +5,14 @@ import { useTLDrawContext } from '~hooks' import { DMContent, DMTriggerIcon } from '~components/DropdownMenu' import { BoxIcon, CircleIcon } from '~components/icons' import { ToolButton } from '~components/ToolButton' -import type { Data, ColorStyle } from '~types' +import type { TLDrawSnapshot, ColorStyle } from '~types' -const selectColor = (s: Data) => s.appState.selectedStyle.color +const selectColor = (s: TLDrawSnapshot) => s.appState.selectedStyle.color const preventEvent = (e: Event) => e.preventDefault() -const themeSelector = (data: Data) => (data.settings.isDarkMode ? 'dark' : 'light') +const themeSelector = (data: TLDrawSnapshot) => (data.settings.isDarkMode ? 'dark' : 'light') export const ColorMenu = React.memo(function ColorMenu(): JSX.Element { - const { tlstate, useSelector } = useTLDrawContext() + const { state, useSelector } = useTLDrawContext() const theme = useSelector(themeSelector) const color = useSelector(selectColor) @@ -28,7 +28,7 @@ export const ColorMenu = React.memo(function ColorMenu(): JSX.Element { tlstate.style({ color: colorStyle as ColorStyle })} + onClick={() => state.style({ color: colorStyle as ColorStyle })} > , } -const selectDash = (s: Data) => s.appState.selectedStyle.dash +const selectDash = (s: TLDrawSnapshot) => s.appState.selectedStyle.dash const preventEvent = (e: Event) => e.preventDefault() export const DashMenu = React.memo(function DashMenu(): JSX.Element { - const { tlstate, useSelector } = useTLDrawContext() + const { state, useSelector } = useTLDrawContext() const dash = useSelector(selectDash) @@ -31,7 +31,7 @@ export const DashMenu = React.memo(function DashMenu(): JSX.Element { tlstate.style({ dash: dashStyle as DashStyle })} + onClick={() => state.style({ dash: dashStyle as DashStyle })} > {dashes[dashStyle as DashStyle]} diff --git a/packages/tldraw/src/components/TopPanel/FillCheckbox.tsx b/packages/tldraw/src/components/TopPanel/FillCheckbox.tsx index 2602c4f85..c987cde0f 100644 --- a/packages/tldraw/src/components/TopPanel/FillCheckbox.tsx +++ b/packages/tldraw/src/components/TopPanel/FillCheckbox.tsx @@ -1,20 +1,20 @@ import * as React from 'react' import * as Checkbox from '@radix-ui/react-checkbox' import { useTLDrawContext } from '~hooks' -import type { Data } from '~types' +import type { TLDrawSnapshot } from '~types' import { BoxIcon, IsFilledIcon } from '~components/icons' import { ToolButton } from '~components/ToolButton' -const isFilledSelector = (s: Data) => s.appState.selectedStyle.isFilled +const isFilledSelector = (s: TLDrawSnapshot) => s.appState.selectedStyle.isFilled export const FillCheckbox = React.memo(function FillCheckbox(): JSX.Element { - const { tlstate, useSelector } = useTLDrawContext() + const { state, useSelector } = useTLDrawContext() const isFilled = useSelector(isFilledSelector) const handleIsFilledChange = React.useCallback( - (isFilled: boolean) => tlstate.style({ isFilled }), - [tlstate] + (isFilled: boolean) => state.style({ isFilled }), + [state] ) return ( diff --git a/packages/tldraw/src/components/TopPanel/Menu.tsx b/packages/tldraw/src/components/TopPanel/Menu.tsx index 157a36da4..e44324a2f 100644 --- a/packages/tldraw/src/components/TopPanel/Menu.tsx +++ b/packages/tldraw/src/components/TopPanel/Menu.tsx @@ -12,53 +12,53 @@ interface MenuProps { } export const Menu = React.memo(function Menu({ readOnly }: MenuProps) { - const { tlstate, callbacks } = useTLDrawContext() + const { state } = useTLDrawContext() const { onNewProject, onOpenProject, onSaveProject, onSaveProjectAs } = useFileSystemHandlers() const handleSignIn = React.useCallback(() => { - callbacks.onSignIn?.(tlstate) - }, [tlstate]) + state.callbacks.onSignIn?.(state) + }, [state]) const handleSignOut = React.useCallback(() => { - callbacks.onSignOut?.(tlstate) - }, [tlstate]) + state.callbacks.onSignOut?.(state) + }, [state]) const handleCut = React.useCallback(() => { - tlstate.cut() - }, [tlstate]) + state.cut() + }, [state]) const handleCopy = React.useCallback(() => { - tlstate.copy() - }, [tlstate]) + state.copy() + }, [state]) const handlePaste = React.useCallback(() => { - tlstate.paste() - }, [tlstate]) + state.paste() + }, [state]) const handleCopySvg = React.useCallback(() => { - tlstate.copySvg() - }, [tlstate]) + state.copySvg() + }, [state]) const handleCopyJson = React.useCallback(() => { - tlstate.copyJson() - }, [tlstate]) + state.copyJson() + }, [state]) const handleSelectAll = React.useCallback(() => { - tlstate.selectAll() - }, [tlstate]) + state.selectAll() + }, [state]) const handleselectNone = React.useCallback(() => { - tlstate.selectNone() - }, [tlstate]) + state.selectNone() + }, [state]) const showFileMenu = - callbacks.onNewProject || - callbacks.onOpenProject || - callbacks.onSaveProject || - callbacks.onSaveProjectAs + state.callbacks.onNewProject || + state.callbacks.onOpenProject || + state.callbacks.onSaveProject || + state.callbacks.onSaveProjectAs - const showSignInOutMenu = callbacks.onSignIn || callbacks.onSignOut + const showSignInOutMenu = state.callbacks.onSignIn || state.callbacks.onSignOut return ( @@ -68,22 +68,22 @@ export const Menu = React.memo(function Menu({ readOnly }: MenuProps) { {showFileMenu && ( - {callbacks.onNewProject && ( + {state.callbacks.onNewProject && ( New Project )} - {callbacks.onOpenProject && ( + {state.callbacks.onOpenProject && ( Open... )} - {callbacks.onSaveProject && ( + {state.callbacks.onSaveProject && ( Save )} - {callbacks.onSaveProjectAs && ( + {state.callbacks.onSaveProjectAs && ( Save As... @@ -93,10 +93,10 @@ export const Menu = React.memo(function Menu({ readOnly }: MenuProps) { {!readOnly && ( <> - + Undo - + Redo @@ -127,8 +127,8 @@ export const Menu = React.memo(function Menu({ readOnly }: MenuProps) { {showSignInOutMenu && ( <> {' '} - {callbacks.onSignIn && Sign In} - {callbacks.onSignOut && ( + {state.callbacks.onSignIn && Sign In} + {state.callbacks.onSignOut && ( Sign Out diff --git a/packages/tldraw/src/components/TopPanel/PageMenu.tsx b/packages/tldraw/src/components/TopPanel/PageMenu.tsx index cb3a09f61..0c3837cfd 100644 --- a/packages/tldraw/src/components/TopPanel/PageMenu.tsx +++ b/packages/tldraw/src/components/TopPanel/PageMenu.tsx @@ -4,18 +4,19 @@ import { PlusIcon, CheckIcon } from '@radix-ui/react-icons' import { PageOptionsDialog } from './PageOptionsDialog' import { styled } from '~styles' import { useTLDrawContext } from '~hooks' -import type { Data } from '~types' +import type { TLDrawSnapshot } from '~types' import { DMContent, DMDivider } from '~components/DropdownMenu' import { SmallIcon } from '~components/SmallIcon' import { RowButton } from '~components/RowButton' import { ToolButton } from '~components/ToolButton' -const sortedSelector = (s: Data) => +const sortedSelector = (s: TLDrawSnapshot) => Object.values(s.document.pages).sort((a, b) => (a.childIndex || 0) - (b.childIndex || 0)) -const currentPageNameSelector = (s: Data) => s.document.pages[s.appState.currentPageId].name +const currentPageNameSelector = (s: TLDrawSnapshot) => + s.document.pages[s.appState.currentPageId].name -const currentPageIdSelector = (s: Data) => s.document.pages[s.appState.currentPageId].id +const currentPageIdSelector = (s: TLDrawSnapshot) => s.document.pages[s.appState.currentPageId].id export function PageMenu(): JSX.Element { const { useSelector } = useTLDrawContext() @@ -57,22 +58,22 @@ export function PageMenu(): JSX.Element { } function PageMenuContent({ onClose }: { onClose: () => void }) { - const { tlstate, useSelector } = useTLDrawContext() + const { state, useSelector } = useTLDrawContext() const sortedPages = useSelector(sortedSelector) const currentPageId = useSelector(currentPageIdSelector) const handleCreatePage = React.useCallback(() => { - tlstate.createPage() - }, [tlstate]) + state.createPage() + }, [state]) const handleChangePage = React.useCallback( (id: string) => { onClose() - tlstate.changePage(id) + state.changePage(id) }, - [tlstate] + [state] ) return ( diff --git a/packages/tldraw/src/components/TopPanel/PageOptionsDialog.tsx b/packages/tldraw/src/components/TopPanel/PageOptionsDialog.tsx index 094b61ec6..4ecafbced 100644 --- a/packages/tldraw/src/components/TopPanel/PageOptionsDialog.tsx +++ b/packages/tldraw/src/components/TopPanel/PageOptionsDialog.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as Dialog from '@radix-ui/react-alert-dialog' import { MixerVerticalIcon } from '@radix-ui/react-icons' -import type { Data, TLDrawPage } from '~types' +import type { TLDrawSnapshot, TLDrawPage } from '~types' import { useTLDrawContext } from '~hooks' import { RowButton, RowButtonProps } from '~components/RowButton' import { styled } from '~styles' @@ -10,7 +10,7 @@ import { IconButton } from '~components/IconButton/IconButton' import { SmallIcon } from '~components/SmallIcon' import { breakpoints } from '~components/breakpoints' -const canDeleteSelector = (s: Data) => { +const canDeleteSelector = (s: TLDrawSnapshot) => { return Object.keys(s.document.pages).length > 1 } @@ -21,7 +21,7 @@ interface PageOptionsDialogProps { } export function PageOptionsDialog({ page, onOpen, onClose }: PageOptionsDialogProps): JSX.Element { - const { tlstate, useSelector } = useTLDrawContext() + const { state, useSelector } = useTLDrawContext() const [isOpen, setIsOpen] = React.useState(false) @@ -30,16 +30,16 @@ export function PageOptionsDialog({ page, onOpen, onClose }: PageOptionsDialogPr const rInput = React.useRef(null) const handleDuplicate = React.useCallback(() => { - tlstate.duplicatePage(page.id) + state.duplicatePage(page.id) onClose?.() - }, [tlstate]) + }, [state]) const handleDelete = React.useCallback(() => { if (window.confirm(`Are you sure you want to delete this page?`)) { - tlstate.deletePage(page.id) + state.deletePage(page.id) onClose?.() } - }, [tlstate]) + }, [state]) const handleOpenChange = React.useCallback( (isOpen: boolean) => { @@ -50,7 +50,7 @@ export function PageOptionsDialog({ page, onOpen, onClose }: PageOptionsDialogPr return } }, - [tlstate, name] + [state, name] ) function stopPropagation(e: React.KeyboardEvent) { @@ -60,7 +60,7 @@ export function PageOptionsDialog({ page, onOpen, onClose }: PageOptionsDialogPr // TODO: Replace with text input function handleRename() { const nextName = window.prompt('New name:', page.name) - tlstate.renamePage(page.id, nextName || page.name || 'Page') + state.renamePage(page.id, nextName || page.name || 'Page') } React.useEffect(() => { diff --git a/packages/tldraw/src/components/TopPanel/PreferencesMenu.tsx b/packages/tldraw/src/components/TopPanel/PreferencesMenu.tsx index e9de8294b..046f9c124 100644 --- a/packages/tldraw/src/components/TopPanel/PreferencesMenu.tsx +++ b/packages/tldraw/src/components/TopPanel/PreferencesMenu.tsx @@ -1,42 +1,42 @@ import * as React from 'react' import { DMCheckboxItem, DMDivider, DMSubMenu } from '~components/DropdownMenu' import { useTLDrawContext } from '~hooks' -import type { Data } from '~types' +import type { TLDrawSnapshot } from '~types' -const settingsSelector = (s: Data) => s.settings +const settingsSelector = (s: TLDrawSnapshot) => s.settings export function PreferencesMenu() { - const { tlstate, useSelector } = useTLDrawContext() + const { state, useSelector } = useTLDrawContext() const settings = useSelector(settingsSelector) const toggleDebugMode = React.useCallback(() => { - tlstate.setSetting('isDebugMode', (v) => !v) - }, [tlstate]) + state.setSetting('isDebugMode', (v) => !v) + }, [state]) const toggleDarkMode = React.useCallback(() => { - tlstate.setSetting('isDarkMode', (v) => !v) - }, [tlstate]) + state.setSetting('isDarkMode', (v) => !v) + }, [state]) const toggleFocusMode = React.useCallback(() => { - tlstate.setSetting('isFocusMode', (v) => !v) - }, [tlstate]) + state.setSetting('isFocusMode', (v) => !v) + }, [state]) const toggleRotateHandle = React.useCallback(() => { - tlstate.setSetting('showRotateHandles', (v) => !v) - }, [tlstate]) + state.setSetting('showRotateHandles', (v) => !v) + }, [state]) const toggleBoundShapesHandle = React.useCallback(() => { - tlstate.setSetting('showBindingHandles', (v) => !v) - }, [tlstate]) + state.setSetting('showBindingHandles', (v) => !v) + }, [state]) const toggleisSnapping = React.useCallback(() => { - tlstate.setSetting('isSnapping', (v) => !v) - }, [tlstate]) + state.setSetting('isSnapping', (v) => !v) + }, [state]) const toggleCloneControls = React.useCallback(() => { - tlstate.setSetting('showCloneHandles', (v) => !v) - }, [tlstate]) + state.setSetting('showCloneHandles', (v) => !v) + }, [state]) return ( diff --git a/packages/tldraw/src/components/TopPanel/SizeMenu.tsx b/packages/tldraw/src/components/TopPanel/SizeMenu.tsx index de7647fd1..7b9bff5fd 100644 --- a/packages/tldraw/src/components/TopPanel/SizeMenu.tsx +++ b/packages/tldraw/src/components/TopPanel/SizeMenu.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as DropdownMenu from '@radix-ui/react-dropdown-menu' -import { Data, SizeStyle } from '~types' +import { TLDrawSnapshot, SizeStyle } from '~types' import { useTLDrawContext } from '~hooks' import { DMContent, DMTriggerIcon } from '~components/DropdownMenu' import { ToolButton } from '~components/ToolButton' @@ -12,12 +12,12 @@ const sizes = { [SizeStyle.Large]: , } -const selectSize = (s: Data) => s.appState.selectedStyle.size +const selectSize = (s: TLDrawSnapshot) => s.appState.selectedStyle.size const preventEvent = (e: Event) => e.preventDefault() export const SizeMenu = React.memo(function SizeMenu(): JSX.Element { - const { tlstate, useSelector } = useTLDrawContext() + const { state, useSelector } = useTLDrawContext() const size = useSelector(selectSize) @@ -30,7 +30,7 @@ export const SizeMenu = React.memo(function SizeMenu(): JSX.Element { tlstate.style({ size: sizeStyle as SizeStyle })} + onClick={() => state.style({ size: sizeStyle as SizeStyle })} > {sizes[sizeStyle as SizeStyle]} diff --git a/packages/tldraw/src/components/TopPanel/ZoomMenu.tsx b/packages/tldraw/src/components/TopPanel/ZoomMenu.tsx index 8601fb20c..6243b37e2 100644 --- a/packages/tldraw/src/components/TopPanel/ZoomMenu.tsx +++ b/packages/tldraw/src/components/TopPanel/ZoomMenu.tsx @@ -1,15 +1,16 @@ import * as React from 'react' import { useTLDrawContext } from '~hooks' -import type { Data } from '~types' +import type { TLDrawSnapshot } from '~types' import { styled } from '~styles' import * as DropdownMenu from '@radix-ui/react-dropdown-menu' import { DMItem, DMContent } from '~components/DropdownMenu' import { ToolButton } from '~components/ToolButton' -const zoomSelector = (s: Data) => s.document.pageStates[s.appState.currentPageId].camera.zoom +const zoomSelector = (s: TLDrawSnapshot) => + s.document.pageStates[s.appState.currentPageId].camera.zoom export function ZoomMenu() { - const { tlstate, useSelector } = useTLDrawContext() + const { state, useSelector } = useTLDrawContext() const zoom = useSelector(zoomSelector) return ( @@ -18,19 +19,19 @@ export function ZoomMenu() { {Math.round(zoom * 100)}% - + Zoom In - + Zoom Out - + To 100% - + To Fit - + To Selection diff --git a/packages/tldraw/src/hooks/useFileSystem.ts b/packages/tldraw/src/hooks/useFileSystem.ts index 79f88585e..904d21148 100644 --- a/packages/tldraw/src/hooks/useFileSystem.ts +++ b/packages/tldraw/src/hooks/useFileSystem.ts @@ -2,40 +2,40 @@ import * as React from 'react' import type { TLDrawState } from '~state' export function useFileSystem() { - const promptSaveBeforeChange = React.useCallback(async (tlstate: TLDrawState) => { - if (tlstate.isDirty) { - if (tlstate.fileSystemHandle) { + const promptSaveBeforeChange = React.useCallback(async (state: TLDrawState) => { + if (state.isDirty) { + if (state.fileSystemHandle) { if (window.confirm('Do you want to save changes to your current project?')) { - await tlstate.saveProject() + await state.saveProject() } } else { if (window.confirm('Do you want to save your current project?')) { - await tlstate.saveProject() + await state.saveProject() } } } }, []) const onNewProject = React.useCallback( - async (tlstate: TLDrawState) => { - await promptSaveBeforeChange(tlstate) - tlstate.newProject() + async (state: TLDrawState) => { + await promptSaveBeforeChange(state) + state.newProject() }, [promptSaveBeforeChange] ) - const onSaveProject = React.useCallback((tlstate: TLDrawState) => { - tlstate.saveProject() + const onSaveProject = React.useCallback((state: TLDrawState) => { + state.saveProject() }, []) - const onSaveProjectAs = React.useCallback((tlstate: TLDrawState) => { - tlstate.saveProjectAs() + const onSaveProjectAs = React.useCallback((state: TLDrawState) => { + state.saveProjectAs() }, []) const onOpenProject = React.useCallback( - async (tlstate: TLDrawState) => { - await promptSaveBeforeChange(tlstate) - tlstate.openProject() + async (state: TLDrawState) => { + await promptSaveBeforeChange(state) + state.openProject() }, [promptSaveBeforeChange] ) diff --git a/packages/tldraw/src/hooks/useFileSystemHandlers.ts b/packages/tldraw/src/hooks/useFileSystemHandlers.ts index 1fad1cc2c..00b6c2a04 100644 --- a/packages/tldraw/src/hooks/useFileSystemHandlers.ts +++ b/packages/tldraw/src/hooks/useFileSystemHandlers.ts @@ -2,38 +2,38 @@ import * as React from 'react' import { useTLDrawContext } from '~hooks' export function useFileSystemHandlers() { - const { tlstate, callbacks } = useTLDrawContext() + const { state } = useTLDrawContext() const onNewProject = React.useCallback( async (e?: KeyboardEvent) => { - if (e && callbacks.onOpenProject) e.preventDefault() - callbacks.onNewProject?.(tlstate) + if (e && state.callbacks.onOpenProject) e.preventDefault() + state.callbacks.onNewProject?.(state) }, - [callbacks] + [state] ) const onSaveProject = React.useCallback( (e?: KeyboardEvent) => { - if (e && callbacks.onOpenProject) e.preventDefault() - callbacks.onSaveProject?.(tlstate) + if (e && state.callbacks.onOpenProject) e.preventDefault() + state.callbacks.onSaveProject?.(state) }, - [callbacks] + [state] ) const onSaveProjectAs = React.useCallback( (e?: KeyboardEvent) => { - if (e && callbacks.onOpenProject) e.preventDefault() - callbacks.onSaveProjectAs?.(tlstate) + if (e && state.callbacks.onOpenProject) e.preventDefault() + state.callbacks.onSaveProjectAs?.(state) }, - [callbacks] + [state] ) const onOpenProject = React.useCallback( async (e?: KeyboardEvent) => { - if (e && callbacks.onOpenProject) e.preventDefault() - callbacks.onOpenProject?.(tlstate) + if (e && state.callbacks.onOpenProject) e.preventDefault() + state.callbacks.onOpenProject?.(state) }, - [callbacks] + [state] ) return { diff --git a/packages/tldraw/src/hooks/useKeyboardShortcuts.tsx b/packages/tldraw/src/hooks/useKeyboardShortcuts.tsx index fcdea9dd7..071623513 100644 --- a/packages/tldraw/src/hooks/useKeyboardShortcuts.tsx +++ b/packages/tldraw/src/hooks/useKeyboardShortcuts.tsx @@ -4,7 +4,7 @@ import { TLDrawShapeType } from '~types' import { useFileSystemHandlers, useTLDrawContext } from '~hooks' export function useKeyboardShortcuts(ref: React.RefObject) { - const { tlstate } = useTLDrawContext() + const { state } = useTLDrawContext() const canHandleEvent = React.useCallback(() => { const elm = ref.current @@ -16,63 +16,63 @@ export function useKeyboardShortcuts(ref: React.RefObject) { useHotkeys( 'v,1', () => { - if (canHandleEvent()) tlstate.selectTool('select') + if (canHandleEvent()) state.selectTool('select') }, - [tlstate, ref.current] + [state, ref.current] ) useHotkeys( 'd,2', () => { - if (canHandleEvent()) tlstate.selectTool(TLDrawShapeType.Draw) + if (canHandleEvent()) state.selectTool(TLDrawShapeType.Draw) }, undefined, - [tlstate] + [state] ) useHotkeys( 'r,3', () => { - if (canHandleEvent()) tlstate.selectTool(TLDrawShapeType.Rectangle) + if (canHandleEvent()) state.selectTool(TLDrawShapeType.Rectangle) }, undefined, - [tlstate] + [state] ) useHotkeys( 'e,4', () => { - if (canHandleEvent()) tlstate.selectTool(TLDrawShapeType.Ellipse) + if (canHandleEvent()) state.selectTool(TLDrawShapeType.Ellipse) }, undefined, - [tlstate] + [state] ) useHotkeys( 'a,5', () => { - if (canHandleEvent()) tlstate.selectTool(TLDrawShapeType.Arrow) + if (canHandleEvent()) state.selectTool(TLDrawShapeType.Arrow) }, undefined, - [tlstate] + [state] ) useHotkeys( 't,6', () => { - if (canHandleEvent()) tlstate.selectTool(TLDrawShapeType.Text) + if (canHandleEvent()) state.selectTool(TLDrawShapeType.Text) }, undefined, - [tlstate] + [state] ) useHotkeys( 'n,7', () => { - if (canHandleEvent()) tlstate.selectTool(TLDrawShapeType.Sticky) + if (canHandleEvent()) state.selectTool(TLDrawShapeType.Sticky) }, undefined, - [tlstate] + [state] ) /* ---------------------- Misc ---------------------- */ @@ -83,12 +83,12 @@ export function useKeyboardShortcuts(ref: React.RefObject) { 'ctrl+shift+d,command+shift+d', (e) => { if (canHandleEvent()) { - tlstate.toggleDarkMode() + state.toggleDarkMode() e.preventDefault() } }, undefined, - [tlstate] + [state] ) // Focus Mode @@ -96,10 +96,10 @@ export function useKeyboardShortcuts(ref: React.RefObject) { useHotkeys( 'ctrl+.,command+.', () => { - if (canHandleEvent()) tlstate.toggleFocusMode() + if (canHandleEvent()) state.toggleFocusMode() }, undefined, - [tlstate] + [state] ) // File System @@ -114,7 +114,7 @@ export function useKeyboardShortcuts(ref: React.RefObject) { } }, undefined, - [tlstate] + [state] ) useHotkeys( 'ctrl+s,command+s', @@ -124,7 +124,7 @@ export function useKeyboardShortcuts(ref: React.RefObject) { } }, undefined, - [tlstate] + [state] ) useHotkeys( @@ -135,7 +135,7 @@ export function useKeyboardShortcuts(ref: React.RefObject) { } }, undefined, - [tlstate] + [state] ) useHotkeys( 'ctrl+o,command+o', @@ -145,7 +145,7 @@ export function useKeyboardShortcuts(ref: React.RefObject) { } }, undefined, - [tlstate] + [state] ) // Undo Redo @@ -154,30 +154,30 @@ export function useKeyboardShortcuts(ref: React.RefObject) { 'command+z,ctrl+z', () => { if (canHandleEvent()) { - if (tlstate.session) { - tlstate.cancelSession() + if (state.session) { + state.cancelSession() } else { - tlstate.undo() + state.undo() } } }, undefined, - [tlstate] + [state] ) useHotkeys( 'ctrl+shift-z,command+shift+z', () => { if (canHandleEvent()) { - if (tlstate.session) { - tlstate.cancelSession() + if (state.session) { + state.cancelSession() } else { - tlstate.redo() + state.redo() } } }, undefined, - [tlstate] + [state] ) // Undo Redo @@ -185,19 +185,19 @@ export function useKeyboardShortcuts(ref: React.RefObject) { useHotkeys( 'command+u,ctrl+u', () => { - if (canHandleEvent()) tlstate.undoSelect() + if (canHandleEvent()) state.undoSelect() }, undefined, - [tlstate] + [state] ) useHotkeys( 'ctrl+shift-u,command+shift+u', () => { - if (canHandleEvent()) tlstate.redoSelect() + if (canHandleEvent()) state.redoSelect() }, undefined, - [tlstate] + [state] ) /* -------------------- Commands -------------------- */ @@ -208,51 +208,51 @@ export function useKeyboardShortcuts(ref: React.RefObject) { 'ctrl+=,command+=', (e) => { if (canHandleEvent()) { - tlstate.zoomIn() + state.zoomIn() e.preventDefault() } }, undefined, - [tlstate] + [state] ) useHotkeys( 'ctrl+-,command+-', (e) => { if (canHandleEvent()) { - tlstate.zoomOut() + state.zoomOut() e.preventDefault() } }, undefined, - [tlstate] + [state] ) useHotkeys( 'shift+1', () => { - if (canHandleEvent()) tlstate.zoomToFit() + if (canHandleEvent()) state.zoomToFit() }, undefined, - [tlstate] + [state] ) useHotkeys( 'shift+2', () => { - if (canHandleEvent()) tlstate.zoomToSelection() + if (canHandleEvent()) state.zoomToSelection() }, undefined, - [tlstate] + [state] ) useHotkeys( 'shift+0', () => { - if (canHandleEvent()) tlstate.resetZoom() + if (canHandleEvent()) state.resetZoom() }, undefined, - [tlstate] + [state] ) // Duplicate @@ -261,12 +261,12 @@ export function useKeyboardShortcuts(ref: React.RefObject) { 'ctrl+d,command+d', (e) => { if (canHandleEvent()) { - tlstate.duplicate() + state.duplicate() e.preventDefault() } }, undefined, - [tlstate] + [state] ) // Flip @@ -274,19 +274,19 @@ export function useKeyboardShortcuts(ref: React.RefObject) { useHotkeys( 'shift+h', () => { - if (canHandleEvent()) tlstate.flipHorizontal() + if (canHandleEvent()) state.flipHorizontal() }, undefined, - [tlstate] + [state] ) useHotkeys( 'shift+v', () => { - if (canHandleEvent()) tlstate.flipVertical() + if (canHandleEvent()) state.flipVertical() }, undefined, - [tlstate] + [state] ) // Cancel @@ -295,11 +295,11 @@ export function useKeyboardShortcuts(ref: React.RefObject) { 'escape', () => { if (canHandleEvent()) { - tlstate.cancel() + state.cancel() } }, undefined, - [tlstate] + [state] ) // Delete @@ -307,10 +307,10 @@ export function useKeyboardShortcuts(ref: React.RefObject) { useHotkeys( 'backspace', () => { - if (canHandleEvent()) tlstate.delete() + if (canHandleEvent()) state.delete() }, undefined, - [tlstate] + [state] ) // Select All @@ -318,10 +318,10 @@ export function useKeyboardShortcuts(ref: React.RefObject) { useHotkeys( 'command+a,ctrl+a', () => { - if (canHandleEvent()) tlstate.selectAll() + if (canHandleEvent()) state.selectAll() }, undefined, - [tlstate] + [state] ) // Nudge @@ -329,73 +329,73 @@ export function useKeyboardShortcuts(ref: React.RefObject) { useHotkeys( 'up', () => { - if (canHandleEvent()) tlstate.nudge([0, -1], false) + if (canHandleEvent()) state.nudge([0, -1], false) }, undefined, - [tlstate] + [state] ) useHotkeys( 'right', () => { - if (canHandleEvent()) tlstate.nudge([1, 0], false) + if (canHandleEvent()) state.nudge([1, 0], false) }, undefined, - [tlstate] + [state] ) useHotkeys( 'down', () => { - if (canHandleEvent()) tlstate.nudge([0, 1], false) + if (canHandleEvent()) state.nudge([0, 1], false) }, undefined, - [tlstate] + [state] ) useHotkeys( 'left', () => { - if (canHandleEvent()) tlstate.nudge([-1, 0], false) + if (canHandleEvent()) state.nudge([-1, 0], false) }, undefined, - [tlstate] + [state] ) useHotkeys( 'shift+up', () => { - if (canHandleEvent()) tlstate.nudge([0, -1], true) + if (canHandleEvent()) state.nudge([0, -1], true) }, undefined, - [tlstate] + [state] ) useHotkeys( 'shift+right', () => { - if (canHandleEvent()) tlstate.nudge([1, 0], true) + if (canHandleEvent()) state.nudge([1, 0], true) }, undefined, - [tlstate] + [state] ) useHotkeys( 'shift+down', () => { - if (canHandleEvent()) tlstate.nudge([0, 1], true) + if (canHandleEvent()) state.nudge([0, 1], true) }, undefined, - [tlstate] + [state] ) useHotkeys( 'shift+left', () => { - if (canHandleEvent()) tlstate.nudge([-1, 0], true) + if (canHandleEvent()) state.nudge([-1, 0], true) }, undefined, - [tlstate] + [state] ) // Copy, Cut & Paste @@ -403,28 +403,28 @@ export function useKeyboardShortcuts(ref: React.RefObject) { useHotkeys( 'command+c,ctrl+c', () => { - if (canHandleEvent()) tlstate.copy() + if (canHandleEvent()) state.copy() }, undefined, - [tlstate] + [state] ) useHotkeys( 'command+x,ctrl+x', () => { - if (canHandleEvent()) tlstate.cut() + if (canHandleEvent()) state.cut() }, undefined, - [tlstate] + [state] ) useHotkeys( 'command+v,ctrl+v', () => { - if (canHandleEvent()) tlstate.paste() + if (canHandleEvent()) state.paste() }, undefined, - [tlstate] + [state] ) // Group & Ungroup @@ -433,24 +433,24 @@ export function useKeyboardShortcuts(ref: React.RefObject) { 'command+g,ctrl+g', (e) => { if (canHandleEvent()) { - tlstate.group() + state.group() e.preventDefault() } }, undefined, - [tlstate] + [state] ) useHotkeys( 'command+shift+g,ctrl+shift+g', (e) => { if (canHandleEvent()) { - tlstate.ungroup() + state.ungroup() e.preventDefault() } }, undefined, - [tlstate] + [state] ) // Move @@ -458,37 +458,37 @@ export function useKeyboardShortcuts(ref: React.RefObject) { useHotkeys( '[', () => { - if (canHandleEvent()) tlstate.moveBackward() + if (canHandleEvent()) state.moveBackward() }, undefined, - [tlstate] + [state] ) useHotkeys( ']', () => { - if (canHandleEvent()) tlstate.moveForward() + if (canHandleEvent()) state.moveForward() }, undefined, - [tlstate] + [state] ) useHotkeys( 'shift+[', () => { - if (canHandleEvent()) tlstate.moveToBack() + if (canHandleEvent()) state.moveToBack() }, undefined, - [tlstate] + [state] ) useHotkeys( 'shift+]', () => { - if (canHandleEvent()) tlstate.moveToFront() + if (canHandleEvent()) state.moveToFront() }, undefined, - [tlstate] + [state] ) useHotkeys( @@ -496,12 +496,12 @@ export function useKeyboardShortcuts(ref: React.RefObject) { (e) => { if (canHandleEvent()) { if (process.env.NODE_ENV === 'development') { - tlstate.resetDocument() + state.resetDocument() } e.preventDefault() } }, undefined, - [tlstate] + [state] ) } diff --git a/packages/tldraw/src/hooks/useTLDrawContext.tsx b/packages/tldraw/src/hooks/useTLDrawContext.tsx index a638ba9ba..cd57c6ae3 100644 --- a/packages/tldraw/src/hooks/useTLDrawContext.tsx +++ b/packages/tldraw/src/hooks/useTLDrawContext.tsx @@ -1,19 +1,11 @@ import * as React from 'react' -import type { Data } from '~types' +import type { TLDrawSnapshot } from '~types' import type { UseBoundStore } from 'zustand' import type { TLDrawState } from '~state' export interface TLDrawContextType { - tlstate: TLDrawState - useSelector: UseBoundStore - callbacks: { - onNewProject?: (tlstate: TLDrawState) => void - onSaveProject?: (tlstate: TLDrawState) => void - onSaveProjectAs?: (tlstate: TLDrawState) => void - onOpenProject?: (tlstate: TLDrawState) => void - onSignIn?: (tlstate: TLDrawState) => void - onSignOut?: (tlstate: TLDrawState) => void - } + state: TLDrawState + useSelector: UseBoundStore } export const TLDrawContext = React.createContext({} as TLDrawContextType) diff --git a/packages/tldraw/src/hooks/useTheme.ts b/packages/tldraw/src/hooks/useTheme.ts index 419b74638..63e59a3fe 100644 --- a/packages/tldraw/src/hooks/useTheme.ts +++ b/packages/tldraw/src/hooks/useTheme.ts @@ -1,14 +1,14 @@ -import type { Data, Theme } from '~types' +import type { TLDrawSnapshot, Theme } from '~types' import { useTLDrawContext } from './useTLDrawContext' -const themeSelector = (data: Data): Theme => (data.settings.isDarkMode ? 'dark' : 'light') +const themeSelector = (data: TLDrawSnapshot): Theme => (data.settings.isDarkMode ? 'dark' : 'light') export function useTheme() { - const { tlstate, useSelector } = useTLDrawContext() + const { state, useSelector } = useTLDrawContext() const theme = useSelector(themeSelector) return { theme, - toggle: tlstate.toggleDarkMode, + toggle: state.toggleDarkMode, } } diff --git a/packages/tldraw/src/state/TLDR.ts b/packages/tldraw/src/state/TLDR.ts index 2604ae8e6..8be397abb 100644 --- a/packages/tldraw/src/state/TLDR.ts +++ b/packages/tldraw/src/state/TLDR.ts @@ -1,6 +1,6 @@ import { TLBounds, TLTransformInfo, Utils, TLPageState } from '@tldraw/core' import { - Data, + TLDrawSnapshot, ShapeStyles, ShapesWithProp, TLDrawShape, @@ -24,13 +24,13 @@ export class TLDR { return getShapeUtils(shape) } - static getSelectedShapes(data: Data, pageId: string) { + static getSelectedShapes(data: TLDrawSnapshot, pageId: string) { const page = TLDR.getPage(data, pageId) const selectedIds = TLDR.getSelectedIds(data, pageId) return selectedIds.map((id) => page.shapes[id]) } - static screenToWorld(data: Data, point: number[]) { + static screenToWorld(data: TLDrawSnapshot, point: number[]) { const camera = TLDR.getPageState(data, data.appState.currentPageId).camera return Vec.sub(Vec.div(point, camera.zoom), camera.point) } @@ -39,28 +39,28 @@ export class TLDR { return Utils.clamp(zoom, 0.1, 5) } - static getPage(data: Data, pageId: string): TLDrawPage { + static getPage(data: TLDrawSnapshot, pageId: string): TLDrawPage { return data.document.pages[pageId] } - static getPageState(data: Data, pageId: string): TLPageState { + static getPageState(data: TLDrawSnapshot, pageId: string): TLPageState { return data.document.pageStates[pageId] } - static getSelectedIds(data: Data, pageId: string): string[] { + static getSelectedIds(data: TLDrawSnapshot, pageId: string): string[] { return TLDR.getPageState(data, pageId).selectedIds } - static getShapes(data: Data, pageId: string): TLDrawShape[] { + static getShapes(data: TLDrawSnapshot, pageId: string): TLDrawShape[] { return Object.values(TLDR.getPage(data, pageId).shapes) } - static getCamera(data: Data, pageId: string): TLPageState['camera'] { + static getCamera(data: TLDrawSnapshot, pageId: string): TLPageState['camera'] { return TLDR.getPageState(data, pageId).camera } static getShape( - data: Data, + data: TLDrawSnapshot, shapeId: string, pageId: string ): T { @@ -79,7 +79,7 @@ export class TLDR { return TLDR.getShapeUtils(shape).getRotatedBounds(shape) } - static getSelectedBounds(data: Data): TLBounds { + static getSelectedBounds(data: TLDrawSnapshot): TLBounds { return Utils.getCommonBounds( TLDR.getSelectedShapes(data, data.appState.currentPageId).map((shape) => TLDR.getShapeUtils(shape).getBounds(shape) @@ -87,11 +87,11 @@ export class TLDR { ) } - static getParentId(data: Data, id: string, pageId: string) { + static getParentId(data: TLDrawSnapshot, id: string, pageId: string) { return TLDR.getShape(data, id, pageId).parentId } - // static getPointedId(data: Data, id: string, pageId: string): string { + // static getPointedId(data: TLDrawSnapshot, id: string, pageId: string): string { // const page = TLDR.getPage(data, pageId) // const pageState = TLDR.getPageState(data, data.appState.currentPageId) // const shape = TLDR.getShape(data, id, pageId) @@ -102,7 +102,7 @@ export class TLDR { // : TLDR.getPointedId(data, shape.parentId, pageId) // } - // static getDrilledPointedId(data: Data, id: string, pageId: string): string { + // static getDrilledPointedId(data: TLDrawSnapshot, id: string, pageId: string): string { // const shape = TLDR.getShape(data, id, pageId) // const { currentPageId } = data.appState // const { currentParentId, pointedId } = TLDR.getPageState(data, data.appState.currentPageId) @@ -114,7 +114,7 @@ export class TLDR { // : TLDR.getDrilledPointedId(data, shape.parentId, pageId) // } - // static getTopParentId(data: Data, id: string, pageId: string): string { + // static getTopParentId(data: TLDrawSnapshot, id: string, pageId: string): string { // const page = TLDR.getPage(data, pageId) // const pageState = TLDR.getPageState(data, pageId) // const shape = TLDR.getShape(data, id, pageId) @@ -129,7 +129,7 @@ export class TLDR { // } // Get an array of a shape id and its descendant shapes' ids - static getDocumentBranch(data: Data, id: string, pageId: string): string[] { + static getDocumentBranch(data: TLDrawSnapshot, id: string, pageId: string): string[] { const shape = TLDR.getShape(data, id, pageId) if (shape.children === undefined) return [id] @@ -142,13 +142,13 @@ export class TLDR { // Get a deep array of unproxied shapes and their descendants static getSelectedBranchSnapshot( - data: Data, + data: TLDrawSnapshot, pageId: string, fn: (shape: TLDrawShape) => K ): ({ id: string } & K)[] - static getSelectedBranchSnapshot(data: Data, pageId: string): TLDrawShape[] + static getSelectedBranchSnapshot(data: TLDrawSnapshot, pageId: string): TLDrawShape[] static getSelectedBranchSnapshot( - data: Data, + data: TLDrawSnapshot, pageId: string, fn?: (shape: TLDrawShape) => K ): (TLDrawShape | K)[] { @@ -167,14 +167,14 @@ export class TLDR { } // Get a shallow array of unproxied shapes - static getSelectedShapeSnapshot(data: Data, pageId: string): TLDrawShape[] + static getSelectedShapeSnapshot(data: TLDrawSnapshot, pageId: string): TLDrawShape[] static getSelectedShapeSnapshot( - data: Data, + data: TLDrawSnapshot, pageId: string, fn?: (shape: TLDrawShape) => K ): ({ id: string } & K)[] static getSelectedShapeSnapshot( - data: Data, + data: TLDrawSnapshot, pageId: string, fn?: (shape: TLDrawShape) => K ): (TLDrawShape | K)[] { @@ -191,7 +191,7 @@ export class TLDR { // For a given array of shape ids, an array of all other shapes that may be affected by a mutation to it. // Use this to decide which shapes to clone as before / after for a command. - static getAllEffectedShapeIds(data: Data, ids: string[], pageId: string): string[] { + static getAllEffectedShapeIds(data: TLDrawSnapshot, ids: string[], pageId: string): string[] { const page = TLDR.getPage(data, pageId) const visited = new Set(ids) @@ -236,41 +236,47 @@ export class TLDR { } static updateBindings( - data: Data, + data: TLDrawSnapshot, id: string, beforeShapes: Record> = {}, afterShapes: Record> = {}, pageId: string - ): Data { + ): TLDrawSnapshot { const page = { ...TLDR.getPage(data, pageId) } return Object.values(page.bindings) .filter((binding) => binding.fromId === id || binding.toId === id) - .reduce((cData, binding) => { + .reduce((cTLDrawSnapshot, binding) => { if (!beforeShapes[binding.fromId]) { beforeShapes[binding.fromId] = Utils.deepClone( - TLDR.getShape(cData, binding.fromId, pageId) + TLDR.getShape(cTLDrawSnapshot, binding.fromId, pageId) ) } if (!beforeShapes[binding.toId]) { - beforeShapes[binding.toId] = Utils.deepClone(TLDR.getShape(cData, binding.toId, pageId)) + beforeShapes[binding.toId] = Utils.deepClone( + TLDR.getShape(cTLDrawSnapshot, binding.toId, pageId) + ) } TLDR.onBindingChange( - TLDR.getShape(cData, binding.fromId, pageId), + TLDR.getShape(cTLDrawSnapshot, binding.fromId, pageId), binding, - TLDR.getShape(cData, binding.toId, pageId) + TLDR.getShape(cTLDrawSnapshot, binding.toId, pageId) ) - afterShapes[binding.fromId] = Utils.deepClone(TLDR.getShape(cData, binding.fromId, pageId)) - afterShapes[binding.toId] = Utils.deepClone(TLDR.getShape(cData, binding.toId, pageId)) + afterShapes[binding.fromId] = Utils.deepClone( + TLDR.getShape(cTLDrawSnapshot, binding.fromId, pageId) + ) + afterShapes[binding.toId] = Utils.deepClone( + TLDR.getShape(cTLDrawSnapshot, binding.toId, pageId) + ) - return cData + return cTLDrawSnapshot }, data) } static getLinkedShapes( - data: Data, + data: TLDrawSnapshot, pageId: string, direction: 'center' | 'left' | 'right', includeArrows = true @@ -372,7 +378,7 @@ export class TLDR { return Array.from(linkedIds.values()) } - static getChildIndexAbove(data: Data, id: string, pageId: string): number { + static getChildIndexAbove(data: TLDrawSnapshot, id: string, pageId: string): number { const page = data.document.pages[pageId] const shape = page.shapes[id] @@ -410,14 +416,14 @@ export class TLDR { } static mutateShapes( - data: Data, + data: TLDrawSnapshot, ids: string[], fn: (shape: T, i: number) => Partial | void, pageId: string ): { before: Record> after: Record> - data: Data + data: TLDrawSnapshot } { const beforeShapes: Record> = {} const afterShapes: Record> = {} @@ -440,8 +446,8 @@ export class TLDR { }, }, }) - const dataWithBindingChanges = ids.reduce((cData, id) => { - return TLDR.updateBindings(cData, id, beforeShapes, afterShapes, pageId) + const dataWithBindingChanges = ids.reduce((cTLDrawSnapshot, id) => { + return TLDR.updateBindings(cTLDrawSnapshot, id, beforeShapes, afterShapes, pageId) }, dataWithMutations) return { @@ -451,7 +457,7 @@ export class TLDR { } } - static createShapes(data: Data, shapes: TLDrawShape[], pageId: string): TLDrawCommand { + static createShapes(data: TLDrawSnapshot, shapes: TLDrawShape[], pageId: string): TLDrawCommand { const before: TLDrawPatch = { document: { pages: { @@ -515,7 +521,7 @@ export class TLDR { } static deleteShapes( - data: Data, + data: TLDrawSnapshot, shapes: TLDrawShape[] | string[], pageId?: string ): TLDrawCommand { @@ -612,7 +618,7 @@ export class TLDR { return { ...shape, ...delta } } - static onChildrenChange(data: Data, shape: T, pageId: string) { + static onChildrenChange(data: TLDrawSnapshot, shape: T, pageId: string) { if (!shape.children) return const delta = TLDR.getShapeUtils(shape).onChildrenChange?.( @@ -717,7 +723,7 @@ export class TLDR { /* Parents */ /* -------------------------------------------------- */ - static updateParents(data: Data, pageId: string, changedShapeIds: string[]): void { + static updateParents(data: TLDrawSnapshot, pageId: string, changedShapeIds: string[]): void { const page = TLDR.getPage(data, pageId) if (changedShapeIds.length === 0) return @@ -741,7 +747,7 @@ export class TLDR { TLDR.updateParents(data, pageId, parentToUpdateIds) } - static getSelectedStyle(data: Data, pageId: string): ShapeStyles | false { + static getSelectedStyle(data: TLDrawSnapshot, pageId: string): ShapeStyles | false { const { currentStyle } = data.appState const page = data.document.pages[pageId] @@ -782,23 +788,27 @@ export class TLDR { /* Bindings */ /* -------------------------------------------------- */ - static getBinding(data: Data, id: string, pageId: string): TLDrawBinding { + static getBinding(data: TLDrawSnapshot, id: string, pageId: string): TLDrawBinding { return TLDR.getPage(data, pageId).bindings[id] } - static getBindings(data: Data, pageId: string): TLDrawBinding[] { + static getBindings(data: TLDrawSnapshot, pageId: string): TLDrawBinding[] { const page = TLDR.getPage(data, pageId) return Object.values(page.bindings) } - static getBindableShapeIds(data: Data) { + static getBindableShapeIds(data: TLDrawSnapshot) { return TLDR.getShapes(data, data.appState.currentPageId) .filter((shape) => TLDR.getShapeUtils(shape).canBind) .sort((a, b) => b.childIndex - a.childIndex) .map((shape) => shape.id) } - static getBindingsWithShapeIds(data: Data, ids: string[], pageId: string): TLDrawBinding[] { + static getBindingsWithShapeIds( + data: TLDrawSnapshot, + ids: string[], + pageId: string + ): TLDrawBinding[] { return Array.from( new Set( TLDR.getBindings(data, pageId).filter((binding) => { @@ -808,7 +818,7 @@ export class TLDR { ) } - static getRelatedBindings(data: Data, ids: string[], pageId: string): TLDrawBinding[] { + static getRelatedBindings(data: TLDrawSnapshot, ids: string[], pageId: string): TLDrawBinding[] { const changedShapeIds = new Set(ids) const page = TLDR.getPage(data, pageId) @@ -887,7 +897,7 @@ export class TLDR { /* Groups */ /* -------------------------------------------------- */ - static flattenShape = (data: Data, shape: TLDrawShape): TLDrawShape[] => { + static flattenShape = (data: TLDrawSnapshot, shape: TLDrawShape): TLDrawShape[] => { return [ shape, ...(shape.children ?? []) @@ -897,13 +907,13 @@ export class TLDR { ] } - static flattenPage = (data: Data, pageId: string): TLDrawShape[] => { + static flattenPage = (data: TLDrawSnapshot, pageId: string): TLDrawShape[] => { return Object.values(data.document.pages[pageId].shapes) .sort((a, b) => a.childIndex - b.childIndex) .reduce((acc, shape) => [...acc, ...TLDR.flattenShape(data, shape)], []) } - static getTopChildIndex = (data: Data, pageId: string): number => { + static getTopChildIndex = (data: TLDrawSnapshot, pageId: string): number => { const shapes = TLDR.getShapes(data, pageId) return shapes.length === 0 ? 1 diff --git a/packages/tldraw/src/state/TLDrawState.spec.ts b/packages/tldraw/src/state/TLDrawState.spec.ts index e134e5311..d277cb5e2 100644 --- a/packages/tldraw/src/state/TLDrawState.spec.ts +++ b/packages/tldraw/src/state/TLDrawState.spec.ts @@ -5,63 +5,63 @@ import { ArrowShape, ColorStyle, SessionType, TLDrawShapeType } from '~types' import type { SelectTool } from './tools/SelectTool' describe('TLDrawState', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() - const tlu = new TLDrawStateUtils(tlstate) + const tlu = new TLDrawStateUtils(state) describe('When copying and pasting...', () => { it('copies a shape', () => { - tlstate.loadDocument(mockDocument).selectNone().copy(['rect1']) + state.loadDocument(mockDocument).selectNone().copy(['rect1']) }) it('pastes a shape', () => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) - const prevCount = Object.keys(tlstate.page.shapes).length + const prevCount = Object.keys(state.page.shapes).length - tlstate.selectNone().copy(['rect1']).paste() + state.selectNone().copy(['rect1']).paste() - expect(Object.keys(tlstate.page.shapes).length).toBe(prevCount + 1) + expect(Object.keys(state.page.shapes).length).toBe(prevCount + 1) - tlstate.undo() + state.undo() - expect(Object.keys(tlstate.page.shapes).length).toBe(prevCount) + expect(Object.keys(state.page.shapes).length).toBe(prevCount) - tlstate.redo() + state.redo() - expect(Object.keys(tlstate.page.shapes).length).toBe(prevCount + 1) + expect(Object.keys(state.page.shapes).length).toBe(prevCount + 1) }) it('pastes a shape to a new page', () => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) - tlstate.selectNone().copy(['rect1']).createPage().paste() + state.selectNone().copy(['rect1']).createPage().paste() - expect(Object.keys(tlstate.page.shapes).length).toBe(1) + expect(Object.keys(state.page.shapes).length).toBe(1) - tlstate.undo() + state.undo() - expect(Object.keys(tlstate.page.shapes).length).toBe(0) + expect(Object.keys(state.page.shapes).length).toBe(0) - tlstate.redo() + state.redo() - expect(Object.keys(tlstate.page.shapes).length).toBe(1) + expect(Object.keys(state.page.shapes).length).toBe(1) }) it('Copies grouped shapes.', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .group(['rect1', 'rect2'], 'groupA') .select('groupA') .copy() - const beforeShapes = tlstate.shapes + const beforeShapes = state.shapes - tlstate.paste() + state.paste() - expect(tlstate.shapes.filter((shape) => shape.type === TLDrawShapeType.Group).length).toBe(2) + expect(state.shapes.filter((shape) => shape.type === TLDrawShapeType.Group).length).toBe(2) - const afterShapes = tlstate.shapes + const afterShapes = state.shapes const newShapes = afterShapes.filter( (shape) => !beforeShapes.find(({ id }) => id === shape.id) @@ -83,9 +83,9 @@ describe('TLDrawState', () => { describe('When copying and pasting a shape with bindings', () => { it('copies two bound shapes and their binding', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() - tlstate + state .createShapes( { type: TLDrawShapeType.Rectangle, id: 'target1', point: [0, 0], size: [100, 100] }, { type: TLDrawShapeType.Arrow, id: 'arrow1', point: [200, 200] } @@ -95,23 +95,23 @@ describe('TLDrawState', () => { .updateSession([55, 55]) .completeSession() - expect(tlstate.bindings.length).toBe(1) + expect(state.bindings.length).toBe(1) - tlstate.selectAll().copy().paste() + state.selectAll().copy().paste() - const newArrow = tlstate.shapes.sort((a, b) => b.childIndex - a.childIndex)[0] as ArrowShape + const newArrow = state.shapes.sort((a, b) => b.childIndex - a.childIndex)[0] as ArrowShape expect(newArrow.handles.start.bindingId).not.toBe( - tlstate.getShape('arrow1').handles.start.bindingId + state.getShape('arrow1').handles.start.bindingId ) - expect(tlstate.bindings.length).toBe(2) + expect(state.bindings.length).toBe(2) }) it('removes bindings from copied shape handles', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() - tlstate + state .createShapes( { type: TLDrawShapeType.Rectangle, id: 'target1', point: [0, 0], size: [100, 100] }, { type: TLDrawShapeType.Arrow, id: 'arrow1', point: [200, 200] } @@ -121,13 +121,13 @@ describe('TLDrawState', () => { .updateSession([55, 55]) .completeSession() - expect(tlstate.bindings.length).toBe(1) + expect(state.bindings.length).toBe(1) - expect(tlstate.getShape('arrow1').handles.start.bindingId).toBeDefined() + expect(state.getShape('arrow1').handles.start.bindingId).toBeDefined() - tlstate.select('arrow1').copy().paste() + state.select('arrow1').copy().paste() - const newArrow = tlstate.shapes.sort((a, b) => b.childIndex - a.childIndex)[0] as ArrowShape + const newArrow = state.shapes.sort((a, b) => b.childIndex - a.childIndex)[0] as ArrowShape expect(newArrow.handles.start.bindingId).toBeUndefined() }) @@ -135,62 +135,62 @@ describe('TLDrawState', () => { describe('Selection', () => { it('selects a shape', () => { - tlstate.loadDocument(mockDocument).selectNone() + state.loadDocument(mockDocument).selectNone() tlu.clickShape('rect1') - expect(tlstate.selectedIds).toStrictEqual(['rect1']) - expect(tlstate.appState.status).toBe('idle') + expect(state.selectedIds).toStrictEqual(['rect1']) + expect(state.appState.status).toBe('idle') }) it('selects and deselects a shape', () => { - tlstate.loadDocument(mockDocument).selectNone() + state.loadDocument(mockDocument).selectNone() tlu.clickShape('rect1') tlu.clickCanvas() - expect(tlstate.selectedIds).toStrictEqual([]) - expect(tlstate.appState.status).toBe('idle') + expect(state.selectedIds).toStrictEqual([]) + expect(state.appState.status).toBe('idle') }) it('selects multiple shapes', () => { - tlstate.loadDocument(mockDocument).selectNone() + state.loadDocument(mockDocument).selectNone() tlu.clickShape('rect1') tlu.clickShape('rect2', { shiftKey: true }) - expect(tlstate.selectedIds).toStrictEqual(['rect1', 'rect2']) - expect(tlstate.appState.status).toBe('idle') + expect(state.selectedIds).toStrictEqual(['rect1', 'rect2']) + expect(state.appState.status).toBe('idle') }) it('shift-selects to deselect shapes', () => { - tlstate.loadDocument(mockDocument).selectNone() + state.loadDocument(mockDocument).selectNone() tlu.clickShape('rect1') tlu.clickShape('rect2', { shiftKey: true }) tlu.clickShape('rect2', { shiftKey: true }) - expect(tlstate.selectedIds).toStrictEqual(['rect1']) - expect(tlstate.appState.status).toBe('idle') + expect(state.selectedIds).toStrictEqual(['rect1']) + expect(state.appState.status).toBe('idle') }) it('clears selection when clicking bounds', () => { - tlstate.loadDocument(mockDocument).selectNone() - tlstate.startSession(SessionType.Brush, [-10, -10]) - tlstate.updateSession([110, 110]) - tlstate.completeSession() - expect(tlstate.selectedIds.length).toBe(3) + state.loadDocument(mockDocument).selectNone() + state.startSession(SessionType.Brush, [-10, -10]) + state.updateSession([110, 110]) + state.completeSession() + expect(state.selectedIds.length).toBe(3) }) it('selects selected shape when single-clicked', () => { - tlstate.loadDocument(mockDocument).selectAll() + state.loadDocument(mockDocument).selectAll() tlu.clickShape('rect2') - expect(tlstate.selectedIds).toStrictEqual(['rect2']) + expect(state.selectedIds).toStrictEqual(['rect2']) }) // it('selects shape when double-clicked', () => { - // tlstate.loadDocument(mockDocument).selectAll() + // state.loadDocument(mockDocument).selectAll() // tlu.doubleClickShape('rect2') - // expect(tlstate.selectedIds).toStrictEqual(['rect2']) + // expect(state.selectedIds).toStrictEqual(['rect2']) // }) it('does not select on meta-click', () => { - tlstate.loadDocument(mockDocument).selectNone() + state.loadDocument(mockDocument).selectNone() tlu.clickShape('rect1', { ctrlKey: true }) - expect(tlstate.selectedIds).toStrictEqual([]) - expect(tlstate.appState.status).toBe('idle') + expect(state.selectedIds).toStrictEqual([]) + expect(state.appState.status).toBe('idle') }) it.todo('deletes shapes if cancelled during creating') @@ -201,120 +201,120 @@ describe('TLDrawState', () => { describe('When selecting all', () => { it('selects all', () => { - const tlstate = new TLDrawState().loadDocument(mockDocument).selectAll() - expect(tlstate.selectedIds).toMatchSnapshot('selected all') + const state = new TLDrawState().loadDocument(mockDocument).selectAll() + expect(state.selectedIds).toMatchSnapshot('selected all') }) it('does not select children of a group', () => { - const tlstate = new TLDrawState().loadDocument(mockDocument).selectAll().group() - expect(tlstate.selectedIds.length).toBe(1) + const state = new TLDrawState().loadDocument(mockDocument).selectAll().group() + expect(state.selectedIds.length).toBe(1) }) }) // Single click on a selected shape to select just that shape it('single-selects shape in selection on click', () => { - tlstate.selectNone() + state.selectNone() tlu.clickShape('rect1') tlu.clickShape('rect2', { shiftKey: true }) tlu.clickShape('rect2') - expect(tlstate.selectedIds).toStrictEqual(['rect2']) - expect(tlstate.appState.status).toBe('idle') + expect(state.selectedIds).toStrictEqual(['rect2']) + expect(state.appState.status).toBe('idle') }) it('single-selects shape in selection on pointerup only', () => { - tlstate.selectNone() + state.selectNone() tlu.clickShape('rect1') tlu.clickShape('rect2', { shiftKey: true }) tlu.pointShape('rect2') - expect(tlstate.selectedIds).toStrictEqual(['rect1', 'rect2']) + expect(state.selectedIds).toStrictEqual(['rect1', 'rect2']) tlu.stopPointing('rect2') - expect(tlstate.selectedIds).toStrictEqual(['rect2']) - expect(tlstate.appState.status).toBe('idle') + expect(state.selectedIds).toStrictEqual(['rect2']) + expect(state.appState.status).toBe('idle') }) // it('selects shapes if shift key is lifted before pointerup', () => { - // tlstate.selectNone() + // state.selectNone() // tlu.clickShape('rect1') // tlu.pointShape('rect2', { shiftKey: true }) - // expect(tlstate.appState.status).toBe('pointingBounds') + // expect(state.appState.status).toBe('pointingBounds') // tlu.stopPointing('rect2') - // expect(tlstate.selectedIds).toStrictEqual(['rect2']) - // expect(tlstate.appState.status).toBe('idle') + // expect(state.selectedIds).toStrictEqual(['rect2']) + // expect(state.appState.status).toBe('idle') // }) }) describe('Select history', () => { it('selects, undoes and redoes', () => { - tlstate.reset().loadDocument(mockDocument) + state.reset().loadDocument(mockDocument) - expect(tlstate.selectHistory.pointer).toBe(0) - expect(tlstate.selectHistory.stack).toStrictEqual([[]]) - expect(tlstate.selectedIds).toStrictEqual([]) + expect(state.selectHistory.pointer).toBe(0) + expect(state.selectHistory.stack).toStrictEqual([[]]) + expect(state.selectedIds).toStrictEqual([]) tlu.pointShape('rect1') - expect(tlstate.selectHistory.pointer).toBe(1) - expect(tlstate.selectHistory.stack).toStrictEqual([[], ['rect1']]) - expect(tlstate.selectedIds).toStrictEqual(['rect1']) + expect(state.selectHistory.pointer).toBe(1) + expect(state.selectHistory.stack).toStrictEqual([[], ['rect1']]) + expect(state.selectedIds).toStrictEqual(['rect1']) tlu.stopPointing('rect1') - expect(tlstate.selectHistory.pointer).toBe(1) - expect(tlstate.selectHistory.stack).toStrictEqual([[], ['rect1']]) - expect(tlstate.selectedIds).toStrictEqual(['rect1']) + expect(state.selectHistory.pointer).toBe(1) + expect(state.selectHistory.stack).toStrictEqual([[], ['rect1']]) + expect(state.selectedIds).toStrictEqual(['rect1']) tlu.clickShape('rect2', { shiftKey: true }) - expect(tlstate.selectHistory.pointer).toBe(2) - expect(tlstate.selectHistory.stack).toStrictEqual([[], ['rect1'], ['rect1', 'rect2']]) - expect(tlstate.selectedIds).toStrictEqual(['rect1', 'rect2']) + expect(state.selectHistory.pointer).toBe(2) + expect(state.selectHistory.stack).toStrictEqual([[], ['rect1'], ['rect1', 'rect2']]) + expect(state.selectedIds).toStrictEqual(['rect1', 'rect2']) - tlstate.undoSelect() + state.undoSelect() - expect(tlstate.selectHistory.pointer).toBe(1) - expect(tlstate.selectHistory.stack).toStrictEqual([[], ['rect1'], ['rect1', 'rect2']]) - expect(tlstate.selectedIds).toStrictEqual(['rect1']) + expect(state.selectHistory.pointer).toBe(1) + expect(state.selectHistory.stack).toStrictEqual([[], ['rect1'], ['rect1', 'rect2']]) + expect(state.selectedIds).toStrictEqual(['rect1']) - tlstate.undoSelect() + state.undoSelect() - expect(tlstate.selectHistory.pointer).toBe(0) - expect(tlstate.selectHistory.stack).toStrictEqual([[], ['rect1'], ['rect1', 'rect2']]) - expect(tlstate.selectedIds).toStrictEqual([]) + expect(state.selectHistory.pointer).toBe(0) + expect(state.selectHistory.stack).toStrictEqual([[], ['rect1'], ['rect1', 'rect2']]) + expect(state.selectedIds).toStrictEqual([]) - tlstate.redoSelect() + state.redoSelect() - expect(tlstate.selectHistory.pointer).toBe(1) - expect(tlstate.selectHistory.stack).toStrictEqual([[], ['rect1'], ['rect1', 'rect2']]) - expect(tlstate.selectedIds).toStrictEqual(['rect1']) + expect(state.selectHistory.pointer).toBe(1) + expect(state.selectHistory.stack).toStrictEqual([[], ['rect1'], ['rect1', 'rect2']]) + expect(state.selectedIds).toStrictEqual(['rect1']) - tlstate.select('rect2') + state.select('rect2') - expect(tlstate.selectHistory.pointer).toBe(2) - expect(tlstate.selectHistory.stack).toStrictEqual([[], ['rect1'], ['rect2']]) - expect(tlstate.selectedIds).toStrictEqual(['rect2']) + expect(state.selectHistory.pointer).toBe(2) + expect(state.selectHistory.stack).toStrictEqual([[], ['rect1'], ['rect2']]) + expect(state.selectedIds).toStrictEqual(['rect2']) - tlstate.delete() + state.delete() - expect(tlstate.selectHistory.pointer).toBe(0) - expect(tlstate.selectHistory.stack).toStrictEqual([[]]) - expect(tlstate.selectedIds).toStrictEqual([]) + expect(state.selectHistory.pointer).toBe(0) + expect(state.selectHistory.stack).toStrictEqual([[]]) + expect(state.selectedIds).toStrictEqual([]) - tlstate.undoSelect() + state.undoSelect() - expect(tlstate.selectHistory.pointer).toBe(0) - expect(tlstate.selectHistory.stack).toStrictEqual([[]]) - expect(tlstate.selectedIds).toStrictEqual([]) + expect(state.selectHistory.pointer).toBe(0) + expect(state.selectHistory.stack).toStrictEqual([[]]) + expect(state.selectedIds).toStrictEqual([]) }) }) describe('Copies to JSON', () => { - tlstate.selectAll() - expect(tlstate.copyJson()).toMatchSnapshot('copied json') + state.selectAll() + expect(state.copyJson()).toMatchSnapshot('copied json') }) describe('Mutates bound shapes', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .createShapes( { id: 'rect', @@ -337,88 +337,80 @@ describe('TLDrawState', () => { .selectAll() .style({ color: ColorStyle.Red }) - expect(tlstate.getShape('arrow').style.color).toBe(ColorStyle.Red) - expect(tlstate.getShape('rect').style.color).toBe(ColorStyle.Red) + expect(state.getShape('arrow').style.color).toBe(ColorStyle.Red) + expect(state.getShape('rect').style.color).toBe(ColorStyle.Red) - tlstate.undo() + state.undo() - expect(tlstate.getShape('arrow').style.color).toBe(ColorStyle.Black) - expect(tlstate.getShape('rect').style.color).toBe(ColorStyle.Black) + expect(state.getShape('arrow').style.color).toBe(ColorStyle.Black) + expect(state.getShape('rect').style.color).toBe(ColorStyle.Black) - tlstate.redo() + state.redo() - expect(tlstate.getShape('arrow').style.color).toBe(ColorStyle.Red) - expect(tlstate.getShape('rect').style.color).toBe(ColorStyle.Red) + expect(state.getShape('arrow').style.color).toBe(ColorStyle.Red) + expect(state.getShape('rect').style.color).toBe(ColorStyle.Red) }) describe('when selecting shapes in a group', () => { it('selects the group when a grouped shape is clicked', () => { - const tlstate = new TLDrawState() - .loadDocument(mockDocument) - .group(['rect1', 'rect2'], 'groupA') + const state = new TLDrawState().loadDocument(mockDocument).group(['rect1', 'rect2'], 'groupA') - const tlu = new TLDrawStateUtils(tlstate) + const tlu = new TLDrawStateUtils(state) tlu.clickShape('rect1') - expect((tlstate.currentTool as SelectTool).selectedGroupId).toBeUndefined() - expect(tlstate.selectedIds).toStrictEqual(['groupA']) + expect((state.currentTool as SelectTool).selectedGroupId).toBeUndefined() + expect(state.selectedIds).toStrictEqual(['groupA']) }) it('selects the grouped shape when double clicked', () => { - const tlstate = new TLDrawState() - .loadDocument(mockDocument) - .group(['rect1', 'rect2'], 'groupA') + const state = new TLDrawState().loadDocument(mockDocument).group(['rect1', 'rect2'], 'groupA') - const tlu = new TLDrawStateUtils(tlstate) + const tlu = new TLDrawStateUtils(state) tlu.doubleClickShape('rect1') - expect((tlstate.currentTool as SelectTool).selectedGroupId).toStrictEqual('groupA') - expect(tlstate.selectedIds).toStrictEqual(['rect1']) + expect((state.currentTool as SelectTool).selectedGroupId).toStrictEqual('groupA') + expect(state.selectedIds).toStrictEqual(['rect1']) }) it('clears the selectedGroupId when selecting a different shape', () => { - const tlstate = new TLDrawState() - .loadDocument(mockDocument) - .group(['rect1', 'rect2'], 'groupA') + const state = new TLDrawState().loadDocument(mockDocument).group(['rect1', 'rect2'], 'groupA') - const tlu = new TLDrawStateUtils(tlstate) + const tlu = new TLDrawStateUtils(state) tlu.doubleClickShape('rect1') tlu.clickShape('rect3') - expect((tlstate.currentTool as SelectTool).selectedGroupId).toBeUndefined() - expect(tlstate.selectedIds).toStrictEqual(['rect3']) + expect((state.currentTool as SelectTool).selectedGroupId).toBeUndefined() + expect(state.selectedIds).toStrictEqual(['rect3']) }) it('selects a grouped shape when meta-shift-clicked', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .group(['rect1', 'rect2'], 'groupA') .selectNone() - const tlu = new TLDrawStateUtils(tlstate) + const tlu = new TLDrawStateUtils(state) tlu.clickShape('rect1', { ctrlKey: true, shiftKey: true }) - expect(tlstate.selectedIds).toStrictEqual(['rect1']) + expect(state.selectedIds).toStrictEqual(['rect1']) tlu.clickShape('rect1', { ctrlKey: true, shiftKey: true }) - expect(tlstate.selectedIds).toStrictEqual([]) + expect(state.selectedIds).toStrictEqual([]) }) it('selects a hovered shape from the selected group when meta-shift-clicked', () => { - const tlstate = new TLDrawState() - .loadDocument(mockDocument) - .group(['rect1', 'rect2'], 'groupA') + const state = new TLDrawState().loadDocument(mockDocument).group(['rect1', 'rect2'], 'groupA') - const tlu = new TLDrawStateUtils(tlstate) + const tlu = new TLDrawStateUtils(state) tlu.clickShape('rect1', { ctrlKey: true, shiftKey: true }) - expect(tlstate.selectedIds).toStrictEqual(['rect1']) + expect(state.selectedIds).toStrictEqual(['rect1']) tlu.clickShape('rect1', { ctrlKey: true, shiftKey: true }) - expect(tlstate.selectedIds).toStrictEqual([]) + expect(state.selectedIds).toStrictEqual([]) }) }) describe('when creating shapes', () => { it('Creates shapes with the correct child index', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .createShapes( { id: 'rect1', @@ -438,39 +430,39 @@ describe('TLDrawState', () => { ) .selectTool(TLDrawShapeType.Rectangle) - const tlu = new TLDrawStateUtils(tlstate) + const tlu = new TLDrawStateUtils(state) - const prevA = tlstate.shapes.map((shape) => shape.id) + const prevA = state.shapes.map((shape) => shape.id) tlu.pointCanvas({ x: 0, y: 0 }) tlu.movePointer({ x: 100, y: 100 }) tlu.stopPointing() - const newIdA = tlstate.shapes.map((shape) => shape.id).find((id) => !prevA.includes(id))! - const shapeA = tlstate.getShape(newIdA) + const newIdA = state.shapes.map((shape) => shape.id).find((id) => !prevA.includes(id))! + const shapeA = state.getShape(newIdA) expect(shapeA.childIndex).toBe(4) - tlstate.group(['rect2', 'rect3', newIdA], 'groupA') + state.group(['rect2', 'rect3', newIdA], 'groupA') - expect(tlstate.getShape('groupA').childIndex).toBe(2) + expect(state.getShape('groupA').childIndex).toBe(2) - tlstate.selectNone() - tlstate.selectTool(TLDrawShapeType.Rectangle) + state.selectNone() + state.selectTool(TLDrawShapeType.Rectangle) - const prevB = tlstate.shapes.map((shape) => shape.id) + const prevB = state.shapes.map((shape) => shape.id) tlu.pointCanvas({ x: 0, y: 0 }) tlu.movePointer({ x: 100, y: 100 }) tlu.stopPointing() - const newIdB = tlstate.shapes.map((shape) => shape.id).find((id) => !prevB.includes(id))! - const shapeB = tlstate.getShape(newIdB) + const newIdB = state.shapes.map((shape) => shape.id).find((id) => !prevB.includes(id))! + const shapeB = state.getShape(newIdB) expect(shapeB.childIndex).toBe(3) }) }) it('Exposes undo/redo stack', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .createShapes({ id: 'rect1', @@ -485,23 +477,23 @@ describe('TLDrawState', () => { size: [100, 200], }) - expect(tlstate.history.length).toBe(2) + expect(state.history.length).toBe(2) - expect(tlstate.history).toBeDefined() - expect(tlstate.history).toMatchSnapshot('history') + expect(state.history).toBeDefined() + expect(state.history).toMatchSnapshot('history') - tlstate.history = [] - expect(tlstate.history).toEqual([]) + state.history = [] + expect(state.history).toEqual([]) - const before = tlstate.state - tlstate.undo() - const after = tlstate.state + const before = state.state + state.undo() + const after = state.state expect(before).toBe(after) }) it('Exposes undo/redo stack up to the current pointer', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .createShapes({ id: 'rect1', @@ -517,11 +509,11 @@ describe('TLDrawState', () => { }) .undo() - expect(tlstate.history.length).toBe(1) + expect(state.history.length).toBe(1) }) it('Sets the undo/redo history', () => { - const tlstate = new TLDrawState('some_state_a') + const state = new TLDrawState('some_state_a') .createShapes({ id: 'rect1', type: TLDrawShapeType.Rectangle, @@ -536,29 +528,29 @@ describe('TLDrawState', () => { }) // Save the history and document from the first state - const doc = tlstate.document - const history = tlstate.history + const doc = state.document + const history = state.history // Create a new state - const tlstate2 = new TLDrawState('some_state_b') + const state2 = new TLDrawState('some_state_b') // Load the document and set the history - tlstate2.loadDocument(doc) - tlstate2.history = history + state2.loadDocument(doc) + state2.history = history - expect(tlstate2.shapes.length).toBe(2) + expect(state2.shapes.length).toBe(2) // We should be able to undo the change that was made on the first // state, now that we've brought in its undo / redo stack - tlstate2.undo() + state2.undo() - expect(tlstate2.shapes.length).toBe(1) + expect(state2.shapes.length).toBe(1) }) describe('When copying to SVG', () => { it('Copies shapes.', () => { - const tlstate = new TLDrawState() - const result = tlstate + const state = new TLDrawState() + const result = state .loadDocument(mockDocument) .select('rect1') .rotate(0.1) @@ -568,8 +560,8 @@ describe('TLDrawState', () => { }) it('Copies grouped shapes.', () => { - const tlstate = new TLDrawState() - const result = tlstate + const state = new TLDrawState() + const result = state .loadDocument(mockDocument) .select('rect1', 'rect2') .group() @@ -581,9 +573,9 @@ describe('TLDrawState', () => { it.todo('Copies Text shapes as elements.') // it('Copies Text shapes as elements.', () => { - // const tlstate2 = new TLDrawState() + // const state2 = new TLDrawState() - // const svgString = tlstate2 + // const svgString = state2 // .createShapes({ // id: 'text1', // type: TLDrawShapeType.Text, @@ -633,9 +625,9 @@ describe('TLDrawState', () => { TLDrawState.defaultState = withoutRoom - const tlstate = new TLDrawState('migrate_1') + const state = new TLDrawState('migrate_1') - tlstate.createShapes({ + state.createShapes({ id: 'rect1', type: TLDrawShapeType.Rectangle, }) @@ -645,11 +637,11 @@ describe('TLDrawState', () => { TLDrawState.version = 100 TLDrawState.defaultState.room = defaultState.room - const tlstate2 = new TLDrawState('migrate_1') + const state2 = new TLDrawState('migrate_1') setTimeout(() => { try { - expect(tlstate2.getShape('rect1')).toBeTruthy() + expect(state2.getShape('rect1')).toBeTruthy() done() } catch (e) { done(e) diff --git a/packages/tldraw/src/state/TLDrawState.ts b/packages/tldraw/src/state/TLDrawState.ts index 757e08fc3..02da22fc2 100644 --- a/packages/tldraw/src/state/TLDrawState.ts +++ b/packages/tldraw/src/state/TLDrawState.ts @@ -27,7 +27,7 @@ import { ShapeStyles, TLDrawShape, TLDrawShapeType, - Data, + TLDrawSnapshot, Session, TLDrawStatus, SelectHistory, @@ -58,35 +58,86 @@ import { USER_COLORS, FIT_TO_SCREEN_PADDING } from '~constants' const uuid = Utils.uniqueId() -export class TLDrawState extends StateManager { - private _onMount?: (tlstate: TLDrawState) => void - private _onChange?: (tlstate: TLDrawState, data: Data, reason: string) => void - private _onUserChange?: (tlstate: TLDrawState, user: TLDrawUser) => void +export interface TLDrawCallbacks { + /** + * (optional) A callback to run when the component mounts. + */ + onMount?: (state: TLDrawState) => void + /** + * (optional) A callback to run when the component's state changes. + */ + onChange?: (state: TLDrawState, reason?: string) => void + /** + * (optional) A callback to run when the user creates a new project through the menu or through a keyboard shortcut. + */ + onNewProject?: (state: TLDrawState, e?: KeyboardEvent) => void + /** + * (optional) A callback to run when the user saves a project through the menu or through a keyboard shortcut. + */ + onSaveProject?: (state: TLDrawState, e?: KeyboardEvent) => void + /** + * (optional) A callback to run when the user saves a project as a new project through the menu or through a keyboard shortcut. + */ + onSaveProjectAs?: (state: TLDrawState, e?: KeyboardEvent) => void + /** + * (optional) A callback to run when the user opens new project through the menu or through a keyboard shortcut. + */ + onOpenProject?: (state: TLDrawState, e?: KeyboardEvent) => void + /** + * (optional) A callback to run when the user signs in via the menu. + */ + onSignIn?: (state: TLDrawState) => void + /** + * (optional) A callback to run when the user signs out via the menu. + */ + onSignOut?: (state: TLDrawState) => void + /** + * (optional) A callback to run when the user creates a new project. + */ + onUserChange?: (state: TLDrawState, user: TLDrawUser) => void + /** + * (optional) A callback to run when the state is patched. + */ + onPatch?: (state: TLDrawState, reason?: string) => void + /** + * (optional) A callback to run when the state is changed with a command. + */ + onCommand?: (state: TLDrawState, reason?: string) => void + /** + * (optional) A callback to run when the state is persisted. + */ + onPersist?: (state: TLDrawState) => void + /** + * (optional) A callback to run when the user undos. + */ + onUndo?: (state: TLDrawState) => void + /** + * (optional) A callback to run when the user redos. + */ + onRedo?: (state: TLDrawState) => void +} - readOnly = false - - inputs?: Inputs +export class TLDrawState extends StateManager { + public callbacks: TLDrawCallbacks = {} selectHistory: SelectHistory = { stack: [[]], pointer: 0, } - clipboard?: { + private clipboard?: { shapes: TLDrawShape[] bindings: TLDrawBinding[] } - tools = createTools(this) + private tools = createTools(this) currentTool: BaseTool = this.tools.select - session?: Session - - isCreating = false + private isCreating = false // The editor's bounding client rect - bounds: TLBounds = { + private bounds: TLBounds = { minX: 0, minY: 0, maxX: 640, @@ -96,7 +147,7 @@ export class TLDrawState extends StateManager { } // The most recent pointer location - pointerPoint: number[] = [0, 0] + private pointerPoint: number[] = [0, 0] private pasteInfo = { center: [0, 0], @@ -104,15 +155,11 @@ export class TLDrawState extends StateManager { } fileSystemHandle: FileSystemHandle | null = null - + readOnly = false + session?: Session isDirty = false - constructor( - id?: string, - onMount?: (tlstate: TLDrawState) => void, - onChange?: (tlstate: TLDrawState, data: Data, reason: string) => void, - onUserChange?: (tlstate: TLDrawState, user: TLDrawUser) => void - ) { + constructor(id?: string, callbacks = {} as TLDrawCallbacks) { super(TLDrawState.defaultState, id, TLDrawState.version, (prev, next, prevVersion) => { return { ...next, @@ -123,23 +170,18 @@ export class TLDrawState extends StateManager { } }) + this.callbacks = callbacks + } + + /* -------------------- Internal -------------------- */ + + protected onReady = () => { this.loadDocument(this.document) - this.patchState({ document: migrate(this.document, TLDrawState.version) }) loadFileHandle().then((fileHandle) => { this.fileSystemHandle = fileHandle }) - this._onChange = onChange - this._onMount = onMount - this._onUserChange = onUserChange - - this.session = undefined - } - - /* -------------------- Internal -------------------- */ - - onReady = () => { try { this.patchState({ appState: { @@ -160,8 +202,7 @@ export class TLDrawState extends StateManager { }) } - this.persist() - this._onMount?.(this) + this.callbacks.onMount?.(this) } /** @@ -171,7 +212,7 @@ export class TLDrawState extends StateManager { * @protected * @returns The final state */ - protected cleanup = (state: Data, prev: Data): Data => { + protected cleanup = (state: TLDrawSnapshot, prev: TLDrawSnapshot): TLDrawSnapshot => { const data = { ...state } // Remove deleted shapes and bindings (in Commands, these will be set to undefined) @@ -361,29 +402,58 @@ export class TLDrawState extends StateManager { return data } + onPatch = (state: TLDrawSnapshot, id?: string) => { + this.callbacks.onPatch?.(this, id) + } + + onCommand = (state: TLDrawSnapshot, id?: string) => { + this.clearSelectHistory() + this.isDirty = true + this.callbacks.onCommand?.(this, id) + } + + onReplace = () => { + this.clearSelectHistory() + this.isDirty = false + } + + onUndo = () => { + Session.cache.selectedIds = [...this.selectedIds] + this.callbacks.onUndo?.(this) + } + + onRedo = () => { + Session.cache.selectedIds = [...this.selectedIds] + this.callbacks.onRedo?.(this) + } + + onPersist = () => { + this.callbacks.onPersist?.(this) + } + /** * Clear the selection history after each new command, undo or redo. * @param state * @param id */ - protected onStateDidChange = (state: Data, id: string): void => { - if (!id.startsWith('patch')) { - if (!id.startsWith('replace')) { - // If we've changed the undo stack, then the file is out of - // sync with any saved version on the file system. - this.isDirty = true - } - - this.clearSelectHistory() - } - - if (id.startsWith('undo') || id.startsWith('redo')) { - Session.cache.selectedIds = [...this.selectedIds] - } - - this._onChange?.(this, state, id) + protected onStateDidChange = (_state: TLDrawSnapshot, id?: string): void => { + this.callbacks.onChange?.(this, id) } + // if (id && !id.startsWith('patch')) { + // if (!id.startsWith('replace')) { + // // If we've changed the undo stack, then the file is out of + // // sync with any saved version on the file system. + // this.isDirty = true + // } + // this.clearSelectHistory() + // } + // if (id.startsWith('undo') || id.startsWith('redo')) { + // Session.cache.selectedIds = [...this.selectedIds] + // } + // this.onChange?.(this, id) + // } + /** * Set the current status. * @param status The new status to set. @@ -456,13 +526,16 @@ export class TLDrawState extends StateManager { /** * Set a setting. */ - setSetting = ( + setSetting = < + T extends keyof TLDrawSnapshot['settings'], + V extends TLDrawSnapshot['settings'][T] + >( name: T, value: V | ((value: V) => V) ): this => { if (this.session) return this - return this.patchState( + this.patchState( { settings: { [name]: typeof value === 'function' ? value(this.state.settings[name] as V) : value, @@ -470,6 +543,8 @@ export class TLDrawState extends StateManager { }, `settings:${name}` ) + this.persist() + return this } /** @@ -477,7 +552,7 @@ export class TLDrawState extends StateManager { */ toggleFocusMode = (): this => { if (this.session) return this - return this.patchState( + this.patchState( { settings: { isFocusMode: !this.state.settings.isFocusMode, @@ -485,6 +560,8 @@ export class TLDrawState extends StateManager { }, `settings:toggled_focus_mode` ) + this.persist() + return this } /** @@ -492,7 +569,7 @@ export class TLDrawState extends StateManager { */ togglePenMode = (): this => { if (this.session) return this - return this.patchState( + this.patchState( { settings: { isPenMode: !this.state.settings.isPenMode, @@ -500,6 +577,8 @@ export class TLDrawState extends StateManager { }, `settings:toggled_pen_mode` ) + this.persist() + return this } /** @@ -816,8 +895,7 @@ export class TLDrawState extends StateManager { this.resetHistory() this.clearSelectHistory() this.session = undefined - - return this.replaceState( + this.replaceState( { ...TLDrawState.defaultState, document: migrate(document, TLDrawState.version), @@ -828,6 +906,7 @@ export class TLDrawState extends StateManager { }, 'loaded_document' ) + return this } // Should we move this to the app layer? onSave, onSaveAs, etc? @@ -912,7 +991,7 @@ export class TLDrawState extends StateManager { /** * Get the current app state. */ - getAppState = (): Data['appState'] => { + getAppState = (): TLDrawSnapshot['appState'] => { return this.appState } @@ -958,10 +1037,6 @@ export class TLDrawState extends StateManager { return TLDR.getBounds(this.getShape(id, pageId)) } - greet() { - return 'hello' - } - /** * Get a binding from a given page. * @param id The binding's id. @@ -1013,7 +1088,7 @@ export class TLDrawState extends StateManager { /** * The current app state. */ - get appState(): Data['appState'] { + get appState(): TLDrawSnapshot['appState'] { return this.state.appState } @@ -1598,7 +1673,7 @@ export class TLDrawState extends StateManager { * @param delta The zoom delta. * @param center The point to zoom toward. */ - zoom = Utils.throttle((delta: number, center?: number[]): this => { + zoomBy = Utils.throttle((delta: number, center?: number[]): this => { const { zoom } = this.pageState.camera const nextZoom = TLDR.getCameraZoom(zoom - delta * zoom) return this.zoomTo(nextZoom, center) @@ -1639,7 +1714,8 @@ export class TLDrawState extends StateManager { if (this.state.room) { const { users, userId } = this.state.room - this._onUserChange?.(this, { + + this.callbacks.onUserChange?.(this, { ...users[userId], selectedIds: nextIds, }) @@ -2034,7 +2110,7 @@ export class TLDrawState extends StateManager { /** * Delete all shapes on the page. */ - clear = (): this => { + deleteAll = (): this => { this.selectAll() this.delete() return this @@ -2333,7 +2409,7 @@ export class TLDrawState extends StateManager { onZoom: TLWheelEventHandler = (info, e) => { if (this.state.appState.status !== TLDrawStatus.Idle) return - this.zoom(info.delta[2] / 100, info.delta) + this.zoomBy(info.delta[2] / 100, info.delta) this.onPointerMove(info, e as unknown as React.PointerEvent) } @@ -2349,7 +2425,7 @@ export class TLDrawState extends StateManager { if (this.state.room) { const { users, userId } = this.state.room - this._onUserChange?.(this, { + this.callbacks.onUserChange?.(this, { ...users[userId], point: this.getPagePoint(info.point), }) @@ -2565,7 +2641,7 @@ export class TLDrawState extends StateManager { }, } - static defaultState: Data = { + static defaultState: TLDrawSnapshot = { settings: { isPenMode: false, isDarkMode: false, diff --git a/packages/tldraw/src/state/commands/alignShapes/alignShapes.spec.ts b/packages/tldraw/src/state/commands/alignShapes/alignShapes.spec.ts index ff5a6c2c1..e094eb10e 100644 --- a/packages/tldraw/src/state/commands/alignShapes/alignShapes.spec.ts +++ b/packages/tldraw/src/state/commands/alignShapes/alignShapes.spec.ts @@ -4,14 +4,14 @@ import { mockDocument, TLDrawStateUtils } from '~test' import { AlignType, TLDrawShapeType } from '~types' describe('Align command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() describe('when less than two shapes are selected', () => { it('does nothing', () => { - tlstate.loadDocument(mockDocument).select('rect2') - const initialState = tlstate.state - tlstate.align(AlignType.Top) - const currentState = tlstate.state + state.loadDocument(mockDocument).select('rect2') + const initialState = state.state + state.align(AlignType.Top) + const currentState = state.state expect(currentState).toEqual(initialState) }) @@ -19,67 +19,67 @@ describe('Align command', () => { describe('when multiple shapes are selected', () => { beforeEach(() => { - tlstate.loadDocument(mockDocument) - tlstate.selectAll() + state.loadDocument(mockDocument) + state.selectAll() }) it('does, undoes and redoes command', () => { - tlstate.align(AlignType.Top) + state.align(AlignType.Top) - expect(tlstate.getShape('rect2').point).toEqual([100, 0]) + expect(state.getShape('rect2').point).toEqual([100, 0]) - tlstate.undo() + state.undo() - expect(tlstate.getShape('rect2').point).toEqual([100, 100]) + expect(state.getShape('rect2').point).toEqual([100, 100]) - tlstate.redo() + state.redo() - expect(tlstate.getShape('rect2').point).toEqual([100, 0]) + expect(state.getShape('rect2').point).toEqual([100, 0]) }) it('aligns top', () => { - tlstate.align(AlignType.Top) + state.align(AlignType.Top) - expect(tlstate.getShape('rect2').point).toEqual([100, 0]) + expect(state.getShape('rect2').point).toEqual([100, 0]) }) it('aligns right', () => { - tlstate.align(AlignType.Right) + state.align(AlignType.Right) - expect(tlstate.getShape('rect1').point).toEqual([100, 0]) + expect(state.getShape('rect1').point).toEqual([100, 0]) }) it('aligns bottom', () => { - tlstate.align(AlignType.Bottom) + state.align(AlignType.Bottom) - expect(tlstate.getShape('rect1').point).toEqual([0, 100]) + expect(state.getShape('rect1').point).toEqual([0, 100]) }) it('aligns left', () => { - tlstate.align(AlignType.Left) + state.align(AlignType.Left) - expect(tlstate.getShape('rect2').point).toEqual([0, 100]) + expect(state.getShape('rect2').point).toEqual([0, 100]) }) it('aligns center horizontal', () => { - tlstate.align(AlignType.CenterHorizontal) + state.align(AlignType.CenterHorizontal) - expect(tlstate.getShape('rect1').point).toEqual([50, 0]) - expect(tlstate.getShape('rect2').point).toEqual([50, 100]) + expect(state.getShape('rect1').point).toEqual([50, 0]) + expect(state.getShape('rect2').point).toEqual([50, 100]) }) it('aligns center vertical', () => { - tlstate.align(AlignType.CenterVertical) + state.align(AlignType.CenterVertical) - expect(tlstate.getShape('rect1').point).toEqual([0, 50]) - expect(tlstate.getShape('rect2').point).toEqual([100, 50]) + expect(state.getShape('rect1').point).toEqual([0, 50]) + expect(state.getShape('rect2').point).toEqual([100, 50]) }) }) }) describe('when aligning groups', () => { it('aligns children', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .createShapes( { id: 'rect1', type: TLDrawShapeType.Rectangle, point: [0, 0], size: [100, 100] }, { id: 'rect2', type: TLDrawShapeType.Rectangle, point: [100, 100], size: [100, 100] }, @@ -90,12 +90,12 @@ describe('when aligning groups', () => { .select('rect3', 'rect4') .align(AlignType.CenterVertical) - const p0 = tlstate.getShape('rect4').point - const p1 = tlstate.getShape('rect3').point + const p0 = state.getShape('rect4').point + const p1 = state.getShape('rect3').point - tlstate.undo().delete(['rect4']).selectAll().align(AlignType.CenterVertical) + state.undo().delete(['rect4']).selectAll().align(AlignType.CenterVertical) - new TLDrawStateUtils(tlstate).expectShapesToBeAtPoints({ + new TLDrawStateUtils(state).expectShapesToBeAtPoints({ rect1: p0, rect2: Vec.add(p0, [100, 100]), rect3: p1, diff --git a/packages/tldraw/src/state/commands/alignShapes/alignShapes.ts b/packages/tldraw/src/state/commands/alignShapes/alignShapes.ts index cc8e5143c..e28966c27 100644 --- a/packages/tldraw/src/state/commands/alignShapes/alignShapes.ts +++ b/packages/tldraw/src/state/commands/alignShapes/alignShapes.ts @@ -1,11 +1,11 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { Utils } from '@tldraw/core' import { AlignType, TLDrawCommand, TLDrawShapeType } from '~types' -import type { Data } from '~types' +import type { TLDrawSnapshot } from '~types' import { TLDR } from '~state/TLDR' import Vec from '@tldraw/vec' -export function alignShapes(data: Data, ids: string[], type: AlignType): TLDrawCommand { +export function alignShapes(data: TLDrawSnapshot, ids: string[], type: AlignType): TLDrawCommand { const { currentPageId } = data.appState const initialShapes = ids.map((id) => TLDR.getShape(data, id, currentPageId)) diff --git a/packages/tldraw/src/state/commands/changePage/changePage.spec.ts b/packages/tldraw/src/state/commands/changePage/changePage.spec.ts index 6f1379179..4ed72dfd6 100644 --- a/packages/tldraw/src/state/commands/changePage/changePage.spec.ts +++ b/packages/tldraw/src/state/commands/changePage/changePage.spec.ts @@ -2,31 +2,31 @@ import { TLDrawState } from '~state' import { mockDocument } from '~test' describe('Change page command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() it('does, undoes and redoes command', () => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) - const initialId = tlstate.page.id + const initialId = state.page.id - tlstate.createPage() + state.createPage() - const nextId = tlstate.page.id + const nextId = state.page.id - tlstate.changePage(initialId) + state.changePage(initialId) - expect(tlstate.page.id).toBe(initialId) + expect(state.page.id).toBe(initialId) - tlstate.changePage(nextId) + state.changePage(nextId) - expect(tlstate.page.id).toBe(nextId) + expect(state.page.id).toBe(nextId) - tlstate.undo() + state.undo() - expect(tlstate.page.id).toBe(initialId) + expect(state.page.id).toBe(initialId) - tlstate.redo() + state.redo() - expect(tlstate.page.id).toBe(nextId) + expect(state.page.id).toBe(nextId) }) }) diff --git a/packages/tldraw/src/state/commands/changePage/changePage.ts b/packages/tldraw/src/state/commands/changePage/changePage.ts index a625b3000..d6c5e4c07 100644 --- a/packages/tldraw/src/state/commands/changePage/changePage.ts +++ b/packages/tldraw/src/state/commands/changePage/changePage.ts @@ -1,6 +1,6 @@ -import type { Data, TLDrawCommand } from '~types' +import type { TLDrawSnapshot, TLDrawCommand } from '~types' -export function changePage(data: Data, pageId: string): TLDrawCommand { +export function changePage(data: TLDrawSnapshot, pageId: string): TLDrawCommand { return { id: 'change_page', before: { diff --git a/packages/tldraw/src/state/commands/createPage/createPage.spec.ts b/packages/tldraw/src/state/commands/createPage/createPage.spec.ts index e25ff1a36..bfea4ec5c 100644 --- a/packages/tldraw/src/state/commands/createPage/createPage.spec.ts +++ b/packages/tldraw/src/state/commands/createPage/createPage.spec.ts @@ -2,33 +2,33 @@ import { TLDrawState } from '~state' import { mockDocument } from '~test' describe('Create page command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() it('does, undoes and redoes command', () => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) - const initialId = tlstate.page.id - const initialPageState = tlstate.pageState + const initialId = state.page.id + const initialPageState = state.pageState - tlstate.createPage() + state.createPage() - const nextId = tlstate.page.id - const nextPageState = tlstate.pageState + const nextId = state.page.id + const nextPageState = state.pageState - expect(Object.keys(tlstate.document.pages).length).toBe(2) - expect(tlstate.page.id).toBe(nextId) - expect(tlstate.pageState).toEqual(nextPageState) + expect(Object.keys(state.document.pages).length).toBe(2) + expect(state.page.id).toBe(nextId) + expect(state.pageState).toEqual(nextPageState) - tlstate.undo() + state.undo() - expect(Object.keys(tlstate.document.pages).length).toBe(1) - expect(tlstate.page.id).toBe(initialId) - expect(tlstate.pageState).toEqual(initialPageState) + expect(Object.keys(state.document.pages).length).toBe(1) + expect(state.page.id).toBe(initialId) + expect(state.pageState).toEqual(initialPageState) - tlstate.redo() + state.redo() - expect(Object.keys(tlstate.document.pages).length).toBe(2) - expect(tlstate.page.id).toBe(nextId) - expect(tlstate.pageState).toEqual(nextPageState) + expect(Object.keys(state.document.pages).length).toBe(2) + expect(state.page.id).toBe(nextId) + expect(state.pageState).toEqual(nextPageState) }) }) diff --git a/packages/tldraw/src/state/commands/createPage/createPage.ts b/packages/tldraw/src/state/commands/createPage/createPage.ts index 8fb792ce9..3e4834d16 100644 --- a/packages/tldraw/src/state/commands/createPage/createPage.ts +++ b/packages/tldraw/src/state/commands/createPage/createPage.ts @@ -1,7 +1,11 @@ -import type { Data, TLDrawCommand, TLDrawPage } from '~types' +import type { TLDrawSnapshot, TLDrawCommand, TLDrawPage } from '~types' import { Utils, TLPageState } from '@tldraw/core' -export function createPage(data: Data, center: number[], pageId = Utils.uniqueId()): TLDrawCommand { +export function createPage( + data: TLDrawSnapshot, + center: number[], + pageId = Utils.uniqueId() +): TLDrawCommand { const { currentPageId } = data.appState const topPage = Object.values(data.document.pages).sort( diff --git a/packages/tldraw/src/state/commands/createShapes/createShapes.spec.ts b/packages/tldraw/src/state/commands/createShapes/createShapes.spec.ts index 9a9b532db..5a466fc1a 100644 --- a/packages/tldraw/src/state/commands/createShapes/createShapes.spec.ts +++ b/packages/tldraw/src/state/commands/createShapes/createShapes.spec.ts @@ -2,36 +2,36 @@ import { TLDrawState } from '~state' import { mockDocument } from '~test' describe('Create command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() beforeEach(() => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) }) describe('when no shape is provided', () => { it('does nothing', () => { - const initialState = tlstate.state - tlstate.create() + const initialState = state.state + state.create() - const currentState = tlstate.state + const currentState = state.state expect(currentState).toEqual(initialState) }) }) it('does, undoes and redoes command', () => { - const shape = { ...tlstate.getShape('rect1'), id: 'rect4' } - tlstate.create([shape]) + const shape = { ...state.getShape('rect1'), id: 'rect4' } + state.create([shape]) - expect(tlstate.getShape('rect4')).toBeTruthy() + expect(state.getShape('rect4')).toBeTruthy() - tlstate.undo() + state.undo() - expect(tlstate.getShape('rect4')).toBe(undefined) + expect(state.getShape('rect4')).toBe(undefined) - tlstate.redo() + state.redo() - expect(tlstate.getShape('rect4')).toBeTruthy() + expect(state.getShape('rect4')).toBeTruthy() }) it.todo('Creates bindings') diff --git a/packages/tldraw/src/state/commands/createShapes/createShapes.ts b/packages/tldraw/src/state/commands/createShapes/createShapes.ts index 67e35f544..2557452d6 100644 --- a/packages/tldraw/src/state/commands/createShapes/createShapes.ts +++ b/packages/tldraw/src/state/commands/createShapes/createShapes.ts @@ -1,9 +1,9 @@ import type { Patch } from 'rko' import { TLDR } from '~state/TLDR' -import type { TLDrawShape, Data, TLDrawCommand, TLDrawBinding } from '~types' +import type { TLDrawShape, TLDrawSnapshot, TLDrawCommand, TLDrawBinding } from '~types' export function createShapes( - data: Data, + data: TLDrawSnapshot, shapes: TLDrawShape[], bindings: TLDrawBinding[] = [] ): TLDrawCommand { diff --git a/packages/tldraw/src/state/commands/deletePage/deletePage.spec.ts b/packages/tldraw/src/state/commands/deletePage/deletePage.spec.ts index 1d358bccb..7308c1e2d 100644 --- a/packages/tldraw/src/state/commands/deletePage/deletePage.spec.ts +++ b/packages/tldraw/src/state/commands/deletePage/deletePage.spec.ts @@ -2,40 +2,40 @@ import { TLDrawState } from '~state' import { mockDocument } from '~test' describe('Delete page', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() beforeEach(() => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) }) describe('when there are no pages in the current document', () => { it('does nothing', () => { - tlstate.resetDocument() - const initialState = tlstate.state - tlstate.deletePage('page1') - const currentState = tlstate.state + state.resetDocument() + const initialState = state.state + state.deletePage('page1') + const currentState = state.state expect(currentState).toEqual(initialState) }) }) it('does, undoes and redoes command', () => { - const initialId = tlstate.currentPageId + const initialId = state.currentPageId - tlstate.createPage() + state.createPage() - const nextId = tlstate.currentPageId + const nextId = state.currentPageId - tlstate.deletePage() + state.deletePage() - expect(tlstate.currentPageId).toBe(initialId) + expect(state.currentPageId).toBe(initialId) - tlstate.undo() + state.undo() - expect(tlstate.currentPageId).toBe(nextId) + expect(state.currentPageId).toBe(nextId) - tlstate.redo() + state.redo() - expect(tlstate.currentPageId).toBe(initialId) + expect(state.currentPageId).toBe(initialId) }) }) diff --git a/packages/tldraw/src/state/commands/deletePage/deletePage.ts b/packages/tldraw/src/state/commands/deletePage/deletePage.ts index b54fd7d8b..4f71bdf26 100644 --- a/packages/tldraw/src/state/commands/deletePage/deletePage.ts +++ b/packages/tldraw/src/state/commands/deletePage/deletePage.ts @@ -1,6 +1,6 @@ -import type { Data, TLDrawCommand } from '~types' +import type { TLDrawSnapshot, TLDrawCommand } from '~types' -export function deletePage(data: Data, pageId: string): TLDrawCommand { +export function deletePage(data: TLDrawSnapshot, pageId: string): TLDrawCommand { const { currentPageId } = data.appState const pagesArr = Object.values(data.document.pages).sort( diff --git a/packages/tldraw/src/state/commands/deleteShapes/deleteShapes.spec.ts b/packages/tldraw/src/state/commands/deleteShapes/deleteShapes.spec.ts index 70c647fb1..1ec71c8bd 100644 --- a/packages/tldraw/src/state/commands/deleteShapes/deleteShapes.spec.ts +++ b/packages/tldraw/src/state/commands/deleteShapes/deleteShapes.spec.ts @@ -3,56 +3,56 @@ import { mockDocument } from '~test' import { SessionType, TLDrawShapeType } from '~types' describe('Delete command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() beforeEach(() => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) }) describe('when no shape is selected', () => { it('does nothing', () => { - const initialState = tlstate.state - tlstate.delete() - const currentState = tlstate.state + const initialState = state.state + state.delete() + const currentState = state.state expect(currentState).toEqual(initialState) }) }) it('does, undoes and redoes command', () => { - tlstate.select('rect2') - tlstate.delete() + state.select('rect2') + state.delete() - expect(tlstate.getShape('rect2')).toBe(undefined) - expect(tlstate.getPageState().selectedIds.length).toBe(0) + expect(state.getShape('rect2')).toBe(undefined) + expect(state.getPageState().selectedIds.length).toBe(0) - tlstate.undo() + state.undo() - expect(tlstate.getShape('rect2')).toBeTruthy() - expect(tlstate.getPageState().selectedIds.length).toBe(1) + expect(state.getShape('rect2')).toBeTruthy() + expect(state.getPageState().selectedIds.length).toBe(1) - tlstate.redo() + state.redo() - expect(tlstate.getShape('rect2')).toBe(undefined) - expect(tlstate.getPageState().selectedIds.length).toBe(0) + expect(state.getShape('rect2')).toBe(undefined) + expect(state.getPageState().selectedIds.length).toBe(0) }) it('deletes two shapes', () => { - tlstate.selectAll() - tlstate.delete() + state.selectAll() + state.delete() - expect(tlstate.getShape('rect1')).toBe(undefined) - expect(tlstate.getShape('rect2')).toBe(undefined) + expect(state.getShape('rect1')).toBe(undefined) + expect(state.getShape('rect2')).toBe(undefined) - tlstate.undo() + state.undo() - expect(tlstate.getShape('rect1')).toBeTruthy() - expect(tlstate.getShape('rect2')).toBeTruthy() + expect(state.getShape('rect1')).toBeTruthy() + expect(state.getShape('rect2')).toBeTruthy() - tlstate.redo() + state.redo() - expect(tlstate.getShape('rect1')).toBe(undefined) - expect(tlstate.getShape('rect2')).toBe(undefined) + expect(state.getShape('rect1')).toBe(undefined) + expect(state.getShape('rect2')).toBe(undefined) }) it('deletes bound shapes, undoes and redoes', () => { @@ -70,9 +70,9 @@ describe('Delete command', () => { }) it('deletes bound shapes', () => { - expect(Object.values(tlstate.page.bindings)[0]).toBe(undefined) + expect(Object.values(state.page.bindings)[0]).toBe(undefined) - tlstate + state .selectNone() .createShapes({ id: 'arrow1', @@ -83,46 +83,46 @@ describe('Delete command', () => { .updateSession([110, 110]) .completeSession() - const binding = Object.values(tlstate.page.bindings)[0] + const binding = Object.values(state.page.bindings)[0] expect(binding).toBeTruthy() expect(binding.fromId).toBe('arrow1') expect(binding.toId).toBe('rect3') expect(binding.handleId).toBe('start') - expect(tlstate.getShape('arrow1').handles?.start.bindingId).toBe(binding.id) + expect(state.getShape('arrow1').handles?.start.bindingId).toBe(binding.id) - tlstate.select('rect3').delete() + state.select('rect3').delete() - expect(Object.values(tlstate.page.bindings)[0]).toBe(undefined) - expect(tlstate.getShape('arrow1').handles?.start.bindingId).toBe(undefined) + expect(Object.values(state.page.bindings)[0]).toBe(undefined) + expect(state.getShape('arrow1').handles?.start.bindingId).toBe(undefined) - tlstate.undo() + state.undo() - expect(Object.values(tlstate.page.bindings)[0]).toBeTruthy() - expect(tlstate.getShape('arrow1').handles?.start.bindingId).toBe(binding.id) + expect(Object.values(state.page.bindings)[0]).toBeTruthy() + expect(state.getShape('arrow1').handles?.start.bindingId).toBe(binding.id) - tlstate.redo() + state.redo() - expect(Object.values(tlstate.page.bindings)[0]).toBe(undefined) - expect(tlstate.getShape('arrow1').handles?.start.bindingId).toBe(undefined) + expect(Object.values(state.page.bindings)[0]).toBe(undefined) + expect(state.getShape('arrow1').handles?.start.bindingId).toBe(undefined) }) describe('when deleting shapes in a group', () => { it('updates the group', () => { - tlstate.group(['rect1', 'rect2', 'rect3'], 'newGroup').select('rect1').delete() + state.group(['rect1', 'rect2', 'rect3'], 'newGroup').select('rect1').delete() - expect(tlstate.getShape('rect1')).toBeUndefined() - expect(tlstate.getShape('newGroup').children).toStrictEqual(['rect2', 'rect3']) + expect(state.getShape('rect1')).toBeUndefined() + expect(state.getShape('newGroup').children).toStrictEqual(['rect2', 'rect3']) }) }) describe('when deleting a group', () => { it('deletes all grouped shapes', () => { - tlstate.group(['rect1', 'rect2'], 'newGroup').select('newGroup').delete() + state.group(['rect1', 'rect2'], 'newGroup').select('newGroup').delete() - expect(tlstate.getShape('rect1')).toBeUndefined() - expect(tlstate.getShape('rect2')).toBeUndefined() - expect(tlstate.getShape('newGroup')).toBeUndefined() + expect(state.getShape('rect1')).toBeUndefined() + expect(state.getShape('rect2')).toBeUndefined() + expect(state.getShape('newGroup')).toBeUndefined() }) }) diff --git a/packages/tldraw/src/state/commands/deleteShapes/deleteShapes.ts b/packages/tldraw/src/state/commands/deleteShapes/deleteShapes.ts index 156f4969a..0cd0c0c41 100644 --- a/packages/tldraw/src/state/commands/deleteShapes/deleteShapes.ts +++ b/packages/tldraw/src/state/commands/deleteShapes/deleteShapes.ts @@ -1,9 +1,9 @@ import { TLDR } from '~state/TLDR' -import type { Data, TLDrawCommand } from '~types' +import type { TLDrawSnapshot, TLDrawCommand } from '~types' import { removeShapesFromPage } from '../shared/removeShapesFromPage' export function deleteShapes( - data: Data, + data: TLDrawSnapshot, ids: string[], pageId = data.appState.currentPageId ): TLDrawCommand { diff --git a/packages/tldraw/src/state/commands/distributeShapes/distributeShapes.spec.ts b/packages/tldraw/src/state/commands/distributeShapes/distributeShapes.spec.ts index 64747f8ca..76094b848 100644 --- a/packages/tldraw/src/state/commands/distributeShapes/distributeShapes.spec.ts +++ b/packages/tldraw/src/state/commands/distributeShapes/distributeShapes.spec.ts @@ -4,45 +4,45 @@ import { mockDocument, TLDrawStateUtils } from '~test' import { AlignType, DistributeType, TLDrawShapeType } from '~types' describe('Distribute command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() beforeEach(() => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) }) describe('when less than three shapes are selected', () => { it('does nothing', () => { - tlstate.select('rect1', 'rect2') - const initialState = tlstate.state - tlstate.distribute(DistributeType.Horizontal) - const currentState = tlstate.state + state.select('rect1', 'rect2') + const initialState = state.state + state.distribute(DistributeType.Horizontal) + const currentState = state.state expect(currentState).toEqual(initialState) }) }) it('does, undoes and redoes command', () => { - tlstate.selectAll() - tlstate.distribute(DistributeType.Horizontal) + state.selectAll() + state.distribute(DistributeType.Horizontal) - expect(tlstate.getShape('rect3').point).toEqual([50, 20]) - tlstate.undo() - expect(tlstate.getShape('rect3').point).toEqual([20, 20]) - tlstate.redo() - expect(tlstate.getShape('rect3').point).toEqual([50, 20]) + expect(state.getShape('rect3').point).toEqual([50, 20]) + state.undo() + expect(state.getShape('rect3').point).toEqual([20, 20]) + state.redo() + expect(state.getShape('rect3').point).toEqual([50, 20]) }) it('distributes vertically', () => { - tlstate.selectAll() - tlstate.distribute(DistributeType.Vertical) + state.selectAll() + state.distribute(DistributeType.Vertical) - expect(tlstate.getShape('rect3').point).toEqual([20, 50]) + expect(state.getShape('rect3').point).toEqual([20, 50]) }) }) describe('when distributing groups', () => { it('distributes children', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .createShapes( { id: 'rect1', type: TLDrawShapeType.Rectangle, point: [0, 0], size: [100, 100] }, { id: 'rect2', type: TLDrawShapeType.Rectangle, point: [100, 100], size: [100, 100] }, @@ -54,12 +54,12 @@ describe('when distributing groups', () => { .select('rect3', 'rect4', 'rect5') .distribute(DistributeType.Vertical) - const p0 = tlstate.getShape('rect4').point - const p1 = tlstate.getShape('rect3').point + const p0 = state.getShape('rect4').point + const p1 = state.getShape('rect3').point - tlstate.undo().delete(['rect4']).selectAll().distribute(DistributeType.Vertical) + state.undo().delete(['rect4']).selectAll().distribute(DistributeType.Vertical) - new TLDrawStateUtils(tlstate).expectShapesToBeAtPoints({ + new TLDrawStateUtils(state).expectShapesToBeAtPoints({ rect1: p0, rect2: Vec.add(p0, [100, 100]), rect3: p1, diff --git a/packages/tldraw/src/state/commands/distributeShapes/distributeShapes.ts b/packages/tldraw/src/state/commands/distributeShapes/distributeShapes.ts index 8b554384d..f5e66cfd4 100644 --- a/packages/tldraw/src/state/commands/distributeShapes/distributeShapes.ts +++ b/packages/tldraw/src/state/commands/distributeShapes/distributeShapes.ts @@ -1,10 +1,14 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { Utils } from '@tldraw/core' -import { DistributeType, TLDrawShape, Data, TLDrawCommand, TLDrawShapeType } from '~types' +import { DistributeType, TLDrawShape, TLDrawSnapshot, TLDrawCommand, TLDrawShapeType } from '~types' import { TLDR } from '~state/TLDR' import Vec from '@tldraw/vec' -export function distributeShapes(data: Data, ids: string[], type: DistributeType): TLDrawCommand { +export function distributeShapes( + data: TLDrawSnapshot, + ids: string[], + type: DistributeType +): TLDrawCommand { const { currentPageId } = data.appState const initialShapes = ids.map((id) => TLDR.getShape(data, id, currentPageId)) diff --git a/packages/tldraw/src/state/commands/duplicatePage/duplicatePage.spec.ts b/packages/tldraw/src/state/commands/duplicatePage/duplicatePage.spec.ts index 8b215dbd2..a66091057 100644 --- a/packages/tldraw/src/state/commands/duplicatePage/duplicatePage.spec.ts +++ b/packages/tldraw/src/state/commands/duplicatePage/duplicatePage.spec.ts @@ -2,23 +2,23 @@ import { TLDrawState } from '~state' import { mockDocument } from '~test' describe('Duplicate page command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() it('does, undoes and redoes command', () => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) - const initialId = tlstate.page.id + const initialId = state.page.id - tlstate.duplicatePage(tlstate.currentPageId) + state.duplicatePage(state.currentPageId) - const nextId = tlstate.page.id + const nextId = state.page.id - tlstate.undo() + state.undo() - expect(tlstate.page.id).toBe(initialId) + expect(state.page.id).toBe(initialId) - tlstate.redo() + state.redo() - expect(tlstate.page.id).toBe(nextId) + expect(state.page.id).toBe(nextId) }) }) diff --git a/packages/tldraw/src/state/commands/duplicatePage/duplicatePage.ts b/packages/tldraw/src/state/commands/duplicatePage/duplicatePage.ts index 1270b0a1d..b0bdc7952 100644 --- a/packages/tldraw/src/state/commands/duplicatePage/duplicatePage.ts +++ b/packages/tldraw/src/state/commands/duplicatePage/duplicatePage.ts @@ -1,7 +1,11 @@ -import type { Data, TLDrawCommand } from '~types' +import type { TLDrawSnapshot, TLDrawCommand } from '~types' import { Utils } from '@tldraw/core' -export function duplicatePage(data: Data, center: number[], pageId: string): TLDrawCommand { +export function duplicatePage( + data: TLDrawSnapshot, + center: number[], + pageId: string +): TLDrawCommand { const newId = Utils.uniqueId() const { currentPageId } = data.appState diff --git a/packages/tldraw/src/state/commands/duplicateShapes/duplicateShapes.spec.ts b/packages/tldraw/src/state/commands/duplicateShapes/duplicateShapes.spec.ts index 2e89dd472..84584dbda 100644 --- a/packages/tldraw/src/state/commands/duplicateShapes/duplicateShapes.spec.ts +++ b/packages/tldraw/src/state/commands/duplicateShapes/duplicateShapes.spec.ts @@ -6,38 +6,38 @@ import { mockDocument } from '~test' import { ArrowShape, SessionType, TLDrawShapeType } from '~types' describe('Duplicate command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() beforeEach(() => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) }) describe('when no shape is selected', () => { it('does nothing', () => { - const initialState = tlstate.state - tlstate.duplicate() - const currentState = tlstate.state + const initialState = state.state + state.duplicate() + const currentState = state.state expect(currentState).toEqual(initialState) }) }) it('does, undoes and redoes command', () => { - tlstate.select('rect1') + state.select('rect1') - expect(Object.keys(tlstate.getPage().shapes).length).toBe(3) + expect(Object.keys(state.getPage().shapes).length).toBe(3) - tlstate.duplicate() + state.duplicate() - expect(Object.keys(tlstate.getPage().shapes).length).toBe(4) + expect(Object.keys(state.getPage().shapes).length).toBe(4) - tlstate.undo() + state.undo() - expect(Object.keys(tlstate.getPage().shapes).length).toBe(3) + expect(Object.keys(state.getPage().shapes).length).toBe(3) - tlstate.redo() + state.redo() - expect(Object.keys(tlstate.getPage().shapes).length).toBe(4) + expect(Object.keys(state.getPage().shapes).length).toBe(4) }) describe('when duplicating a shape', () => { @@ -46,7 +46,7 @@ describe('Duplicate command', () => { describe('when duplicating a bound shape', () => { it('removed the binding when the target is not selected', () => { - tlstate.resetDocument().createShapes( + state.resetDocument().createShapes( { id: 'target1', type: TLDrawShapeType.Rectangle, @@ -60,33 +60,33 @@ describe('Duplicate command', () => { } ) - const beforeShapeIds = Object.keys(tlstate.page.shapes) + const beforeShapeIds = Object.keys(state.page.shapes) - tlstate + state .select('arrow1') .startSession(SessionType.Arrow, [200, 200], 'start') .updateSession([50, 50]) .completeSession() - const beforeArrow = tlstate.getShape('arrow1') + const beforeArrow = state.getShape('arrow1') expect(beforeArrow.handles.start.bindingId).toBeTruthy() - tlstate.select('arrow1').duplicate() + state.select('arrow1').duplicate() - const afterShapeIds = Object.keys(tlstate.page.shapes) + const afterShapeIds = Object.keys(state.page.shapes) const newShapeIds = afterShapeIds.filter((id) => !beforeShapeIds.includes(id)) expect(newShapeIds.length).toBe(1) - const duplicatedArrow = tlstate.getShape(newShapeIds[0]) + const duplicatedArrow = state.getShape(newShapeIds[0]) expect(duplicatedArrow.handles.start.bindingId).toBeUndefined() }) it('duplicates the binding when the target is selected', () => { - tlstate.resetDocument().createShapes( + state.resetDocument().createShapes( { id: 'target1', type: TLDrawShapeType.Rectangle, @@ -100,76 +100,74 @@ describe('Duplicate command', () => { } ) - const beforeShapeIds = Object.keys(tlstate.page.shapes) + const beforeShapeIds = Object.keys(state.page.shapes) - tlstate + state .select('arrow1') .startSession(SessionType.Arrow, [200, 200], 'start') .updateSession([50, 50]) .completeSession() - const oldBindingId = tlstate.getShape('arrow1').handles.start.bindingId + const oldBindingId = state.getShape('arrow1').handles.start.bindingId expect(oldBindingId).toBeTruthy() - tlstate.select('arrow1', 'target1').duplicate() + state.select('arrow1', 'target1').duplicate() - const afterShapeIds = Object.keys(tlstate.page.shapes) + const afterShapeIds = Object.keys(state.page.shapes) const newShapeIds = afterShapeIds.filter((id) => !beforeShapeIds.includes(id)) expect(newShapeIds.length).toBe(2) - const newBindingId = tlstate.getShape(newShapeIds[0]).handles.start.bindingId + const newBindingId = state.getShape(newShapeIds[0]).handles.start.bindingId expect(newBindingId).toBeTruthy() - tlstate.undo() + state.undo() - expect(tlstate.getBinding(newBindingId!)).toBeUndefined() - expect(tlstate.getShape(newShapeIds[0])).toBeUndefined() + expect(state.getBinding(newBindingId!)).toBeUndefined() + expect(state.getShape(newShapeIds[0])).toBeUndefined() - tlstate.redo() + state.redo() - expect(tlstate.getBinding(newBindingId!)).toBeTruthy() - expect(tlstate.getShape(newShapeIds[0]).handles.start.bindingId).toBe( - newBindingId - ) + expect(state.getBinding(newBindingId!)).toBeTruthy() + expect(state.getShape(newShapeIds[0]).handles.start.bindingId).toBe(newBindingId) }) it('duplicates groups', () => { - tlstate.group(['rect1', 'rect2'], 'newGroup').select('newGroup') + state.group(['rect1', 'rect2'], 'newGroup').select('newGroup') - const beforeShapeIds = Object.keys(tlstate.page.shapes) + const beforeShapeIds = Object.keys(state.page.shapes) - tlstate.duplicate() + state.duplicate() - expect(Object.keys(tlstate.page.shapes).length).toBe(beforeShapeIds.length + 3) + expect(Object.keys(state.page.shapes).length).toBe(beforeShapeIds.length + 3) - tlstate.undo() + state.undo() - expect(Object.keys(tlstate.page.shapes).length).toBe(beforeShapeIds.length) + expect(Object.keys(state.page.shapes).length).toBe(beforeShapeIds.length) - tlstate.redo() + state.redo() - expect(Object.keys(tlstate.page.shapes).length).toBe(beforeShapeIds.length + 3) + expect(Object.keys(state.page.shapes).length).toBe(beforeShapeIds.length + 3) }) it('duplicates grouped shapes', () => { - tlstate.group(['rect1', 'rect2'], 'newGroup').select('rect1') + state.group(['rect1', 'rect2'], 'newGroup').select('rect1') - const beforeShapeIds = Object.keys(tlstate.page.shapes) + const beforeShapeIds = Object.keys(state.page.shapes) - tlstate.duplicate() + state.duplicate() - expect(Object.keys(tlstate.page.shapes).length).toBe(beforeShapeIds.length + 1) + expect(Object.keys(state.page.shapes).length).toBe(beforeShapeIds.length + 1) - tlstate.undo() + state.undo() - expect(Object.keys(tlstate.page.shapes).length).toBe(beforeShapeIds.length) + expect(Object.keys(state.page.shapes).length).toBe(beforeShapeIds.length) - tlstate.redo() + state.redo() - expect(Object.keys(tlstate.page.shapes).length).toBe(beforeShapeIds.length + 1) + expect(Object.keys(state.page.shapes).length).toBe(beforeShapeIds.length + 1) }) }) @@ -178,25 +176,25 @@ describe('Duplicate command', () => { describe('when point-duplicating', () => { it('duplicates without crashing', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() - tlstate + state .loadDocument(mockDocument) .group(['rect1', 'rect2']) .selectAll() - .duplicate(tlstate.selectedIds, [200, 200]) + .duplicate(state.selectedIds, [200, 200]) }) it('duplicates in the correct place', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() - tlstate.loadDocument(mockDocument).group(['rect1', 'rect2']).selectAll() + state.loadDocument(mockDocument).group(['rect1', 'rect2']).selectAll() - const before = tlstate.shapes.map((shape) => shape.id) + const before = state.shapes.map((shape) => shape.id) - tlstate.duplicate(tlstate.selectedIds, [200, 200]) + state.duplicate(state.selectedIds, [200, 200]) - const after = tlstate.shapes.filter((shape) => !before.includes(shape.id)) + const after = state.shapes.filter((shape) => !before.includes(shape.id)) expect( Utils.getBoundsCenter(Utils.getCommonBounds(after.map((shape) => TLDR.getBounds(shape)))) diff --git a/packages/tldraw/src/state/commands/duplicateShapes/duplicateShapes.ts b/packages/tldraw/src/state/commands/duplicateShapes/duplicateShapes.ts index 104734120..97aa1b7b0 100644 --- a/packages/tldraw/src/state/commands/duplicateShapes/duplicateShapes.ts +++ b/packages/tldraw/src/state/commands/duplicateShapes/duplicateShapes.ts @@ -2,9 +2,13 @@ import { Utils } from '@tldraw/core' import { Vec } from '@tldraw/vec' import { TLDR } from '~state/TLDR' -import type { Data, PagePartial, TLDrawCommand, TLDrawShape } from '~types' +import type { TLDrawSnapshot, PagePartial, TLDrawCommand, TLDrawShape } from '~types' -export function duplicateShapes(data: Data, ids: string[], point?: number[]): TLDrawCommand { +export function duplicateShapes( + data: TLDrawSnapshot, + ids: string[], + point?: number[] +): TLDrawCommand { const { currentPageId } = data.appState const page = TLDR.getPage(data, currentPageId) diff --git a/packages/tldraw/src/state/commands/flipShapes/flipShapes.spec.ts b/packages/tldraw/src/state/commands/flipShapes/flipShapes.spec.ts index 248dca5af..db66f29cc 100644 --- a/packages/tldraw/src/state/commands/flipShapes/flipShapes.spec.ts +++ b/packages/tldraw/src/state/commands/flipShapes/flipShapes.spec.ts @@ -3,48 +3,48 @@ import { mockDocument } from '~test' import type { RectangleShape } from '~types' describe('Flip command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() beforeEach(() => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) }) describe('when no shape is selected', () => { it('does nothing', () => { - const initialState = tlstate.state - tlstate.flipHorizontal() - const currentState = tlstate.state + const initialState = state.state + state.flipHorizontal() + const currentState = state.state expect(currentState).toEqual(initialState) }) }) it('does, undoes and redoes command', () => { - tlstate.select('rect1', 'rect2') - tlstate.flipHorizontal() + state.select('rect1', 'rect2') + state.flipHorizontal() - expect(tlstate.getShape('rect1').point).toStrictEqual([100, 0]) + expect(state.getShape('rect1').point).toStrictEqual([100, 0]) - tlstate.undo() + state.undo() - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) - tlstate.redo() + state.redo() - expect(tlstate.getShape('rect1').point).toStrictEqual([100, 0]) + expect(state.getShape('rect1').point).toStrictEqual([100, 0]) }) it('flips horizontally', () => { - tlstate.select('rect1', 'rect2') - tlstate.flipHorizontal() + state.select('rect1', 'rect2') + state.flipHorizontal() - expect(tlstate.getShape('rect1').point).toStrictEqual([100, 0]) + expect(state.getShape('rect1').point).toStrictEqual([100, 0]) }) it('flips vertically', () => { - tlstate.select('rect1', 'rect2') - tlstate.flipVertical() + state.select('rect1', 'rect2') + state.flipVertical() - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 100]) + expect(state.getShape('rect1').point).toStrictEqual([0, 100]) }) }) diff --git a/packages/tldraw/src/state/commands/flipShapes/flipShapes.ts b/packages/tldraw/src/state/commands/flipShapes/flipShapes.ts index 92c5375b6..ec4046f63 100644 --- a/packages/tldraw/src/state/commands/flipShapes/flipShapes.ts +++ b/packages/tldraw/src/state/commands/flipShapes/flipShapes.ts @@ -1,9 +1,9 @@ import { FlipType } from '~types' import { TLBoundsCorner, Utils } from '@tldraw/core' -import type { Data, TLDrawCommand } from '~types' +import type { TLDrawSnapshot, TLDrawCommand } from '~types' import { TLDR } from '~state/TLDR' -export function flipShapes(data: Data, ids: string[], type: FlipType): TLDrawCommand { +export function flipShapes(data: TLDrawSnapshot, ids: string[], type: FlipType): TLDrawCommand { const { currentPageId } = data.appState const initialShapes = ids.map((id) => TLDR.getShape(data, id, currentPageId)) diff --git a/packages/tldraw/src/state/commands/groupShapes/groupShapes.spec.ts b/packages/tldraw/src/state/commands/groupShapes/groupShapes.spec.ts index 5ad564142..108f628d2 100644 --- a/packages/tldraw/src/state/commands/groupShapes/groupShapes.spec.ts +++ b/packages/tldraw/src/state/commands/groupShapes/groupShapes.spec.ts @@ -4,42 +4,42 @@ import { mockDocument } from '~test' import { GroupShape, TLDrawShape, TLDrawShapeType } from '~types' describe('Group command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() beforeEach(() => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) }) it('does, undoes and redoes command', () => { - tlstate.group(['rect1', 'rect2'], 'newGroup') + state.group(['rect1', 'rect2'], 'newGroup') - expect(tlstate.getShape('newGroup')).toBeTruthy() + expect(state.getShape('newGroup')).toBeTruthy() - tlstate.undo() + state.undo() - expect(tlstate.getShape('newGroup')).toBeUndefined() + expect(state.getShape('newGroup')).toBeUndefined() - tlstate.redo() + state.redo() - expect(tlstate.getShape('newGroup')).toBeTruthy() + expect(state.getShape('newGroup')).toBeTruthy() }) describe('when less than two shapes are selected', () => { it('does nothing', () => { - tlstate.selectNone() + state.selectNone() // @ts-ignore - const stackLength = tlstate.stack.length + const stackLength = state.stack.length - tlstate.group([], 'newGroup') - expect(tlstate.getShape('newGroup')).toBeUndefined() + state.group([], 'newGroup') + expect(state.getShape('newGroup')).toBeUndefined() // @ts-ignore - expect(tlstate.stack.length).toBe(stackLength) + expect(state.stack.length).toBe(stackLength) - tlstate.group(['rect1'], 'newGroup') - expect(tlstate.getShape('newGroup')).toBeUndefined() + state.group(['rect1'], 'newGroup') + expect(state.getShape('newGroup')).toBeUndefined() // @ts-ignore - expect(tlstate.stack.length).toBe(stackLength) + expect(state.stack.length).toBe(stackLength) }) }) @@ -51,7 +51,7 @@ describe('Group command', () => { */ it('creates a group with the correct props', () => { - tlstate.updateShapes( + state.updateShapes( { id: 'rect1', point: [300, 300], @@ -64,8 +64,8 @@ describe('Group command', () => { } ) - tlstate.group(['rect1', 'rect2'], 'newGroup') - const group = tlstate.getShape('newGroup') + state.group(['rect1', 'rect2'], 'newGroup') + const group = state.getShape('newGroup') expect(group).toBeTruthy() expect(group.parentId).toBe('page1') expect(group.childIndex).toBe(3) @@ -74,7 +74,7 @@ describe('Group command', () => { }) it('reparents the grouped shapes', () => { - tlstate.updateShapes( + state.updateShapes( { id: 'rect1', childIndex: 2.5, @@ -85,13 +85,13 @@ describe('Group command', () => { } ) - tlstate.group(['rect1', 'rect2'], 'newGroup') + state.group(['rect1', 'rect2'], 'newGroup') let rect1: TLDrawShape let rect2: TLDrawShape - rect1 = tlstate.getShape('rect1') - rect2 = tlstate.getShape('rect2') + rect1 = state.getShape('rect1') + rect2 = state.getShape('rect2') // Reparents the shapes expect(rect1.parentId).toBe('newGroup') expect(rect2.parentId).toBe('newGroup') @@ -99,10 +99,10 @@ describe('Group command', () => { expect(rect1.childIndex).toBe(1) expect(rect2.childIndex).toBe(2) - tlstate.undo() + state.undo() - rect1 = tlstate.getShape('rect1') - rect2 = tlstate.getShape('rect2') + rect1 = state.getShape('rect1') + rect2 = state.getShape('rect2') // Restores the shapes' parentIds expect(rect1.parentId).toBe('page1') expect(rect2.parentId).toBe('page1') @@ -127,7 +127,7 @@ describe('Group command', () => { original group be updated to only contain the remaining ones. */ - tlstate.resetDocument().createShapes( + state.resetDocument().createShapes( { id: 'rect1', type: TLDrawShapeType.Rectangle, @@ -150,59 +150,59 @@ describe('Group command', () => { } ) - tlstate.group(['rect1', 'rect2', 'rect3', 'rect4'], 'newGroupA') + state.group(['rect1', 'rect2', 'rect3', 'rect4'], 'newGroupA') - expect(tlstate.getShape('newGroupA')).toBeTruthy() - expect(tlstate.getShape('rect1').childIndex).toBe(1) - expect(tlstate.getShape('rect2').childIndex).toBe(2) - expect(tlstate.getShape('rect3').childIndex).toBe(3) - expect(tlstate.getShape('rect4').childIndex).toBe(4) - expect(tlstate.getShape('newGroupA').children).toStrictEqual([ + expect(state.getShape('newGroupA')).toBeTruthy() + expect(state.getShape('rect1').childIndex).toBe(1) + expect(state.getShape('rect2').childIndex).toBe(2) + expect(state.getShape('rect3').childIndex).toBe(3) + expect(state.getShape('rect4').childIndex).toBe(4) + expect(state.getShape('newGroupA').children).toStrictEqual([ 'rect1', 'rect2', 'rect3', 'rect4', ]) - tlstate.group(['rect1', 'rect3'], 'newGroupB') + state.group(['rect1', 'rect3'], 'newGroupB') - expect(tlstate.getShape('newGroupA')).toBeTruthy() - expect(tlstate.getShape('rect2').childIndex).toBe(2) - expect(tlstate.getShape('rect4').childIndex).toBe(4) - expect(tlstate.getShape('newGroupA').children).toStrictEqual(['rect2', 'rect4']) + expect(state.getShape('newGroupA')).toBeTruthy() + expect(state.getShape('rect2').childIndex).toBe(2) + expect(state.getShape('rect4').childIndex).toBe(4) + expect(state.getShape('newGroupA').children).toStrictEqual(['rect2', 'rect4']) - expect(tlstate.getShape('newGroupB')).toBeTruthy() - expect(tlstate.getShape('rect1').childIndex).toBe(1) - expect(tlstate.getShape('rect3').childIndex).toBe(2) - expect(tlstate.getShape('newGroupB').children).toStrictEqual(['rect1', 'rect3']) + expect(state.getShape('newGroupB')).toBeTruthy() + expect(state.getShape('rect1').childIndex).toBe(1) + expect(state.getShape('rect3').childIndex).toBe(2) + expect(state.getShape('newGroupB').children).toStrictEqual(['rect1', 'rect3']) - tlstate.undo() + state.undo() - expect(tlstate.getShape('newGroupA')).toBeTruthy() - expect(tlstate.getShape('rect1').childIndex).toBe(1) - expect(tlstate.getShape('rect2').childIndex).toBe(2) - expect(tlstate.getShape('rect3').childIndex).toBe(3) - expect(tlstate.getShape('rect4').childIndex).toBe(4) - expect(tlstate.getShape('newGroupA').children).toStrictEqual([ + expect(state.getShape('newGroupA')).toBeTruthy() + expect(state.getShape('rect1').childIndex).toBe(1) + expect(state.getShape('rect2').childIndex).toBe(2) + expect(state.getShape('rect3').childIndex).toBe(3) + expect(state.getShape('rect4').childIndex).toBe(4) + expect(state.getShape('newGroupA').children).toStrictEqual([ 'rect1', 'rect2', 'rect3', 'rect4', ]) - expect(tlstate.getShape('newGroupB')).toBeUndefined() + expect(state.getShape('newGroupB')).toBeUndefined() - tlstate.redo() + state.redo() - expect(tlstate.getShape('newGroupA')).toBeTruthy() - expect(tlstate.getShape('rect2').childIndex).toBe(2) - expect(tlstate.getShape('rect4').childIndex).toBe(4) - expect(tlstate.getShape('newGroupA').children).toStrictEqual(['rect2', 'rect4']) + expect(state.getShape('newGroupA')).toBeTruthy() + expect(state.getShape('rect2').childIndex).toBe(2) + expect(state.getShape('rect4').childIndex).toBe(4) + expect(state.getShape('newGroupA').children).toStrictEqual(['rect2', 'rect4']) - expect(tlstate.getShape('newGroupB')).toBeTruthy() - expect(tlstate.getShape('rect1').childIndex).toBe(1) - expect(tlstate.getShape('rect3').childIndex).toBe(2) - expect(tlstate.getShape('newGroupB').children).toStrictEqual(['rect1', 'rect3']) + expect(state.getShape('newGroupB')).toBeTruthy() + expect(state.getShape('rect1').childIndex).toBe(1) + expect(state.getShape('rect3').childIndex).toBe(2) + expect(state.getShape('newGroupB').children).toStrictEqual(['rect1', 'rect3']) }) it('does nothing if all shapes in the group are selected', () => { @@ -210,7 +210,7 @@ describe('Group command', () => { If the selected shapes represent ALL of the children of the a group, then no effect should occur. */ - tlstate.resetDocument().createShapes( + state.resetDocument().createShapes( { id: 'rect1', type: TLDrawShapeType.Rectangle, @@ -228,9 +228,9 @@ describe('Group command', () => { } ) - tlstate.group(['rect1', 'rect2', 'rect3'], 'newGroupA') - tlstate.group(['rect1', 'rect2', 'rect3'], 'newGroupB') - expect(tlstate.getShape('newGroupB')).toBeUndefined() + state.group(['rect1', 'rect2', 'rect3'], 'newGroupA') + state.group(['rect1', 'rect2', 'rect3'], 'newGroupB') + expect(state.getShape('newGroupB')).toBeUndefined() }) it('deletes any groups that no longer have children', () => { @@ -240,7 +240,7 @@ describe('Group command', () => { Other rules around deleted shapes should here apply: bindings connected to the group should be deleted, etc. */ - tlstate.resetDocument().createShapes( + state.resetDocument().createShapes( { id: 'rect1', type: TLDrawShapeType.Rectangle, @@ -258,10 +258,10 @@ describe('Group command', () => { } ) - tlstate.group(['rect1', 'rect2'], 'newGroupA') - tlstate.group(['rect1', 'rect2', 'rect3'], 'newGroupB') - expect(tlstate.getShape('newGroupA')).toBeUndefined() - expect(tlstate.getShape('newGroupB').children).toStrictEqual([ + state.group(['rect1', 'rect2'], 'newGroupA') + state.group(['rect1', 'rect2', 'rect3'], 'newGroupB') + expect(state.getShape('newGroupA')).toBeUndefined() + expect(state.getShape('newGroupB').children).toStrictEqual([ 'rect1', 'rect2', 'rect3', @@ -274,7 +274,7 @@ describe('Group command', () => { groups, then the selected groups should be destroyed and a new group created with the selected shapes and the group(s)' children. */ - tlstate.resetDocument().createShapes( + state.resetDocument().createShapes( { id: 'rect1', type: TLDrawShapeType.Rectangle, @@ -292,26 +292,26 @@ describe('Group command', () => { } ) - tlstate.group(['rect1', 'rect2'], 'newGroupA') - tlstate.group(['newGroupA', 'rect3'], 'newGroupB') - expect(tlstate.getShape('newGroupA')).toBeUndefined() - expect(tlstate.getShape('newGroupB').children).toStrictEqual([ + state.group(['rect1', 'rect2'], 'newGroupA') + state.group(['newGroupA', 'rect3'], 'newGroupB') + expect(state.getShape('newGroupA')).toBeUndefined() + expect(state.getShape('newGroupB').children).toStrictEqual([ 'rect1', 'rect2', 'rect3', ]) - tlstate.undo() + state.undo() - expect(tlstate.getShape('newGroupB')).toBeUndefined() - expect(tlstate.getShape('newGroupA')).toBeDefined() - expect(tlstate.getShape('newGroupA').children).toStrictEqual(['rect1', 'rect2']) + expect(state.getShape('newGroupB')).toBeUndefined() + expect(state.getShape('newGroupA')).toBeDefined() + expect(state.getShape('newGroupA').children).toStrictEqual(['rect1', 'rect2']) - tlstate.redo() + state.redo() - expect(tlstate.getShape('newGroupA')).toBeUndefined() - expect(tlstate.getShape('newGroupB')).toBeDefined() - expect(tlstate.getShape('newGroupB').children).toStrictEqual([ + expect(state.getShape('newGroupA')).toBeUndefined() + expect(state.getShape('newGroupB')).toBeDefined() + expect(state.getShape('newGroupB').children).toStrictEqual([ 'rect1', 'rect2', 'rect3', @@ -319,7 +319,7 @@ describe('Group command', () => { }) it('Ungroups if the only shape selected is a group', () => { - tlstate.resetDocument().createShapes( + state.resetDocument().createShapes( { id: 'rect1', type: TLDrawShapeType.Rectangle, @@ -337,15 +337,15 @@ describe('Group command', () => { } ) - expect(tlstate.shapes.length).toBe(3) + expect(state.shapes.length).toBe(3) - tlstate.selectAll().group() + state.selectAll().group() - expect(tlstate.shapes.length).toBe(4) + expect(state.shapes.length).toBe(4) - tlstate.selectAll().group() + state.selectAll().group() - expect(tlstate.shapes.length).toBe(3) + expect(state.shapes.length).toBe(3) }) /* diff --git a/packages/tldraw/src/state/commands/groupShapes/groupShapes.ts b/packages/tldraw/src/state/commands/groupShapes/groupShapes.ts index 5bf04d411..1f873921e 100644 --- a/packages/tldraw/src/state/commands/groupShapes/groupShapes.ts +++ b/packages/tldraw/src/state/commands/groupShapes/groupShapes.ts @@ -1,11 +1,11 @@ import { TLDrawBinding, TLDrawShape, TLDrawShapeType } from '~types' import { Utils } from '@tldraw/core' -import type { Data, TLDrawCommand } from '~types' +import type { TLDrawSnapshot, TLDrawCommand } from '~types' import { TLDR } from '~state/TLDR' import type { Patch } from 'rko' export function groupShapes( - data: Data, + data: TLDrawSnapshot, ids: string[], groupId: string, pageId: string diff --git a/packages/tldraw/src/state/commands/moveShapesToPage/moveShapesToPage.spec.ts b/packages/tldraw/src/state/commands/moveShapesToPage/moveShapesToPage.spec.ts index e195cfab0..ac69d6051 100644 --- a/packages/tldraw/src/state/commands/moveShapesToPage/moveShapesToPage.spec.ts +++ b/packages/tldraw/src/state/commands/moveShapesToPage/moveShapesToPage.spec.ts @@ -3,17 +3,17 @@ import { mockDocument } from '~test' import { ArrowShape, SessionType, TLDrawShapeType } from '~types' describe('Move to page command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() beforeEach(() => { - tlstate.loadDocument(mockDocument).createPage('page2').changePage('page1') + state.loadDocument(mockDocument).createPage('page2').changePage('page1') }) describe('when no shape is selected', () => { it('does nothing', () => { - const initialState = tlstate.state - tlstate.moveToPage('page2') - const currentState = tlstate.state + const initialState = state.state + state.moveToPage('page2') + const currentState = state.state expect(currentState).toEqual(initialState) }) @@ -29,37 +29,37 @@ describe('Move to page command', () => { */ it('does, undoes and redoes command', () => { - tlstate.select('rect1', 'rect2').moveToPage('page2') + state.select('rect1', 'rect2').moveToPage('page2') - expect(tlstate.currentPageId).toBe('page2') - expect(tlstate.getShape('rect1', 'page1')).toBeUndefined() - expect(tlstate.getShape('rect1', 'page2')).toBeDefined() - expect(tlstate.getShape('rect2', 'page1')).toBeUndefined() - expect(tlstate.getShape('rect2', 'page2')).toBeDefined() - expect(tlstate.selectedIds).toStrictEqual(['rect1', 'rect2']) + expect(state.currentPageId).toBe('page2') + expect(state.getShape('rect1', 'page1')).toBeUndefined() + expect(state.getShape('rect1', 'page2')).toBeDefined() + expect(state.getShape('rect2', 'page1')).toBeUndefined() + expect(state.getShape('rect2', 'page2')).toBeDefined() + expect(state.selectedIds).toStrictEqual(['rect1', 'rect2']) - tlstate.undo() + state.undo() - expect(tlstate.getShape('rect1', 'page1')).toBeDefined() - expect(tlstate.getShape('rect1', 'page2')).toBeUndefined() - expect(tlstate.getShape('rect2', 'page1')).toBeDefined() - expect(tlstate.getShape('rect2', 'page2')).toBeUndefined() - expect(tlstate.selectedIds).toStrictEqual(['rect1', 'rect2']) - expect(tlstate.currentPageId).toBe('page1') + expect(state.getShape('rect1', 'page1')).toBeDefined() + expect(state.getShape('rect1', 'page2')).toBeUndefined() + expect(state.getShape('rect2', 'page1')).toBeDefined() + expect(state.getShape('rect2', 'page2')).toBeUndefined() + expect(state.selectedIds).toStrictEqual(['rect1', 'rect2']) + expect(state.currentPageId).toBe('page1') - tlstate.redo() + state.redo() - expect(tlstate.getShape('rect1', 'page1')).toBeUndefined() - expect(tlstate.getShape('rect1', 'page2')).toBeDefined() - expect(tlstate.getShape('rect2', 'page1')).toBeUndefined() - expect(tlstate.getShape('rect2', 'page2')).toBeDefined() - expect(tlstate.selectedIds).toStrictEqual(['rect1', 'rect2']) - expect(tlstate.currentPageId).toBe('page2') + expect(state.getShape('rect1', 'page1')).toBeUndefined() + expect(state.getShape('rect1', 'page2')).toBeDefined() + expect(state.getShape('rect2', 'page1')).toBeUndefined() + expect(state.getShape('rect2', 'page2')).toBeDefined() + expect(state.selectedIds).toStrictEqual(['rect1', 'rect2']) + expect(state.currentPageId).toBe('page2') }) describe('when moving shapes with bindings', () => { it('deletes bindings when only the bound-to shape is moved', () => { - tlstate + state .selectAll() .delete() .createShapes( @@ -71,36 +71,30 @@ describe('Move to page command', () => { .updateSession([50, 50]) .completeSession() - const bindingId = tlstate.bindings[0].id - expect(tlstate.getShape('arrow1').handles.start.bindingId).toBe(bindingId) + const bindingId = state.bindings[0].id + expect(state.getShape('arrow1').handles.start.bindingId).toBe(bindingId) - tlstate.select('target1').moveToPage('page2') + state.select('target1').moveToPage('page2') - expect( - tlstate.getShape('arrow1', 'page1').handles.start.bindingId - ).toBeUndefined() - expect(tlstate.document.pages['page1'].bindings[bindingId]).toBeUndefined() - expect(tlstate.document.pages['page2'].bindings[bindingId]).toBeUndefined() + expect(state.getShape('arrow1', 'page1').handles.start.bindingId).toBeUndefined() + expect(state.document.pages['page1'].bindings[bindingId]).toBeUndefined() + expect(state.document.pages['page2'].bindings[bindingId]).toBeUndefined() - tlstate.undo() + state.undo() - expect(tlstate.getShape('arrow1', 'page1').handles.start.bindingId).toBe( - bindingId - ) - expect(tlstate.document.pages['page1'].bindings[bindingId]).toBeDefined() - expect(tlstate.document.pages['page2'].bindings[bindingId]).toBeUndefined() + expect(state.getShape('arrow1', 'page1').handles.start.bindingId).toBe(bindingId) + expect(state.document.pages['page1'].bindings[bindingId]).toBeDefined() + expect(state.document.pages['page2'].bindings[bindingId]).toBeUndefined() - tlstate.redo() + state.redo() - expect( - tlstate.getShape('arrow1', 'page1').handles.start.bindingId - ).toBeUndefined() - expect(tlstate.document.pages['page1'].bindings[bindingId]).toBeUndefined() - expect(tlstate.document.pages['page2'].bindings[bindingId]).toBeUndefined() + expect(state.getShape('arrow1', 'page1').handles.start.bindingId).toBeUndefined() + expect(state.document.pages['page1'].bindings[bindingId]).toBeUndefined() + expect(state.document.pages['page2'].bindings[bindingId]).toBeUndefined() }) it('deletes bindings when only the bound-from shape is moved', () => { - tlstate + state .selectAll() .delete() .createShapes( @@ -112,36 +106,30 @@ describe('Move to page command', () => { .updateSession([50, 50]) .completeSession() - const bindingId = tlstate.bindings[0].id - expect(tlstate.getShape('arrow1').handles.start.bindingId).toBe(bindingId) + const bindingId = state.bindings[0].id + expect(state.getShape('arrow1').handles.start.bindingId).toBe(bindingId) - tlstate.select('arrow1').moveToPage('page2') + state.select('arrow1').moveToPage('page2') - expect( - tlstate.getShape('arrow1', 'page2').handles.start.bindingId - ).toBeUndefined() - expect(tlstate.document.pages['page1'].bindings[bindingId]).toBeUndefined() - expect(tlstate.document.pages['page2'].bindings[bindingId]).toBeUndefined() + expect(state.getShape('arrow1', 'page2').handles.start.bindingId).toBeUndefined() + expect(state.document.pages['page1'].bindings[bindingId]).toBeUndefined() + expect(state.document.pages['page2'].bindings[bindingId]).toBeUndefined() - tlstate.undo() + state.undo() - expect(tlstate.getShape('arrow1', 'page1').handles.start.bindingId).toBe( - bindingId - ) - expect(tlstate.document.pages['page1'].bindings[bindingId]).toBeDefined() - expect(tlstate.document.pages['page2'].bindings[bindingId]).toBeUndefined() + expect(state.getShape('arrow1', 'page1').handles.start.bindingId).toBe(bindingId) + expect(state.document.pages['page1'].bindings[bindingId]).toBeDefined() + expect(state.document.pages['page2'].bindings[bindingId]).toBeUndefined() - tlstate.redo() + state.redo() - expect( - tlstate.getShape('arrow1', 'page2').handles.start.bindingId - ).toBeUndefined() - expect(tlstate.document.pages['page1'].bindings[bindingId]).toBeUndefined() - expect(tlstate.document.pages['page2'].bindings[bindingId]).toBeUndefined() + expect(state.getShape('arrow1', 'page2').handles.start.bindingId).toBeUndefined() + expect(state.document.pages['page1'].bindings[bindingId]).toBeUndefined() + expect(state.document.pages['page2'].bindings[bindingId]).toBeUndefined() }) it('moves bindings when both shapes are moved', () => { - tlstate + state .selectAll() .delete() .createShapes( @@ -153,87 +141,77 @@ describe('Move to page command', () => { .updateSession([50, 50]) .completeSession() - const bindingId = tlstate.bindings[0].id - expect(tlstate.getShape('arrow1').handles.start.bindingId).toBe(bindingId) + const bindingId = state.bindings[0].id + expect(state.getShape('arrow1').handles.start.bindingId).toBe(bindingId) - tlstate.select('arrow1', 'target1').moveToPage('page2') + state.select('arrow1', 'target1').moveToPage('page2') - expect(tlstate.getShape('arrow1', 'page2').handles.start.bindingId).toBe( - bindingId - ) - expect(tlstate.document.pages['page1'].bindings[bindingId]).toBeUndefined() - expect(tlstate.document.pages['page2'].bindings[bindingId]).toBeDefined() + expect(state.getShape('arrow1', 'page2').handles.start.bindingId).toBe(bindingId) + expect(state.document.pages['page1'].bindings[bindingId]).toBeUndefined() + expect(state.document.pages['page2'].bindings[bindingId]).toBeDefined() - tlstate.undo() + state.undo() - expect(tlstate.getShape('arrow1', 'page1').handles.start.bindingId).toBe( - bindingId - ) - expect(tlstate.document.pages['page1'].bindings[bindingId]).toBeDefined() - expect(tlstate.document.pages['page2'].bindings[bindingId]).toBeUndefined() + expect(state.getShape('arrow1', 'page1').handles.start.bindingId).toBe(bindingId) + expect(state.document.pages['page1'].bindings[bindingId]).toBeDefined() + expect(state.document.pages['page2'].bindings[bindingId]).toBeUndefined() - tlstate.redo() + state.redo() - expect(tlstate.getShape('arrow1', 'page2').handles.start.bindingId).toBe( - bindingId - ) - expect(tlstate.document.pages['page1'].bindings[bindingId]).toBeUndefined() - expect(tlstate.document.pages['page2'].bindings[bindingId]).toBeDefined() + expect(state.getShape('arrow1', 'page2').handles.start.bindingId).toBe(bindingId) + expect(state.document.pages['page1'].bindings[bindingId]).toBeUndefined() + expect(state.document.pages['page2'].bindings[bindingId]).toBeDefined() }) }) describe('when moving grouped shapes', () => { it('moves groups and their children', () => { - tlstate.group(['rect1', 'rect2'], 'groupA').moveToPage('page2') + state.group(['rect1', 'rect2'], 'groupA').moveToPage('page2') - expect(tlstate.getShape('rect1', 'page1')).toBeUndefined() - expect(tlstate.getShape('rect2', 'page1')).toBeUndefined() - expect(tlstate.getShape('groupA', 'page1')).toBeUndefined() + expect(state.getShape('rect1', 'page1')).toBeUndefined() + expect(state.getShape('rect2', 'page1')).toBeUndefined() + expect(state.getShape('groupA', 'page1')).toBeUndefined() - expect(tlstate.getShape('rect1', 'page2')).toBeDefined() - expect(tlstate.getShape('rect2', 'page2')).toBeDefined() - expect(tlstate.getShape('groupA', 'page2')).toBeDefined() + expect(state.getShape('rect1', 'page2')).toBeDefined() + expect(state.getShape('rect2', 'page2')).toBeDefined() + expect(state.getShape('groupA', 'page2')).toBeDefined() - tlstate.undo() + state.undo() - expect(tlstate.getShape('rect1', 'page2')).toBeUndefined() - expect(tlstate.getShape('rect2', 'page2')).toBeUndefined() - expect(tlstate.getShape('groupA', 'page2')).toBeUndefined() + expect(state.getShape('rect1', 'page2')).toBeUndefined() + expect(state.getShape('rect2', 'page2')).toBeUndefined() + expect(state.getShape('groupA', 'page2')).toBeUndefined() - expect(tlstate.getShape('rect1', 'page1')).toBeDefined() - expect(tlstate.getShape('rect2', 'page1')).toBeDefined() - expect(tlstate.getShape('groupA', 'page1')).toBeDefined() + expect(state.getShape('rect1', 'page1')).toBeDefined() + expect(state.getShape('rect2', 'page1')).toBeDefined() + expect(state.getShape('groupA', 'page1')).toBeDefined() - tlstate.redo() + state.redo() - expect(tlstate.getShape('rect1', 'page1')).toBeUndefined() - expect(tlstate.getShape('rect2', 'page1')).toBeUndefined() - expect(tlstate.getShape('groupA', 'page1')).toBeUndefined() + expect(state.getShape('rect1', 'page1')).toBeUndefined() + expect(state.getShape('rect2', 'page1')).toBeUndefined() + expect(state.getShape('groupA', 'page1')).toBeUndefined() - expect(tlstate.getShape('rect1', 'page2')).toBeDefined() - expect(tlstate.getShape('rect2', 'page2')).toBeDefined() - expect(tlstate.getShape('groupA', 'page2')).toBeDefined() + expect(state.getShape('rect1', 'page2')).toBeDefined() + expect(state.getShape('rect2', 'page2')).toBeDefined() + expect(state.getShape('groupA', 'page2')).toBeDefined() }) it.todo('deletes groups shapes if the groups children were all moved') it('reparents grouped shapes if the group is not moved', () => { - tlstate.group(['rect1', 'rect2', 'rect3'], 'groupA').select('rect1').moveToPage('page2') + state.group(['rect1', 'rect2', 'rect3'], 'groupA').select('rect1').moveToPage('page2') - expect(tlstate.getShape('rect1', 'page1')).toBeUndefined() - expect(tlstate.getShape('rect1', 'page2')).toBeDefined() - expect(tlstate.getShape('rect1', 'page2').parentId).toBe('page2') - expect(tlstate.getShape('groupA', 'page1').children).toStrictEqual(['rect2', 'rect3']) + expect(state.getShape('rect1', 'page1')).toBeUndefined() + expect(state.getShape('rect1', 'page2')).toBeDefined() + expect(state.getShape('rect1', 'page2').parentId).toBe('page2') + expect(state.getShape('groupA', 'page1').children).toStrictEqual(['rect2', 'rect3']) - tlstate.undo() + state.undo() - expect(tlstate.getShape('rect1', 'page2')).toBeUndefined() - expect(tlstate.getShape('rect1', 'page1').parentId).toBe('groupA') - expect(tlstate.getShape('groupA', 'page1').children).toStrictEqual([ - 'rect1', - 'rect2', - 'rect3', - ]) + expect(state.getShape('rect1', 'page2')).toBeUndefined() + expect(state.getShape('rect1', 'page1').parentId).toBe('groupA') + expect(state.getShape('groupA', 'page1').children).toStrictEqual(['rect1', 'rect2', 'rect3']) }) }) diff --git a/packages/tldraw/src/state/commands/moveShapesToPage/moveShapesToPage.ts b/packages/tldraw/src/state/commands/moveShapesToPage/moveShapesToPage.ts index fc2c8e87b..fd0e8a71d 100644 --- a/packages/tldraw/src/state/commands/moveShapesToPage/moveShapesToPage.ts +++ b/packages/tldraw/src/state/commands/moveShapesToPage/moveShapesToPage.ts @@ -1,11 +1,11 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { ArrowShape, Data, PagePartial, TLDrawCommand, TLDrawShape } from '~types' +import type { ArrowShape, TLDrawSnapshot, PagePartial, TLDrawCommand, TLDrawShape } from '~types' import { TLDR } from '~state/TLDR' import { Utils, TLBounds } from '@tldraw/core' import { Vec } from '@tldraw/vec' export function moveShapesToPage( - data: Data, + data: TLDrawSnapshot, ids: string[], viewportBounds: TLBounds, fromPageId: string, diff --git a/packages/tldraw/src/state/commands/renamePage/renamePage.spec.ts b/packages/tldraw/src/state/commands/renamePage/renamePage.spec.ts index ffa84024d..fa4391878 100644 --- a/packages/tldraw/src/state/commands/renamePage/renamePage.spec.ts +++ b/packages/tldraw/src/state/commands/renamePage/renamePage.spec.ts @@ -2,24 +2,24 @@ import { TLDrawState } from '~state' import { mockDocument } from '~test' describe('Rename page command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() it('does, undoes and redoes command', () => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) - const initialId = tlstate.page.id - const initialName = tlstate.page.name + const initialId = state.page.id + const initialName = state.page.name - tlstate.renamePage(initialId, 'My Special Page') + state.renamePage(initialId, 'My Special Page') - expect(tlstate.page.name).toBe('My Special Page') + expect(state.page.name).toBe('My Special Page') - tlstate.undo() + state.undo() - expect(tlstate.page.name).toBe(initialName) + expect(state.page.name).toBe(initialName) - tlstate.redo() + state.redo() - expect(tlstate.page.name).toBe('My Special Page') + expect(state.page.name).toBe('My Special Page') }) }) diff --git a/packages/tldraw/src/state/commands/renamePage/renamePage.ts b/packages/tldraw/src/state/commands/renamePage/renamePage.ts index a694bd8eb..679c57132 100644 --- a/packages/tldraw/src/state/commands/renamePage/renamePage.ts +++ b/packages/tldraw/src/state/commands/renamePage/renamePage.ts @@ -1,6 +1,6 @@ -import type { Data, TLDrawCommand } from '~types' +import type { TLDrawSnapshot, TLDrawCommand } from '~types' -export function renamePage(data: Data, pageId: string, name: string): TLDrawCommand { +export function renamePage(data: TLDrawSnapshot, pageId: string, name: string): TLDrawCommand { const page = data.document.pages[pageId] return { id: 'rename_page', diff --git a/packages/tldraw/src/state/commands/reorderShapes/reorderShapes.spec.ts b/packages/tldraw/src/state/commands/reorderShapes/reorderShapes.spec.ts index ac4f09d24..ae5ced124 100644 --- a/packages/tldraw/src/state/commands/reorderShapes/reorderShapes.spec.ts +++ b/packages/tldraw/src/state/commands/reorderShapes/reorderShapes.spec.ts @@ -1,8 +1,8 @@ import { TLDrawState } from '~state' -import { Data, TLDrawShapeType } from '~types' +import { TLDrawSnapshot, TLDrawShapeType } from '~types' import { TLDR } from '~state/TLDR' -const tlstate = new TLDrawState().createShapes( +const state = new TLDrawState().createShapes( { type: TLDrawShapeType.Rectangle, id: 'a', @@ -25,16 +25,16 @@ const tlstate = new TLDrawState().createShapes( } ) -const doc = { ...tlstate.document } +const doc = { ...state.document } -function getSortedShapeIds(data: Data) { +function getSortedShapeIds(data: TLDrawSnapshot) { return TLDR.getShapes(data, data.appState.currentPageId) .sort((a, b) => a.childIndex - b.childIndex) .map((shape) => shape.id) .join('') } -function getSortedIndices(data: Data) { +function getSortedIndices(data: TLDrawSnapshot) { return TLDR.getShapes(data, data.appState.currentPageId) .sort((a, b) => a.childIndex - b.childIndex) .map((shape) => shape.childIndex.toFixed(2)) @@ -43,156 +43,156 @@ function getSortedIndices(data: Data) { describe('Move command', () => { beforeEach(() => { - tlstate.loadDocument(doc) + state.loadDocument(doc) }) describe('when no shape is selected', () => { it('does nothing', () => { - const initialState = tlstate.state - tlstate.moveToBack() + const initialState = state.state + state.moveToBack() - const currentState = tlstate.state + const currentState = state.state expect(currentState).toEqual(initialState) }) }) it('does, undoes and redoes command', () => { - tlstate.select('b') - tlstate.moveToBack() - expect(getSortedShapeIds(tlstate.state)).toBe('bacd') - tlstate.undo() - expect(getSortedShapeIds(tlstate.state)).toBe('abcd') - tlstate.redo() - expect(getSortedShapeIds(tlstate.state)).toBe('bacd') + state.select('b') + state.moveToBack() + expect(getSortedShapeIds(state.state)).toBe('bacd') + state.undo() + expect(getSortedShapeIds(state.state)).toBe('abcd') + state.redo() + expect(getSortedShapeIds(state.state)).toBe('bacd') }) describe('to back', () => { it('moves a shape to back', () => { - tlstate.select('b') - tlstate.moveToBack() - expect(getSortedShapeIds(tlstate.state)).toBe('bacd') - expect(getSortedIndices(tlstate.state)).toBe('0.50,1.00,3.00,4.00') + state.select('b') + state.moveToBack() + expect(getSortedShapeIds(state.state)).toBe('bacd') + expect(getSortedIndices(state.state)).toBe('0.50,1.00,3.00,4.00') }) it('moves two adjacent siblings to back', () => { - tlstate.select('b', 'c') - tlstate.moveToBack() - expect(getSortedShapeIds(tlstate.state)).toBe('bcad') - expect(getSortedIndices(tlstate.state)).toBe('0.33,0.67,1.00,4.00') + state.select('b', 'c') + state.moveToBack() + expect(getSortedShapeIds(state.state)).toBe('bcad') + expect(getSortedIndices(state.state)).toBe('0.33,0.67,1.00,4.00') }) it('moves two non-adjacent siblings to back', () => { - tlstate.select('b', 'd') - tlstate.moveToBack() - expect(getSortedShapeIds(tlstate.state)).toBe('bdac') - expect(getSortedIndices(tlstate.state)).toBe('0.33,0.67,1.00,3.00') + state.select('b', 'd') + state.moveToBack() + expect(getSortedShapeIds(state.state)).toBe('bdac') + expect(getSortedIndices(state.state)).toBe('0.33,0.67,1.00,3.00') }) }) describe('backward', () => { it('moves a shape backward', () => { - tlstate.select('c') - tlstate.moveBackward() - expect(getSortedShapeIds(tlstate.state)).toBe('acbd') - expect(getSortedIndices(tlstate.state)).toBe('1.00,1.50,2.00,4.00') + state.select('c') + state.moveBackward() + expect(getSortedShapeIds(state.state)).toBe('acbd') + expect(getSortedIndices(state.state)).toBe('1.00,1.50,2.00,4.00') }) it('moves a shape at first index backward', () => { - tlstate.select('a') - tlstate.moveBackward() - expect(getSortedShapeIds(tlstate.state)).toBe('abcd') - expect(getSortedIndices(tlstate.state)).toBe('1.00,2.00,3.00,4.00') + state.select('a') + state.moveBackward() + expect(getSortedShapeIds(state.state)).toBe('abcd') + expect(getSortedIndices(state.state)).toBe('1.00,2.00,3.00,4.00') }) it('moves two adjacent siblings backward', () => { - tlstate.select('c', 'd') - tlstate.moveBackward() - expect(getSortedShapeIds(tlstate.state)).toBe('acdb') - expect(getSortedIndices(tlstate.state)).toBe('1.00,1.50,1.67,2.00') + state.select('c', 'd') + state.moveBackward() + expect(getSortedShapeIds(state.state)).toBe('acdb') + expect(getSortedIndices(state.state)).toBe('1.00,1.50,1.67,2.00') }) it('moves two non-adjacent siblings backward', () => { - tlstate.select('b', 'd') - tlstate.moveBackward() - expect(getSortedShapeIds(tlstate.state)).toBe('badc') - expect(getSortedIndices(tlstate.state)).toBe('0.50,1.00,2.50,3.00') + state.select('b', 'd') + state.moveBackward() + expect(getSortedShapeIds(state.state)).toBe('badc') + expect(getSortedIndices(state.state)).toBe('0.50,1.00,2.50,3.00') }) it('moves two adjacent siblings backward at zero index', () => { - tlstate.select('a', 'b') - tlstate.moveBackward() - expect(getSortedShapeIds(tlstate.state)).toBe('abcd') - expect(getSortedIndices(tlstate.state)).toBe('1.00,2.00,3.00,4.00') + state.select('a', 'b') + state.moveBackward() + expect(getSortedShapeIds(state.state)).toBe('abcd') + expect(getSortedIndices(state.state)).toBe('1.00,2.00,3.00,4.00') }) }) describe('forward', () => { it('moves a shape forward', () => { - tlstate.select('c') - tlstate.moveForward() - expect(getSortedShapeIds(tlstate.state)).toBe('abdc') - expect(getSortedIndices(tlstate.state)).toBe('1.00,2.00,4.00,5.00') + state.select('c') + state.moveForward() + expect(getSortedShapeIds(state.state)).toBe('abdc') + expect(getSortedIndices(state.state)).toBe('1.00,2.00,4.00,5.00') }) it('moves a shape forward at the top index', () => { - tlstate.select('b') - tlstate.moveForward() - tlstate.moveForward() - tlstate.moveForward() - expect(getSortedShapeIds(tlstate.state)).toBe('acdb') - expect(getSortedIndices(tlstate.state)).toBe('1.00,3.00,4.00,5.00') + state.select('b') + state.moveForward() + state.moveForward() + state.moveForward() + expect(getSortedShapeIds(state.state)).toBe('acdb') + expect(getSortedIndices(state.state)).toBe('1.00,3.00,4.00,5.00') }) it('moves two adjacent siblings forward', () => { - tlstate.select('a', 'b') - tlstate.moveForward() - expect(getSortedShapeIds(tlstate.state)).toBe('cabd') - expect(getSortedIndices(tlstate.state)).toBe('3.00,3.33,3.50,4.00') + state.select('a', 'b') + state.moveForward() + expect(getSortedShapeIds(state.state)).toBe('cabd') + expect(getSortedIndices(state.state)).toBe('3.00,3.33,3.50,4.00') }) it('moves two non-adjacent siblings forward', () => { - tlstate.select('a', 'c') - tlstate.moveForward() - expect(getSortedShapeIds(tlstate.state)).toBe('badc') - expect(getSortedIndices(tlstate.state)).toBe('2.00,2.50,4.00,5.00') + state.select('a', 'c') + state.moveForward() + expect(getSortedShapeIds(state.state)).toBe('badc') + expect(getSortedIndices(state.state)).toBe('2.00,2.50,4.00,5.00') }) it('moves two adjacent siblings forward at top index', () => { - tlstate.select('c', 'd') - tlstate.moveForward() - expect(getSortedShapeIds(tlstate.state)).toBe('abcd') - expect(getSortedIndices(tlstate.state)).toBe('1.00,2.00,3.00,4.00') + state.select('c', 'd') + state.moveForward() + expect(getSortedShapeIds(state.state)).toBe('abcd') + expect(getSortedIndices(state.state)).toBe('1.00,2.00,3.00,4.00') }) }) describe('to front', () => { it('moves a shape to front', () => { - tlstate.select('b') - tlstate.moveToFront() - expect(getSortedShapeIds(tlstate.state)).toBe('acdb') - expect(getSortedIndices(tlstate.state)).toBe('1.00,3.00,4.00,5.00') + state.select('b') + state.moveToFront() + expect(getSortedShapeIds(state.state)).toBe('acdb') + expect(getSortedIndices(state.state)).toBe('1.00,3.00,4.00,5.00') }) it('moves two adjacent siblings to front', () => { - tlstate.select('a', 'b') - tlstate.moveToFront() - expect(getSortedShapeIds(tlstate.state)).toBe('cdab') - expect(getSortedIndices(tlstate.state)).toBe('3.00,4.00,5.00,6.00') + state.select('a', 'b') + state.moveToFront() + expect(getSortedShapeIds(state.state)).toBe('cdab') + expect(getSortedIndices(state.state)).toBe('3.00,4.00,5.00,6.00') }) it('moves two non-adjacent siblings to front', () => { - tlstate.select('a', 'c') - tlstate.moveToFront() - expect(getSortedShapeIds(tlstate.state)).toBe('bdac') - expect(getSortedIndices(tlstate.state)).toBe('2.00,4.00,5.00,6.00') + state.select('a', 'c') + state.moveToFront() + expect(getSortedShapeIds(state.state)).toBe('bdac') + expect(getSortedIndices(state.state)).toBe('2.00,4.00,5.00,6.00') }) it('moves siblings already at front to front', () => { - tlstate.select('c', 'd') - tlstate.moveToFront() - expect(getSortedShapeIds(tlstate.state)).toBe('abcd') - expect(getSortedIndices(tlstate.state)).toBe('1.00,2.00,3.00,4.00') + state.select('c', 'd') + state.moveToFront() + expect(getSortedShapeIds(state.state)).toBe('abcd') + expect(getSortedIndices(state.state)).toBe('1.00,2.00,3.00,4.00') }) }) }) diff --git a/packages/tldraw/src/state/commands/reorderShapes/reorderShapes.ts b/packages/tldraw/src/state/commands/reorderShapes/reorderShapes.ts index ee6457428..c3cf38fd5 100644 --- a/packages/tldraw/src/state/commands/reorderShapes/reorderShapes.ts +++ b/packages/tldraw/src/state/commands/reorderShapes/reorderShapes.ts @@ -1,7 +1,7 @@ -import { MoveType, Data, TLDrawShape, TLDrawCommand } from '~types' +import { MoveType, TLDrawSnapshot, TLDrawShape, TLDrawCommand } from '~types' import { TLDR } from '~state/TLDR' -export function reorderShapes(data: Data, ids: string[], type: MoveType): TLDrawCommand { +export function reorderShapes(data: TLDrawSnapshot, ids: string[], type: MoveType): TLDrawCommand { const { currentPageId } = data.appState // Get the unique parent ids for the selected elements diff --git a/packages/tldraw/src/state/commands/resetBounds/resetBounds.spec.ts b/packages/tldraw/src/state/commands/resetBounds/resetBounds.spec.ts index c49a2ff95..2f91f6927 100644 --- a/packages/tldraw/src/state/commands/resetBounds/resetBounds.spec.ts +++ b/packages/tldraw/src/state/commands/resetBounds/resetBounds.spec.ts @@ -5,14 +5,14 @@ import { mockDocument } from '~test' import { SessionType, TLDrawShapeType } from '~types' describe('Reset bounds command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() beforeEach(() => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) }) it('does, undoes and redoes command', () => { - tlstate.createShapes({ + state.createShapes({ id: 'text1', type: TLDrawShapeType.Text, point: [0, 0], @@ -20,18 +20,18 @@ describe('Reset bounds command', () => { }) // Scale is undefined by default - expect(tlstate.getShape('text1').style.scale).toBeUndefined() + expect(state.getShape('text1').style.scale).toBeUndefined() // Transform the shape in order to change its point and scale - tlstate + state .select('text1') .startSession(SessionType.Transform, [0, 0], TLBoundsCorner.TopLeft) .updateSession([-100, -100], false, false) .completeSession() - const scale = tlstate.getShape('text1').style.scale - const bounds = TLDR.getBounds(tlstate.getShape('text1')) + const scale = state.getShape('text1').style.scale + const bounds = TLDR.getBounds(state.getShape('text1')) const center = Utils.getBoundsCenter(bounds) expect(scale).not.toBe(1) @@ -39,25 +39,25 @@ describe('Reset bounds command', () => { // Reset the bounds - tlstate.resetBounds(['text1']) + state.resetBounds(['text1']) // The scale should be back to 1 - expect(tlstate.getShape('text1').style.scale).toBe(1) + expect(state.getShape('text1').style.scale).toBe(1) // The centers should be the same - expect(Utils.getBoundsCenter(TLDR.getBounds(tlstate.getShape('text1')))).toStrictEqual(center) + expect(Utils.getBoundsCenter(TLDR.getBounds(state.getShape('text1')))).toStrictEqual(center) - tlstate.undo() + state.undo() // The scale should be what it was before - expect(tlstate.getShape('text1').style.scale).not.toBe(1) + expect(state.getShape('text1').style.scale).not.toBe(1) // The centers should be the same - expect(Utils.getBoundsCenter(TLDR.getBounds(tlstate.getShape('text1')))).toStrictEqual(center) + expect(Utils.getBoundsCenter(TLDR.getBounds(state.getShape('text1')))).toStrictEqual(center) - tlstate.redo() + state.redo() // The scale should be back to 1 - expect(tlstate.getShape('text1').style.scale).toBe(1) + expect(state.getShape('text1').style.scale).toBe(1) // The centers should be the same - expect(Utils.getBoundsCenter(TLDR.getBounds(tlstate.getShape('text1')))).toStrictEqual(center) + expect(Utils.getBoundsCenter(TLDR.getBounds(state.getShape('text1')))).toStrictEqual(center) }) }) diff --git a/packages/tldraw/src/state/commands/resetBounds/resetBounds.ts b/packages/tldraw/src/state/commands/resetBounds/resetBounds.ts index b3fbd6cc2..7010fa541 100644 --- a/packages/tldraw/src/state/commands/resetBounds/resetBounds.ts +++ b/packages/tldraw/src/state/commands/resetBounds/resetBounds.ts @@ -1,7 +1,7 @@ -import type { Data, TLDrawCommand } from '~types' +import type { TLDrawSnapshot, TLDrawCommand } from '~types' import { TLDR } from '~state/TLDR' -export function resetBounds(data: Data, ids: string[], pageId: string): TLDrawCommand { +export function resetBounds(data: TLDrawSnapshot, ids: string[], pageId: string): TLDrawCommand { const { currentPageId } = data.appState const { before, after } = TLDR.mutateShapes( diff --git a/packages/tldraw/src/state/commands/rotateShapes/rotateShapes.spec.ts b/packages/tldraw/src/state/commands/rotateShapes/rotateShapes.spec.ts index ebae41f59..1471ce699 100644 --- a/packages/tldraw/src/state/commands/rotateShapes/rotateShapes.spec.ts +++ b/packages/tldraw/src/state/commands/rotateShapes/rotateShapes.spec.ts @@ -2,38 +2,38 @@ import { TLDrawState } from '~state' import { mockDocument } from '~test' describe('Rotate command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() beforeEach(() => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) }) describe('when no shape is selected', () => { it('does nothing', () => { - const initialState = tlstate.state - tlstate.rotate() - const currentState = tlstate.state + const initialState = state.state + state.rotate() + const currentState = state.state expect(currentState).toEqual(initialState) }) }) it('does, undoes and redoes command', () => { - tlstate.select('rect1') + state.select('rect1') - expect(tlstate.getShape('rect1').rotation).toBe(undefined) + expect(state.getShape('rect1').rotation).toBe(undefined) - tlstate.rotate() + state.rotate() - expect(tlstate.getShape('rect1').rotation).toBe(Math.PI * (6 / 4)) + expect(state.getShape('rect1').rotation).toBe(Math.PI * (6 / 4)) - tlstate.undo() + state.undo() - expect(tlstate.getShape('rect1').rotation).toBe(undefined) + expect(state.getShape('rect1').rotation).toBe(undefined) - tlstate.redo() + state.redo() - expect(tlstate.getShape('rect1').rotation).toBe(Math.PI * (6 / 4)) + expect(state.getShape('rect1').rotation).toBe(Math.PI * (6 / 4)) }) it.todo('Rotates several shapes at once.') @@ -43,17 +43,17 @@ describe('Rotate command', () => { describe('when running the command', () => { it('restores selection on undo', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .select('rect1') .rotate() .selectNone() .undo() - expect(tlstate.selectedIds).toEqual(['rect1']) + expect(state.selectedIds).toEqual(['rect1']) - tlstate.selectNone().redo() + state.selectNone().redo() - expect(tlstate.selectedIds).toEqual(['rect1']) + expect(state.selectedIds).toEqual(['rect1']) }) }) diff --git a/packages/tldraw/src/state/commands/rotateShapes/rotateShapes.ts b/packages/tldraw/src/state/commands/rotateShapes/rotateShapes.ts index d76b98216..a9971f497 100644 --- a/packages/tldraw/src/state/commands/rotateShapes/rotateShapes.ts +++ b/packages/tldraw/src/state/commands/rotateShapes/rotateShapes.ts @@ -1,10 +1,14 @@ import { Utils } from '@tldraw/core' -import type { TLDrawCommand, Data, TLDrawShape } from '~types' +import type { TLDrawCommand, TLDrawSnapshot, TLDrawShape } from '~types' import { TLDR } from '~state/TLDR' const PI2 = Math.PI * 2 -export function rotateShapes(data: Data, ids: string[], delta = -PI2 / 4): TLDrawCommand | void { +export function rotateShapes( + data: TLDrawSnapshot, + ids: string[], + delta = -PI2 / 4 +): TLDrawCommand | void { const { currentPageId } = data.appState // The shapes for the before patch diff --git a/packages/tldraw/src/state/commands/shared/removeShapesFromPage.ts b/packages/tldraw/src/state/commands/shared/removeShapesFromPage.ts index 90fe2263a..7a53bfed3 100644 --- a/packages/tldraw/src/state/commands/shared/removeShapesFromPage.ts +++ b/packages/tldraw/src/state/commands/shared/removeShapesFromPage.ts @@ -1,7 +1,7 @@ import { TLDR } from '~state/TLDR' -import type { ArrowShape, Data, GroupShape, PagePartial } from '~types' +import type { ArrowShape, TLDrawSnapshot, GroupShape, PagePartial } from '~types' -export function removeShapesFromPage(data: Data, ids: string[], pageId: string) { +export function removeShapesFromPage(data: TLDrawSnapshot, ids: string[], pageId: string) { const before: PagePartial = { shapes: {}, bindings: {}, diff --git a/packages/tldraw/src/state/commands/stretchShapes/stretchShapes.spec.ts b/packages/tldraw/src/state/commands/stretchShapes/stretchShapes.spec.ts index f1a67e1d0..891d9ef66 100644 --- a/packages/tldraw/src/state/commands/stretchShapes/stretchShapes.spec.ts +++ b/packages/tldraw/src/state/commands/stretchShapes/stretchShapes.spec.ts @@ -4,88 +4,88 @@ import { mockDocument, TLDrawStateUtils } from '~test' import Vec from '@tldraw/vec' describe('Stretch command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() beforeEach(() => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) }) describe('when less than two shapes are selected', () => { it('does nothing', () => { - tlstate.select('rect2') - const initialState = tlstate.state - tlstate.stretch(StretchType.Horizontal) - const currentState = tlstate.state + state.select('rect2') + const initialState = state.state + state.stretch(StretchType.Horizontal) + const currentState = state.state expect(currentState).toEqual(initialState) }) }) it('does, undoes and redoes command', () => { - tlstate.select('rect1', 'rect2') - tlstate.stretch(StretchType.Horizontal) + state.select('rect1', 'rect2') + state.stretch(StretchType.Horizontal) - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) - expect(tlstate.getShape('rect1').size).toStrictEqual([200, 100]) - expect(tlstate.getShape('rect2').point).toStrictEqual([0, 100]) - expect(tlstate.getShape('rect2').size).toStrictEqual([200, 100]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect1').size).toStrictEqual([200, 100]) + expect(state.getShape('rect2').point).toStrictEqual([0, 100]) + expect(state.getShape('rect2').size).toStrictEqual([200, 100]) - tlstate.undo() + state.undo() - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) - expect(tlstate.getShape('rect1').size).toStrictEqual([100, 100]) - expect(tlstate.getShape('rect2').point).toStrictEqual([100, 100]) - expect(tlstate.getShape('rect2').size).toStrictEqual([100, 100]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect1').size).toStrictEqual([100, 100]) + expect(state.getShape('rect2').point).toStrictEqual([100, 100]) + expect(state.getShape('rect2').size).toStrictEqual([100, 100]) - tlstate.redo() + state.redo() - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) - expect(tlstate.getShape('rect1').size).toStrictEqual([200, 100]) - expect(tlstate.getShape('rect2').point).toStrictEqual([0, 100]) - expect(tlstate.getShape('rect2').size).toStrictEqual([200, 100]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect1').size).toStrictEqual([200, 100]) + expect(state.getShape('rect2').point).toStrictEqual([0, 100]) + expect(state.getShape('rect2').size).toStrictEqual([200, 100]) }) it('stretches horizontally', () => { - tlstate.select('rect1', 'rect2') - tlstate.stretch(StretchType.Horizontal) + state.select('rect1', 'rect2') + state.stretch(StretchType.Horizontal) - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) - expect(tlstate.getShape('rect1').size).toStrictEqual([200, 100]) - expect(tlstate.getShape('rect2').point).toStrictEqual([0, 100]) - expect(tlstate.getShape('rect2').size).toStrictEqual([200, 100]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect1').size).toStrictEqual([200, 100]) + expect(state.getShape('rect2').point).toStrictEqual([0, 100]) + expect(state.getShape('rect2').size).toStrictEqual([200, 100]) }) it('stretches vertically', () => { - tlstate.select('rect1', 'rect2') - tlstate.stretch(StretchType.Vertical) + state.select('rect1', 'rect2') + state.stretch(StretchType.Vertical) - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) - expect(tlstate.getShape('rect1').size).toStrictEqual([100, 200]) - expect(tlstate.getShape('rect2').point).toStrictEqual([100, 0]) - expect(tlstate.getShape('rect2').size).toStrictEqual([100, 200]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect1').size).toStrictEqual([100, 200]) + expect(state.getShape('rect2').point).toStrictEqual([100, 0]) + expect(state.getShape('rect2').size).toStrictEqual([100, 200]) }) }) describe('when running the command', () => { it('restores selection on undo', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .select('rect1', 'rect2') .stretch(StretchType.Horizontal) .selectNone() .undo() - expect(tlstate.selectedIds).toEqual(['rect1', 'rect2']) + expect(state.selectedIds).toEqual(['rect1', 'rect2']) - tlstate.selectNone().redo() + state.selectNone().redo() - expect(tlstate.selectedIds).toEqual(['rect1', 'rect2']) + expect(state.selectedIds).toEqual(['rect1', 'rect2']) }) }) describe('when stretching groups', () => { it('stretches children', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .createShapes( { id: 'rect1', type: TLDrawShapeType.Rectangle, point: [0, 0], size: [100, 100] }, { id: 'rect2', type: TLDrawShapeType.Rectangle, point: [100, 100], size: [100, 100] }, @@ -95,7 +95,7 @@ describe('when stretching groups', () => { .selectAll() .stretch(StretchType.Vertical) - new TLDrawStateUtils(tlstate).expectShapesToHaveProps({ + new TLDrawStateUtils(state).expectShapesToHaveProps({ rect1: { point: [0, 0], size: [100, 300] }, rect2: { point: [100, 0], size: [100, 300] }, rect3: { point: [200, 0], size: [100, 300] }, diff --git a/packages/tldraw/src/state/commands/stretchShapes/stretchShapes.ts b/packages/tldraw/src/state/commands/stretchShapes/stretchShapes.ts index a5713034b..1221d9ea1 100644 --- a/packages/tldraw/src/state/commands/stretchShapes/stretchShapes.ts +++ b/packages/tldraw/src/state/commands/stretchShapes/stretchShapes.ts @@ -1,10 +1,14 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { TLBoundsCorner, Utils } from '@tldraw/core' import { StretchType, TLDrawShapeType } from '~types' -import type { Data, TLDrawCommand } from '~types' +import type { TLDrawSnapshot, TLDrawCommand } from '~types' import { TLDR } from '~state/TLDR' -export function stretchShapes(data: Data, ids: string[], type: StretchType): TLDrawCommand { +export function stretchShapes( + data: TLDrawSnapshot, + ids: string[], + type: StretchType +): TLDrawCommand { const { currentPageId } = data.appState const initialShapes = ids.map((id) => TLDR.getShape(data, id, currentPageId)) diff --git a/packages/tldraw/src/state/commands/styleShapes/styleShapes.spec.ts b/packages/tldraw/src/state/commands/styleShapes/styleShapes.spec.ts index b3298dce1..eae919601 100644 --- a/packages/tldraw/src/state/commands/styleShapes/styleShapes.spec.ts +++ b/packages/tldraw/src/state/commands/styleShapes/styleShapes.spec.ts @@ -5,69 +5,69 @@ import { SizeStyle, TLDrawShapeType } from '~types' describe('Style command', () => { it('does, undoes and redoes command', () => { - const tlstate = new TLDrawState() - tlstate.loadDocument(mockDocument) - tlstate.select('rect1') - expect(tlstate.getShape('rect1').style.size).toEqual(SizeStyle.Medium) + const state = new TLDrawState() + state.loadDocument(mockDocument) + state.select('rect1') + expect(state.getShape('rect1').style.size).toEqual(SizeStyle.Medium) - tlstate.style({ size: SizeStyle.Small }) + state.style({ size: SizeStyle.Small }) - expect(tlstate.getShape('rect1').style.size).toEqual(SizeStyle.Small) + expect(state.getShape('rect1').style.size).toEqual(SizeStyle.Small) - tlstate.undo() + state.undo() - expect(tlstate.getShape('rect1').style.size).toEqual(SizeStyle.Medium) + expect(state.getShape('rect1').style.size).toEqual(SizeStyle.Medium) - tlstate.redo() + state.redo() - expect(tlstate.getShape('rect1').style.size).toEqual(SizeStyle.Small) + expect(state.getShape('rect1').style.size).toEqual(SizeStyle.Small) }) describe('When styling groups', () => { it('applies style to all group children', () => { - const tlstate = new TLDrawState() - tlstate + const state = new TLDrawState() + state .loadDocument(mockDocument) .group(['rect1', 'rect2'], 'groupA') .select('groupA') .style({ size: SizeStyle.Small }) - expect(tlstate.getShape('rect1').style.size).toEqual(SizeStyle.Small) - expect(tlstate.getShape('rect2').style.size).toEqual(SizeStyle.Small) + expect(state.getShape('rect1').style.size).toEqual(SizeStyle.Small) + expect(state.getShape('rect2').style.size).toEqual(SizeStyle.Small) - tlstate.undo() + state.undo() - expect(tlstate.getShape('rect1').style.size).toEqual(SizeStyle.Medium) - expect(tlstate.getShape('rect2').style.size).toEqual(SizeStyle.Medium) + expect(state.getShape('rect1').style.size).toEqual(SizeStyle.Medium) + expect(state.getShape('rect2').style.size).toEqual(SizeStyle.Medium) - tlstate.redo() + state.redo() - expect(tlstate.getShape('rect1').style.size).toEqual(SizeStyle.Small) - expect(tlstate.getShape('rect2').style.size).toEqual(SizeStyle.Small) + expect(state.getShape('rect1').style.size).toEqual(SizeStyle.Small) + expect(state.getShape('rect2').style.size).toEqual(SizeStyle.Small) }) }) describe('When styling text', () => { it('recenters the shape if the size changed', () => { - const tlstate = new TLDrawState().createShapes({ + const state = new TLDrawState().createShapes({ id: 'text1', type: TLDrawShapeType.Text, text: 'Hello world', }) - const centerA = TLDR.getShapeUtils(TLDrawShapeType.Text).getCenter(tlstate.getShape('text1')) + const centerA = TLDR.getShapeUtils(TLDrawShapeType.Text).getCenter(state.getShape('text1')) - tlstate.select('text1').style({ size: SizeStyle.Large }) + state.select('text1').style({ size: SizeStyle.Large }) - const centerB = TLDR.getShapeUtils(TLDrawShapeType.Text).getCenter(tlstate.getShape('text1')) + const centerB = TLDR.getShapeUtils(TLDrawShapeType.Text).getCenter(state.getShape('text1')) - tlstate.style({ size: SizeStyle.Small }) + state.style({ size: SizeStyle.Small }) - const centerC = TLDR.getShapeUtils(TLDrawShapeType.Text).getCenter(tlstate.getShape('text1')) + const centerC = TLDR.getShapeUtils(TLDrawShapeType.Text).getCenter(state.getShape('text1')) - tlstate.style({ size: SizeStyle.Medium }) + state.style({ size: SizeStyle.Medium }) - const centerD = TLDR.getShapeUtils(TLDrawShapeType.Text).getCenter(tlstate.getShape('text1')) + const centerD = TLDR.getShapeUtils(TLDrawShapeType.Text).getCenter(state.getShape('text1')) expect(centerA).toEqual(centerB) expect(centerA).toEqual(centerC) @@ -78,17 +78,17 @@ describe('Style command', () => { describe('when running the command', () => { it('restores selection on undo', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .select('rect1') .style({ size: SizeStyle.Small }) .selectNone() .undo() - expect(tlstate.selectedIds).toEqual(['rect1']) + expect(state.selectedIds).toEqual(['rect1']) - tlstate.selectNone().redo() + state.selectNone().redo() - expect(tlstate.selectedIds).toEqual(['rect1']) + expect(state.selectedIds).toEqual(['rect1']) }) }) diff --git a/packages/tldraw/src/state/commands/styleShapes/styleShapes.ts b/packages/tldraw/src/state/commands/styleShapes/styleShapes.ts index 7d689d9bd..f64be5dcf 100644 --- a/packages/tldraw/src/state/commands/styleShapes/styleShapes.ts +++ b/packages/tldraw/src/state/commands/styleShapes/styleShapes.ts @@ -1,10 +1,17 @@ -import { ShapeStyles, TLDrawCommand, Data, TLDrawShape, TLDrawShapeType, TextShape } from '~types' +import { + ShapeStyles, + TLDrawCommand, + TLDrawSnapshot, + TLDrawShape, + TLDrawShapeType, + TextShape, +} from '~types' import { TLDR } from '~state/TLDR' import type { Patch } from 'rko' import Vec from '@tldraw/vec' export function styleShapes( - data: Data, + data: TLDrawSnapshot, ids: string[], changes: Partial ): TLDrawCommand { diff --git a/packages/tldraw/src/state/commands/toggleShapesDecoration/toggleShapesDecoration.spec.ts b/packages/tldraw/src/state/commands/toggleShapesDecoration/toggleShapesDecoration.spec.ts index 2ea7e50c1..bffb2eda7 100644 --- a/packages/tldraw/src/state/commands/toggleShapesDecoration/toggleShapesDecoration.spec.ts +++ b/packages/tldraw/src/state/commands/toggleShapesDecoration/toggleShapesDecoration.spec.ts @@ -4,17 +4,17 @@ import { mockDocument } from '~test' import { ArrowShape, Decoration, TLDrawShape, TLDrawShapeType } from '~types' describe('Toggle decoration command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() beforeEach(() => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) }) describe('when no shape is selected', () => { it('does nothing', () => { - const initialState = tlstate.state - tlstate.toggleDecoration('start') - const currentState = tlstate.state + const initialState = state.state + state.toggleDecoration('start') + const currentState = state.state expect(currentState).toEqual(initialState) }) @@ -22,34 +22,34 @@ describe('Toggle decoration command', () => { describe('when handle id is invalid', () => { it('does nothing', () => { - const initialState = tlstate.state - tlstate.toggleDecoration('invalid') - const currentState = tlstate.state + const initialState = state.state + state.toggleDecoration('invalid') + const currentState = state.state expect(currentState).toEqual(initialState) }) }) it('does, undoes and redoes command', () => { - tlstate + state .createShapes({ id: 'arrow1', type: TLDrawShapeType.Arrow, }) .select('arrow1') - expect(tlstate.getShape('arrow1').decorations?.end).toBe(Decoration.Arrow) + expect(state.getShape('arrow1').decorations?.end).toBe(Decoration.Arrow) - tlstate.toggleDecoration('end') + state.toggleDecoration('end') - expect(tlstate.getShape('arrow1').decorations?.end).toBe(undefined) + expect(state.getShape('arrow1').decorations?.end).toBe(undefined) - tlstate.undo() + state.undo() - expect(tlstate.getShape('arrow1').decorations?.end).toBe(Decoration.Arrow) + expect(state.getShape('arrow1').decorations?.end).toBe(Decoration.Arrow) - tlstate.redo() + state.redo() - expect(tlstate.getShape('arrow1').decorations?.end).toBe(undefined) + expect(state.getShape('arrow1').decorations?.end).toBe(undefined) }) }) diff --git a/packages/tldraw/src/state/commands/toggleShapesDecoration/toggleShapesDecoration.ts b/packages/tldraw/src/state/commands/toggleShapesDecoration/toggleShapesDecoration.ts index 3fe4cc897..0cc216b77 100644 --- a/packages/tldraw/src/state/commands/toggleShapesDecoration/toggleShapesDecoration.ts +++ b/packages/tldraw/src/state/commands/toggleShapesDecoration/toggleShapesDecoration.ts @@ -1,10 +1,10 @@ import { Decoration } from '~types' -import type { ArrowShape, TLDrawCommand, Data } from '~types' +import type { ArrowShape, TLDrawCommand, TLDrawSnapshot } from '~types' import { TLDR } from '~state/TLDR' import type { Patch } from 'rko' export function toggleShapesDecoration( - data: Data, + data: TLDrawSnapshot, ids: string[], decorationId: 'start' | 'end' ): TLDrawCommand { diff --git a/packages/tldraw/src/state/commands/toggleShapesProp/toggleShapesProp.spec.ts b/packages/tldraw/src/state/commands/toggleShapesProp/toggleShapesProp.spec.ts index 070f8a25d..9fcd40334 100644 --- a/packages/tldraw/src/state/commands/toggleShapesProp/toggleShapesProp.spec.ts +++ b/packages/tldraw/src/state/commands/toggleShapesProp/toggleShapesProp.spec.ts @@ -3,77 +3,77 @@ import { TLDrawState } from '~state' import { mockDocument } from '~test' describe('Toggle command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() beforeEach(() => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) }) describe('when no shape is selected', () => { it('does nothing', () => { - const initialState = tlstate.state - tlstate.toggleHidden() - const currentState = tlstate.state + const initialState = state.state + state.toggleHidden() + const currentState = state.state expect(currentState).toEqual(initialState) }) }) it('does, undoes and redoes command', () => { - tlstate.selectAll() + state.selectAll() - expect(tlstate.getShape('rect2').isLocked).toBe(undefined) + expect(state.getShape('rect2').isLocked).toBe(undefined) - tlstate.toggleLocked() + state.toggleLocked() - expect(tlstate.getShape('rect2').isLocked).toBe(true) + expect(state.getShape('rect2').isLocked).toBe(true) - tlstate.undo() + state.undo() - expect(tlstate.getShape('rect2').isLocked).toBe(undefined) + expect(state.getShape('rect2').isLocked).toBe(undefined) - tlstate.redo() + state.redo() - expect(tlstate.getShape('rect2').isLocked).toBe(true) + expect(state.getShape('rect2').isLocked).toBe(true) }) it('toggles on before off when mixed values', () => { - tlstate.select('rect2') + state.select('rect2') - expect(tlstate.getShape('rect1').isAspectRatioLocked).toBe(undefined) - expect(tlstate.getShape('rect2').isAspectRatioLocked).toBe(undefined) + expect(state.getShape('rect1').isAspectRatioLocked).toBe(undefined) + expect(state.getShape('rect2').isAspectRatioLocked).toBe(undefined) - tlstate.toggleAspectRatioLocked() + state.toggleAspectRatioLocked() - expect(tlstate.getShape('rect1').isAspectRatioLocked).toBe(undefined) - expect(tlstate.getShape('rect2').isAspectRatioLocked).toBe(true) + expect(state.getShape('rect1').isAspectRatioLocked).toBe(undefined) + expect(state.getShape('rect2').isAspectRatioLocked).toBe(true) - tlstate.selectAll() - tlstate.toggleAspectRatioLocked() + state.selectAll() + state.toggleAspectRatioLocked() - expect(tlstate.getShape('rect1').isAspectRatioLocked).toBe(true) - expect(tlstate.getShape('rect1').isAspectRatioLocked).toBe(true) + expect(state.getShape('rect1').isAspectRatioLocked).toBe(true) + expect(state.getShape('rect1').isAspectRatioLocked).toBe(true) - tlstate.toggleAspectRatioLocked() + state.toggleAspectRatioLocked() - expect(tlstate.getShape('rect1').isAspectRatioLocked).toBe(false) - expect(tlstate.getShape('rect1').isAspectRatioLocked).toBe(false) + expect(state.getShape('rect1').isAspectRatioLocked).toBe(false) + expect(state.getShape('rect1').isAspectRatioLocked).toBe(false) }) }) describe('when running the command', () => { it('restores selection on undo', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .select('rect1') .toggleHidden() .selectNone() .undo() - expect(tlstate.selectedIds).toEqual(['rect1']) + expect(state.selectedIds).toEqual(['rect1']) - tlstate.selectNone().redo() + state.selectNone().redo() - expect(tlstate.selectedIds).toEqual(['rect1']) + expect(state.selectedIds).toEqual(['rect1']) }) }) diff --git a/packages/tldraw/src/state/commands/toggleShapesProp/toggleShapesProp.ts b/packages/tldraw/src/state/commands/toggleShapesProp/toggleShapesProp.ts index 1d72f5b62..b1ae4c5ea 100644 --- a/packages/tldraw/src/state/commands/toggleShapesProp/toggleShapesProp.ts +++ b/packages/tldraw/src/state/commands/toggleShapesProp/toggleShapesProp.ts @@ -1,7 +1,11 @@ -import type { TLDrawShape, Data, TLDrawCommand } from '~types' +import type { TLDrawShape, TLDrawSnapshot, TLDrawCommand } from '~types' import { TLDR } from '~state/TLDR' -export function toggleShapeProp(data: Data, ids: string[], prop: keyof TLDrawShape): TLDrawCommand { +export function toggleShapeProp( + data: TLDrawSnapshot, + ids: string[], + prop: keyof TLDrawShape +): TLDrawCommand { const { currentPageId } = data.appState const initialShapes = ids.map((id) => TLDR.getShape(data, id, currentPageId)) diff --git a/packages/tldraw/src/state/commands/translateShapes/translateShapes.spec.ts b/packages/tldraw/src/state/commands/translateShapes/translateShapes.spec.ts index a5e7ccbd6..9c31bab4f 100644 --- a/packages/tldraw/src/state/commands/translateShapes/translateShapes.spec.ts +++ b/packages/tldraw/src/state/commands/translateShapes/translateShapes.spec.ts @@ -4,46 +4,46 @@ import { mockDocument, TLDrawStateUtils } from '~test' import { ArrowShape, SessionType, TLDrawShapeType } from '~types' describe('Translate command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() beforeEach(() => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) }) describe('when no shape is selected', () => { it('does nothing', () => { - const initialState = tlstate.state - tlstate.nudge([1, 2]) - const currentState = tlstate.state + const initialState = state.state + state.nudge([1, 2]) + const currentState = state.state expect(currentState).toEqual(initialState) }) }) it('does, undoes and redoes command', () => { - tlstate.selectAll() - tlstate.nudge([1, 2]) + state.selectAll() + state.nudge([1, 2]) - expect(tlstate.getShape('rect2').point).toEqual([101, 102]) + expect(state.getShape('rect2').point).toEqual([101, 102]) - tlstate.undo() + state.undo() - expect(tlstate.getShape('rect2').point).toEqual([100, 100]) + expect(state.getShape('rect2').point).toEqual([100, 100]) - tlstate.redo() + state.redo() - expect(tlstate.getShape('rect2').point).toEqual([101, 102]) + expect(state.getShape('rect2').point).toEqual([101, 102]) }) it('major nudges', () => { - tlstate.selectAll() - tlstate.nudge([1, 2], true) - expect(tlstate.getShape('rect2').point).toEqual([110, 120]) + state.selectAll() + state.nudge([1, 2], true) + expect(state.getShape('rect2').point).toEqual([110, 120]) }) describe('when nudging shapes with bindings', () => { it('deleted bindings if nudging shape is bound to other shapes', () => { - tlstate + state .resetDocument() .createShapes( { @@ -63,26 +63,26 @@ describe('Translate command', () => { .updateSession([50, 50]) .completeSession() - const bindingId = tlstate.getShape('arrow1').handles.start.bindingId! + const bindingId = state.getShape('arrow1').handles.start.bindingId! - tlstate.select('arrow1').nudge([10, 10]) + state.select('arrow1').nudge([10, 10]) - expect(tlstate.getBinding(bindingId)).toBeUndefined() - expect(tlstate.getShape('arrow1').handles.start.bindingId).toBeUndefined() + expect(state.getBinding(bindingId)).toBeUndefined() + expect(state.getShape('arrow1').handles.start.bindingId).toBeUndefined() - tlstate.undo() + state.undo() - expect(tlstate.getBinding(bindingId)).toBeDefined() - expect(tlstate.getShape('arrow1').handles.start.bindingId).toBe(bindingId) + expect(state.getBinding(bindingId)).toBeDefined() + expect(state.getShape('arrow1').handles.start.bindingId).toBe(bindingId) - tlstate.redo() + state.redo() - expect(tlstate.getBinding(bindingId)).toBeUndefined() - expect(tlstate.getShape('arrow1').handles.start.bindingId).toBeUndefined() + expect(state.getBinding(bindingId)).toBeUndefined() + expect(state.getShape('arrow1').handles.start.bindingId).toBeUndefined() }) it('does not delete bindings if both bound and bound-to shapes are nudged', () => { - tlstate + state .resetDocument() .createShapes( { @@ -102,34 +102,34 @@ describe('Translate command', () => { .updateSession([50, 50]) .completeSession() - const bindingId = tlstate.getShape('arrow1').handles.start.bindingId! + const bindingId = state.getShape('arrow1').handles.start.bindingId! - tlstate.select('arrow1', 'target1').nudge([10, 10]) + state.select('arrow1', 'target1').nudge([10, 10]) - expect(tlstate.getBinding(bindingId)).toBeDefined() - expect(tlstate.getShape('arrow1').handles.start.bindingId).toBe(bindingId) + expect(state.getBinding(bindingId)).toBeDefined() + expect(state.getShape('arrow1').handles.start.bindingId).toBe(bindingId) - tlstate.undo() + state.undo() - expect(tlstate.getBinding(bindingId)).toBeDefined() - expect(tlstate.getShape('arrow1').handles.start.bindingId).toBe(bindingId) + expect(state.getBinding(bindingId)).toBeDefined() + expect(state.getShape('arrow1').handles.start.bindingId).toBe(bindingId) - tlstate.redo() + state.redo() - expect(tlstate.getBinding(bindingId)).toBeDefined() - expect(tlstate.getShape('arrow1').handles.start.bindingId).toBe(bindingId) + expect(state.getBinding(bindingId)).toBeDefined() + expect(state.getShape('arrow1').handles.start.bindingId).toBe(bindingId) }) }) }) describe('When nudging groups', () => { it('nudges children instead', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .group(['rect1', 'rect2'], 'groupA') .nudge([1, 1]) - new TLDrawStateUtils(tlstate).expectShapesToBeAtPoints({ + new TLDrawStateUtils(state).expectShapesToBeAtPoints({ rect1: [1, 1], rect2: [101, 101], }) diff --git a/packages/tldraw/src/state/commands/translateShapes/translateShapes.ts b/packages/tldraw/src/state/commands/translateShapes/translateShapes.ts index 3f1afffd7..9f858e78a 100644 --- a/packages/tldraw/src/state/commands/translateShapes/translateShapes.ts +++ b/packages/tldraw/src/state/commands/translateShapes/translateShapes.ts @@ -1,8 +1,12 @@ import { Vec } from '@tldraw/vec' -import { Data, TLDrawCommand, PagePartial, Session } from '~types' +import { TLDrawSnapshot, TLDrawCommand, PagePartial, Session } from '~types' import { TLDR } from '~state/TLDR' -export function translateShapes(data: Data, ids: string[], delta: number[]): TLDrawCommand { +export function translateShapes( + data: TLDrawSnapshot, + ids: string[], + delta: number[] +): TLDrawCommand { const { currentPageId } = data.appState // Clear session cache diff --git a/packages/tldraw/src/state/commands/ungroupShapes/ungroupShapes.spec.ts b/packages/tldraw/src/state/commands/ungroupShapes/ungroupShapes.spec.ts index 5697e84e3..3e7062154 100644 --- a/packages/tldraw/src/state/commands/ungroupShapes/ungroupShapes.spec.ts +++ b/packages/tldraw/src/state/commands/ungroupShapes/ungroupShapes.spec.ts @@ -4,48 +4,44 @@ import { mockDocument } from '~test' import { GroupShape, TLDrawShapeType } from '~types' describe('Ungroup command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() it('does, undoes and redoes command', () => { - tlstate - .loadDocument(mockDocument) - .group(['rect1', 'rect2'], 'groupA') - .select('groupA') - .ungroup() + state.loadDocument(mockDocument).group(['rect1', 'rect2'], 'groupA').select('groupA').ungroup() - expect(tlstate.getShape('groupA')).toBeUndefined() - expect(tlstate.getShape('rect1').parentId).toBe('page1') - expect(tlstate.getShape('rect2').parentId).toBe('page1') + expect(state.getShape('groupA')).toBeUndefined() + expect(state.getShape('rect1').parentId).toBe('page1') + expect(state.getShape('rect2').parentId).toBe('page1') - tlstate.undo() + state.undo() - expect(tlstate.getShape('groupA')).toBeDefined() - expect(tlstate.getShape('groupA').children).toStrictEqual(['rect1', 'rect2']) - expect(tlstate.getShape('rect1').parentId).toBe('groupA') - expect(tlstate.getShape('rect2').parentId).toBe('groupA') + expect(state.getShape('groupA')).toBeDefined() + expect(state.getShape('groupA').children).toStrictEqual(['rect1', 'rect2']) + expect(state.getShape('rect1').parentId).toBe('groupA') + expect(state.getShape('rect2').parentId).toBe('groupA') - tlstate.redo() + state.redo() - expect(tlstate.getShape('groupA')).toBeUndefined() - expect(tlstate.getShape('rect1').parentId).toBe('page1') - expect(tlstate.getShape('rect2').parentId).toBe('page1') + expect(state.getShape('groupA')).toBeUndefined() + expect(state.getShape('rect1').parentId).toBe('page1') + expect(state.getShape('rect2').parentId).toBe('page1') }) describe('When ungrouping', () => { it('Ungroups shapes on any page', () => { - tlstate + state .loadDocument(mockDocument) .group(['rect1', 'rect2'], 'groupA') .createPage('page2') .ungroup(['groupA'], 'page1') - expect(tlstate.getShape('groupA', 'page1')).toBeUndefined() - tlstate.undo() - expect(tlstate.getShape('groupA', 'page1')).toBeDefined() + expect(state.getShape('groupA', 'page1')).toBeUndefined() + state.undo() + expect(state.getShape('groupA', 'page1')).toBeDefined() }) it('Ungroups multiple selected groups', () => { - tlstate + state .loadDocument(mockDocument) .createShapes({ id: 'rect4', @@ -56,20 +52,20 @@ describe('Ungroup command', () => { .selectAll() .ungroup() - expect(tlstate.getShape('groupA', 'page1')).toBeUndefined() - expect(tlstate.getShape('groupB', 'page1')).toBeUndefined() + expect(state.getShape('groupA', 'page1')).toBeUndefined() + expect(state.getShape('groupB', 'page1')).toBeUndefined() }) it('Does not ungroup if a group shape is not selected', () => { - tlstate.loadDocument(mockDocument).select('rect1') - const before = tlstate.state - tlstate.group() + state.loadDocument(mockDocument).select('rect1') + const before = state.state + state.group() // State should not have changed - expect(tlstate.state).toStrictEqual(before) + expect(state.state).toStrictEqual(before) }) it('Correctly selects children after ungrouping', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .createShapes( { id: 'rect1', @@ -92,11 +88,11 @@ describe('Ungroup command', () => { .ungroup() // State should not have changed - expect(tlstate.selectedIds).toStrictEqual(['rect3', 'rect1', 'rect2']) + expect(state.selectedIds).toStrictEqual(['rect3', 'rect1', 'rect2']) }) it('Reparents shapes to the page at the correct childIndex', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .createShapes( { id: 'rect1', @@ -116,18 +112,18 @@ describe('Ungroup command', () => { ) .group(['rect1', 'rect2'], 'groupA') - const { childIndex } = tlstate.getShape('groupA') + const { childIndex } = state.getShape('groupA') expect(childIndex).toBe(1) - expect(tlstate.getShape('rect1').childIndex).toBe(1) - expect(tlstate.getShape('rect2').childIndex).toBe(2) - expect(tlstate.getShape('rect3').childIndex).toBe(3) + expect(state.getShape('rect1').childIndex).toBe(1) + expect(state.getShape('rect2').childIndex).toBe(2) + expect(state.getShape('rect3').childIndex).toBe(3) - tlstate.ungroup() + state.ungroup() - expect(tlstate.getShape('rect1').childIndex).toBe(1) - expect(tlstate.getShape('rect2').childIndex).toBe(2) - expect(tlstate.getShape('rect3').childIndex).toBe(3) + expect(state.getShape('rect1').childIndex).toBe(1) + expect(state.getShape('rect2').childIndex).toBe(2) + expect(state.getShape('rect3').childIndex).toBe(3) }) it.todo('Deletes any bindings to the group') }) diff --git a/packages/tldraw/src/state/commands/ungroupShapes/ungroupShapes.ts b/packages/tldraw/src/state/commands/ungroupShapes/ungroupShapes.ts index f8cef5202..f4ebb7414 100644 --- a/packages/tldraw/src/state/commands/ungroupShapes/ungroupShapes.ts +++ b/packages/tldraw/src/state/commands/ungroupShapes/ungroupShapes.ts @@ -1,10 +1,10 @@ import type { GroupShape, TLDrawBinding, TLDrawShape } from '~types' -import type { Data, TLDrawCommand } from '~types' +import type { TLDrawSnapshot, TLDrawCommand } from '~types' import { TLDR } from '~state/TLDR' import type { Patch } from 'rko' export function ungroupShapes( - data: Data, + data: TLDrawSnapshot, selectedIds: string[], groupShapes: GroupShape[], pageId: string diff --git a/packages/tldraw/src/state/commands/updateShapes/updateShapes.spec.ts b/packages/tldraw/src/state/commands/updateShapes/updateShapes.spec.ts index ef52dda3b..c51ab8f07 100644 --- a/packages/tldraw/src/state/commands/updateShapes/updateShapes.spec.ts +++ b/packages/tldraw/src/state/commands/updateShapes/updateShapes.spec.ts @@ -2,33 +2,33 @@ import { TLDrawState } from '~state' import { mockDocument } from '~test' describe('Update command', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() beforeEach(() => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) }) describe('when no shape is selected', () => { it('does nothing', () => { - const initialState = tlstate.state - tlstate.updateShapes() - const currentState = tlstate.state + const initialState = state.state + state.updateShapes() + const currentState = state.state expect(currentState).toEqual(initialState) }) }) it('does, undoes and redoes command', () => { - tlstate.updateShapes({ id: 'rect1', point: [100, 100] }) + state.updateShapes({ id: 'rect1', point: [100, 100] }) - expect(tlstate.getShape('rect1').point).toStrictEqual([100, 100]) + expect(state.getShape('rect1').point).toStrictEqual([100, 100]) - tlstate.undo() + state.undo() - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) - tlstate.redo() + state.redo() - expect(tlstate.getShape('rect1').point).toStrictEqual([100, 100]) + expect(state.getShape('rect1').point).toStrictEqual([100, 100]) }) }) diff --git a/packages/tldraw/src/state/commands/updateShapes/updateShapes.ts b/packages/tldraw/src/state/commands/updateShapes/updateShapes.ts index c51ff82f6..129e4bdc7 100644 --- a/packages/tldraw/src/state/commands/updateShapes/updateShapes.ts +++ b/packages/tldraw/src/state/commands/updateShapes/updateShapes.ts @@ -1,8 +1,8 @@ -import type { Data, TLDrawCommand, PagePartial, TLDrawShape } from '~types' +import type { TLDrawSnapshot, TLDrawCommand, PagePartial, TLDrawShape } from '~types' import { TLDR } from '~state/TLDR' export function update( - data: Data, + data: TLDrawSnapshot, updates: ({ id: string } & Partial)[], pageId: string ): TLDrawCommand { diff --git a/packages/tldraw/src/state/data/migrate.spec.ts b/packages/tldraw/src/state/data/migrate.spec.ts index 7c693fba0..814358ff7 100644 --- a/packages/tldraw/src/state/data/migrate.spec.ts +++ b/packages/tldraw/src/state/data/migrate.spec.ts @@ -9,7 +9,7 @@ describe('When migrating bindings', () => { }) it('migrates a document with an older version', () => { - const tlstate = new TLDrawState().loadDocument(oldDoc2 as unknown as TLDrawDocument) - expect(tlstate.getShape('d7ab0a49-3cb3-43ae-3d83-f5cf2f4a510a').style.color).toBe('black') + const state = new TLDrawState().loadDocument(oldDoc2 as unknown as TLDrawDocument) + expect(state.getShape('d7ab0a49-3cb3-43ae-3d83-f5cf2f4a510a').style.color).toBe('black') }) }) diff --git a/packages/tldraw/src/state/sessions/ArrowSession/ArrowSession.spec.ts b/packages/tldraw/src/state/sessions/ArrowSession/ArrowSession.spec.ts index fe8c8d287..09f038075 100644 --- a/packages/tldraw/src/state/sessions/ArrowSession/ArrowSession.spec.ts +++ b/packages/tldraw/src/state/sessions/ArrowSession/ArrowSession.spec.ts @@ -13,129 +13,129 @@ describe('Arrow session', () => { ).document it('begins, updateSession', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(restoreDoc) .select('arrow1') .startSession(SessionType.Arrow, [200, 200], 'start') .updateSession([50, 50]) .completeSession() - const binding = tlstate.bindings[0] + const binding = state.bindings[0] expect(binding).toBeTruthy() expect(binding.fromId).toBe('arrow1') expect(binding.toId).toBe('target1') expect(binding.handleId).toBe('start') - expect(tlstate.appState.status).toBe(TLDrawStatus.Idle) - expect(tlstate.getShape('arrow1').handles?.start.bindingId).toBe(binding.id) + expect(state.appState.status).toBe(TLDrawStatus.Idle) + expect(state.getShape('arrow1').handles?.start.bindingId).toBe(binding.id) - tlstate.undo() + state.undo() - expect(tlstate.bindings[0]).toBe(undefined) - expect(tlstate.getShape('arrow1').handles?.start.bindingId).toBe(undefined) + expect(state.bindings[0]).toBe(undefined) + expect(state.getShape('arrow1').handles?.start.bindingId).toBe(undefined) - tlstate.redo() + state.redo() - expect(tlstate.bindings[0]).toBeTruthy() - expect(tlstate.getShape('arrow1').handles?.start.bindingId).toBe(binding.id) + expect(state.bindings[0]).toBeTruthy() + expect(state.getShape('arrow1').handles?.start.bindingId).toBe(binding.id) }) it('cancels session', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(restoreDoc) .select('arrow1') .startSession(SessionType.Arrow, [200, 200], 'start') .updateSession([50, 50]) .cancelSession() - expect(tlstate.bindings[0]).toBe(undefined) - expect(tlstate.getShape('arrow1').handles?.start.bindingId).toBe(undefined) + expect(state.bindings[0]).toBe(undefined) + expect(state.getShape('arrow1').handles?.start.bindingId).toBe(undefined) }) describe('arrow binding', () => { it('points to the center', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(restoreDoc) .select('arrow1') .startSession(SessionType.Arrow, [200, 200], 'start') .updateSession([50, 50]) - expect(tlstate.bindings[0].point).toStrictEqual([0.5, 0.5]) + expect(state.bindings[0].point).toStrictEqual([0.5, 0.5]) }) it('Snaps to the center', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(restoreDoc) .select('arrow1') .startSession(SessionType.Arrow, [200, 200], 'start') .updateSession([55, 55]) - expect(tlstate.bindings[0].point).toStrictEqual([0.5, 0.5]) + expect(state.bindings[0].point).toStrictEqual([0.5, 0.5]) }) it('Binds at the bottom left', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(restoreDoc) .select('arrow1') .startSession(SessionType.Arrow, [200, 200], 'start') .updateSession([124, -24]) - expect(tlstate.bindings[0].point).toStrictEqual([1, 0]) + expect(state.bindings[0].point).toStrictEqual([1, 0]) }) it('Cancels the bind when off of the expanded bounds', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(restoreDoc) .select('arrow1') .startSession(SessionType.Arrow, [200, 200], 'start') .updateSession([133, 133]) - expect(tlstate.bindings[0]).toBe(undefined) + expect(state.bindings[0]).toBe(undefined) }) it('binds on the inside of a shape while alt is held', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(restoreDoc) .select('arrow1') .startSession(SessionType.Arrow, [200, 200], 'start') .updateSession([91, 9]) - expect(tlstate.bindings[0].point).toStrictEqual([0.71, 0.11]) + expect(state.bindings[0].point).toStrictEqual([0.71, 0.11]) - tlstate.updateSession([91, 9], false, true, false) + state.updateSession([91, 9], false, true, false) }) it('snaps to the inside center when the point is close to the center', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(restoreDoc) .select('arrow1') .startSession(SessionType.Arrow, [200, 200], 'start') .updateSession([91, 9], false, true, false) - expect(tlstate.bindings[0].point).toStrictEqual([0.78, 0.22]) + expect(state.bindings[0].point).toStrictEqual([0.78, 0.22]) }) it('ignores binding when meta is held', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(restoreDoc) .select('arrow1') .startSession(SessionType.Arrow, [200, 200], 'start') .updateSession([55, 45], false, false, true) - expect(tlstate.bindings.length).toBe(0) + expect(state.bindings.length).toBe(0) }) }) describe('when dragging a bound shape', () => { it('updates the arrow', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() - tlstate.loadDocument(restoreDoc) + state.loadDocument(restoreDoc) // Select the arrow and begin a session on the handle's start handle - tlstate.select('arrow1').startSession(SessionType.Arrow, [200, 200], 'start') + state.select('arrow1').startSession(SessionType.Arrow, [200, 200], 'start') // Move to [50,50] - tlstate.updateSession([50, 50]) + state.updateSession([50, 50]) // Both handles will keep the same screen positions, but their points will have changed. - expect(tlstate.getShape('arrow1').point).toStrictEqual([116, 116]) - expect(tlstate.getShape('arrow1').handles.start.point).toStrictEqual([0, 0]) - expect(tlstate.getShape('arrow1').handles.end.point).toStrictEqual([85, 85]) + expect(state.getShape('arrow1').point).toStrictEqual([116, 116]) + expect(state.getShape('arrow1').handles.start.point).toStrictEqual([0, 0]) + expect(state.getShape('arrow1').handles.end.point).toStrictEqual([85, 85]) }) it.todo('updates the arrow when bound on both sides') @@ -146,7 +146,7 @@ describe('Arrow session', () => { describe('When creating with an arrow session', () => { it('Deletes the shape on undo', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .createShapes({ type: TLDrawShapeType.Arrow, id: 'arrow1', point: [200, 200] }) .select('arrow1') .startSession(SessionType.Arrow, [200, 200], 'start', true) @@ -154,11 +154,11 @@ describe('When creating with an arrow session', () => { .completeSession() .undo() - expect(tlstate.getShape('arrow1')).toBe(undefined) + expect(state.getShape('arrow1')).toBe(undefined) }) it("Doesn't corrupt a shape after undoing", () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .createShapes( { type: TLDrawShapeType.Rectangle, id: 'rect1', point: [200, 200], size: [200, 200] }, { type: TLDrawShapeType.Rectangle, id: 'rect2', point: [400, 200], size: [200, 200] }, @@ -169,20 +169,20 @@ describe('When creating with an arrow session', () => { .updateSession([55, 45]) .completeSession() - expect(tlstate.bindings.length).toBe(2) + expect(state.bindings.length).toBe(2) - tlstate + state .undo() .select('rect1') .startSession(SessionType.Translate, [250, 250]) .updateSession([275, 275]) .completeSession() - expect(tlstate.bindings.length).toBe(0) + expect(state.bindings.length).toBe(0) }) it('Creates a start binding if possible', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .createShapes( { type: TLDrawShapeType.Rectangle, id: 'rect1', point: [200, 200], size: [200, 200] }, { type: TLDrawShapeType.Rectangle, id: 'rect2', point: [400, 200], size: [200, 200] }, @@ -193,18 +193,18 @@ describe('When creating with an arrow session', () => { .updateSession([450, 250]) .completeSession() - const arrow = tlstate.shapes.find((shape) => shape.type === TLDrawShapeType.Arrow) as ArrowShape + const arrow = state.shapes.find((shape) => shape.type === TLDrawShapeType.Arrow) as ArrowShape expect(arrow).toBeTruthy() - expect(tlstate.bindings.length).toBe(2) + expect(state.bindings.length).toBe(2) expect(arrow.handles.start.bindingId).not.toBe(undefined) expect(arrow.handles.end.bindingId).not.toBe(undefined) }) it('Removes a binding when dragged away', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .createShapes( { type: TLDrawShapeType.Rectangle, id: 'rect1', point: [200, 200], size: [200, 200] }, { type: TLDrawShapeType.Rectangle, id: 'rect2', point: [400, 200], size: [200, 200] }, @@ -219,11 +219,11 @@ describe('When creating with an arrow session', () => { .updateSession([0, 0]) .completeSession() - const arrow = tlstate.shapes.find((shape) => shape.type === TLDrawShapeType.Arrow) as ArrowShape + const arrow = state.shapes.find((shape) => shape.type === TLDrawShapeType.Arrow) as ArrowShape expect(arrow).toBeTruthy() - expect(tlstate.bindings.length).toBe(1) + expect(state.bindings.length).toBe(1) expect(arrow.handles.start.point).toStrictEqual([0, 0]) expect(arrow.handles.start.bindingId).toBe(undefined) diff --git a/packages/tldraw/src/state/sessions/ArrowSession/ArrowSession.ts b/packages/tldraw/src/state/sessions/ArrowSession/ArrowSession.ts index 9dd1ed927..5cf2537b8 100644 --- a/packages/tldraw/src/state/sessions/ArrowSession/ArrowSession.ts +++ b/packages/tldraw/src/state/sessions/ArrowSession/ArrowSession.ts @@ -3,7 +3,7 @@ import { ArrowShape, TLDrawShape, TLDrawBinding, - Data, + TLDrawSnapshot, Session, TLDrawStatus, SessionType, @@ -33,7 +33,7 @@ export class ArrowSession extends Session { isCreate: boolean constructor( - data: Data, + data: TLDrawSnapshot, viewport: TLBounds, point: number[], handleId: 'start' | 'end', @@ -85,7 +85,13 @@ export class ArrowSession extends Session { start = () => void null - update = (data: Data, point: number[], shiftKey = false, altKey = false, metaKey = false) => { + update = ( + data: TLDrawSnapshot, + point: number[], + shiftKey = false, + altKey = false, + metaKey = false + ) => { const { initialShape } = this const page = TLDR.getPage(data, data.appState.currentPageId) @@ -312,7 +318,7 @@ export class ArrowSession extends Session { } } - cancel = (data: Data) => { + cancel = (data: TLDrawSnapshot) => { const { initialShape, initialBinding, newStartBindingId, draggedBindingId } = this const afterBindings: Record = {} @@ -349,7 +355,7 @@ export class ArrowSession extends Session { } } - complete = (data: Data) => { + complete = (data: TLDrawSnapshot) => { const { initialShape, initialBinding, newStartBindingId, startBindingShapeId, handleId } = this const page = TLDR.getPage(data, data.appState.currentPageId) diff --git a/packages/tldraw/src/state/sessions/BrushSession/BrushSession.spec.ts b/packages/tldraw/src/state/sessions/BrushSession/BrushSession.spec.ts index bdc04d025..26cedc89c 100644 --- a/packages/tldraw/src/state/sessions/BrushSession/BrushSession.spec.ts +++ b/packages/tldraw/src/state/sessions/BrushSession/BrushSession.spec.ts @@ -4,39 +4,39 @@ import { SessionType, TLDrawStatus } from '~types' describe('Brush session', () => { it('begins, updateSession', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .selectNone() .startSession(SessionType.Brush, [-10, -10]) .updateSession([10, 10]) .completeSession() - expect(tlstate.appState.status).toBe(TLDrawStatus.Idle) - expect(tlstate.selectedIds.length).toBe(1) + expect(state.appState.status).toBe(TLDrawStatus.Idle) + expect(state.selectedIds.length).toBe(1) }) it('selects multiple shapes', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .selectNone() .startSession(SessionType.Brush, [-10, -10]) .updateSession([110, 110]) .completeSession() - expect(tlstate.selectedIds.length).toBe(3) + expect(state.selectedIds.length).toBe(3) }) it('does not de-select original shapes', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .selectNone() .select('rect1') .startSession(SessionType.Brush, [300, 300]) .updateSession([301, 301]) .completeSession() - expect(tlstate.selectedIds.length).toBe(1) + expect(state.selectedIds.length).toBe(1) }) // it('does not select hidden shapes', () => { - // const tlstate = new TLDrawState() + // const state = new TLDrawState() // .loadDocument(mockDocument) // .selectNone() // .toggleHidden(['rect1']) @@ -47,7 +47,7 @@ describe('Brush session', () => { // }) it('when command is held, require the entire shape to be selected', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .selectNone() .loadDocument(mockDocument) @@ -56,6 +56,6 @@ describe('Brush session', () => { .updateSession([10, 10], false, false, true) .completeSession() - expect(tlstate.selectedIds.length).toBe(0) + expect(state.selectedIds.length).toBe(0) }) }) diff --git a/packages/tldraw/src/state/sessions/BrushSession/BrushSession.ts b/packages/tldraw/src/state/sessions/BrushSession/BrushSession.ts index 376f88044..1d8c403bc 100644 --- a/packages/tldraw/src/state/sessions/BrushSession/BrushSession.ts +++ b/packages/tldraw/src/state/sessions/BrushSession/BrushSession.ts @@ -1,6 +1,6 @@ import { Utils, TLBounds } from '@tldraw/core' import { Vec } from '@tldraw/vec' -import { Data, Session, SessionType, TLDrawPatch, TLDrawStatus } from '~types' +import { TLDrawSnapshot, Session, SessionType, TLDrawPatch, TLDrawStatus } from '~types' import { TLDR } from '~state/TLDR' export class BrushSession extends Session { @@ -9,7 +9,7 @@ export class BrushSession extends Session { origin: number[] snapshot: BrushSnapshot - constructor(data: Data, viewport: TLBounds, point: number[]) { + constructor(data: TLDrawSnapshot, viewport: TLBounds, point: number[]) { super(viewport) this.origin = Vec.round(point) this.snapshot = getBrushSnapshot(data) @@ -18,7 +18,7 @@ export class BrushSession extends Session { start = () => void null update = ( - data: Data, + data: TLDrawSnapshot, point: number[], _shiftKey = false, _altKey = false, @@ -79,7 +79,7 @@ export class BrushSession extends Session { } } - cancel = (data: Data) => { + cancel = (data: TLDrawSnapshot) => { const { currentPageId } = data.appState return { document: { @@ -93,7 +93,7 @@ export class BrushSession extends Session { } } - complete = (data: Data) => { + complete = (data: TLDrawSnapshot) => { const { currentPageId } = data.appState const pageState = TLDR.getPageState(data, currentPageId) @@ -115,7 +115,7 @@ export class BrushSession extends Session { * not already selected, the shape's id and a test to see whether the * brush will intersect that shape. For tests, start broad -> fine. */ -export function getBrushSnapshot(data: Data) { +export function getBrushSnapshot(data: TLDrawSnapshot) { const { currentPageId } = data.appState const selectedIds = [...TLDR.getSelectedIds(data, currentPageId)] diff --git a/packages/tldraw/src/state/sessions/DrawSession/DrawSession.spec.ts b/packages/tldraw/src/state/sessions/DrawSession/DrawSession.spec.ts index 0ed00ee9a..d8a0eab16 100644 --- a/packages/tldraw/src/state/sessions/DrawSession/DrawSession.spec.ts +++ b/packages/tldraw/src/state/sessions/DrawSession/DrawSession.spec.ts @@ -10,14 +10,14 @@ import { } from '~types' describe('Draw session', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() it('begins, updateSession', () => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) - expect(tlstate.getShape('draw1')).toBe(undefined) + expect(state.getShape('draw1')).toBe(undefined) - tlstate + state .createShapes({ id: 'draw1', parentId: 'page1', @@ -37,18 +37,18 @@ describe('Draw session', () => { .updateSession([10, 10, 0.5]) .completeSession() - expect(tlstate.appState.status).toBe(TLDrawStatus.Idle) + expect(state.appState.status).toBe(TLDrawStatus.Idle) }) it('does, undoes and redoes', () => { - expect(tlstate.getShape('draw1')).toBeTruthy() + expect(state.getShape('draw1')).toBeTruthy() - tlstate.undo() + state.undo() - expect(tlstate.getShape('draw1')).toBe(undefined) + expect(state.getShape('draw1')).toBe(undefined) - tlstate.redo() + state.redo() - expect(tlstate.getShape('draw1')).toBeTruthy() + expect(state.getShape('draw1')).toBeTruthy() }) }) diff --git a/packages/tldraw/src/state/sessions/DrawSession/DrawSession.ts b/packages/tldraw/src/state/sessions/DrawSession/DrawSession.ts index 084087ccf..f960a218b 100644 --- a/packages/tldraw/src/state/sessions/DrawSession/DrawSession.ts +++ b/packages/tldraw/src/state/sessions/DrawSession/DrawSession.ts @@ -1,6 +1,6 @@ import { Utils, TLBounds } from '@tldraw/core' import { Vec } from '@tldraw/vec' -import { Data, Session, SessionType, TLDrawStatus } from '~types' +import { TLDrawSnapshot, Session, SessionType, TLDrawStatus } from '~types' import { TLDR } from '~state/TLDR' export class DrawSession extends Session { @@ -16,7 +16,7 @@ export class DrawSession extends Session { isLocked?: boolean lockedDirection?: 'horizontal' | 'vertical' - constructor(data: Data, viewport: TLBounds, point: number[], id: string) { + constructor(data: TLDrawSnapshot, viewport: TLBounds, point: number[], id: string) { super(viewport) this.origin = point this.previous = point @@ -32,7 +32,13 @@ export class DrawSession extends Session { start = () => void null - update = (data: Data, point: number[], shiftKey = false, altKey = false, metaKey = false) => { + update = ( + data: TLDrawSnapshot, + point: number[], + shiftKey = false, + altKey = false, + metaKey = false + ) => { const { shapeId } = this // Even if we're not locked yet, we base the future locking direction @@ -145,7 +151,7 @@ export class DrawSession extends Session { } } - cancel = (data: Data) => { + cancel = (data: TLDrawSnapshot) => { const { shapeId } = this const pageId = data.appState.currentPageId @@ -167,7 +173,7 @@ export class DrawSession extends Session { } } - complete = (data: Data) => { + complete = (data: TLDrawSnapshot) => { const { shapeId } = this const pageId = data.appState.currentPageId diff --git a/packages/tldraw/src/state/sessions/GridSession/GridSession.spec.ts b/packages/tldraw/src/state/sessions/GridSession/GridSession.spec.ts index 75fe02b71..38ae9a846 100644 --- a/packages/tldraw/src/state/sessions/GridSession/GridSession.spec.ts +++ b/packages/tldraw/src/state/sessions/GridSession/GridSession.spec.ts @@ -3,40 +3,40 @@ import { mockDocument } from '~test' import { SessionType, TLDrawStatus } from '~types' describe('Grid session', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() it('begins, updateSession', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1') .startSession(SessionType.Translate, [5, 5]) .updateSession([10, 10]) - expect(tlstate.getShape('rect1').point).toStrictEqual([5, 5]) + expect(state.getShape('rect1').point).toStrictEqual([5, 5]) - tlstate.completeSession() + state.completeSession() - expect(tlstate.appState.status).toBe(TLDrawStatus.Idle) + expect(state.appState.status).toBe(TLDrawStatus.Idle) - expect(tlstate.getShape('rect1').point).toStrictEqual([5, 5]) + expect(state.getShape('rect1').point).toStrictEqual([5, 5]) - tlstate.undo() + state.undo() - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) - tlstate.redo() + state.redo() - expect(tlstate.getShape('rect1').point).toStrictEqual([5, 5]) + expect(state.getShape('rect1').point).toStrictEqual([5, 5]) }) it('cancels session', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1', 'rect2') .startSession(SessionType.Translate, [5, 5]) .updateSession([10, 10]) .cancelSession() - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) }) }) diff --git a/packages/tldraw/src/state/sessions/GridSession/GridSession.ts b/packages/tldraw/src/state/sessions/GridSession/GridSession.ts index d060505d3..9f6d07561 100644 --- a/packages/tldraw/src/state/sessions/GridSession/GridSession.ts +++ b/packages/tldraw/src/state/sessions/GridSession/GridSession.ts @@ -5,7 +5,7 @@ import { TLDrawShape, TLDrawBinding, Session, - Data, + TLDrawSnapshot, TLDrawCommand, TLDrawStatus, ArrowShape, @@ -29,7 +29,13 @@ export class GridSession extends Session { rows = 1 isCopying = false - constructor(data: Data, viewport: TLBounds, id: string, pageId: string, point: number[]) { + constructor( + data: TLDrawSnapshot, + viewport: TLBounds, + id: string, + pageId: string, + point: number[] + ) { super(viewport) this.origin = point this.shape = TLDR.getShape(data, id, pageId) @@ -61,7 +67,13 @@ export class GridSession extends Session { return clone } - update = (data: Data, point: number[], shiftKey = false, altKey = false, metaKey = false) => { + update = ( + data: TLDrawSnapshot, + point: number[], + shiftKey = false, + altKey = false, + metaKey = false + ) => { const nextShapes: Patch> = {} const nextPageState: Patch = {} @@ -156,7 +168,7 @@ export class GridSession extends Session { } } - cancel = (data: Data) => { + cancel = (data: TLDrawSnapshot) => { const nextShapes: Record | undefined> = {} // Delete clones @@ -190,7 +202,7 @@ export class GridSession extends Session { } } - complete = (data: Data) => { + complete = (data: TLDrawSnapshot) => { const pageId = data.appState.currentPageId const beforeShapes: Patch> = {} diff --git a/packages/tldraw/src/state/sessions/HandleSession/HandleSession.spec.ts b/packages/tldraw/src/state/sessions/HandleSession/HandleSession.spec.ts index 849cd35c0..c5388047b 100644 --- a/packages/tldraw/src/state/sessions/HandleSession/HandleSession.spec.ts +++ b/packages/tldraw/src/state/sessions/HandleSession/HandleSession.spec.ts @@ -3,10 +3,10 @@ import { mockDocument } from '~test' import { SessionType, TLDrawShapeType, TLDrawStatus } from '~types' describe('Handle session', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() it('begins, updateSession', () => { - tlstate + state .loadDocument(mockDocument) .createShapes({ id: 'arrow1', @@ -17,13 +17,13 @@ describe('Handle session', () => { .updateSession([10, 10]) .completeSession() - expect(tlstate.appState.status).toBe(TLDrawStatus.Idle) + expect(state.appState.status).toBe(TLDrawStatus.Idle) - tlstate.undo().redo() + state.undo().redo() }) it('cancels session', () => { - tlstate + state .loadDocument(mockDocument) .createShapes({ type: TLDrawShapeType.Arrow, @@ -34,6 +34,6 @@ describe('Handle session', () => { .updateSession([10, 10]) .cancelSession() - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) }) }) diff --git a/packages/tldraw/src/state/sessions/HandleSession/HandleSession.ts b/packages/tldraw/src/state/sessions/HandleSession/HandleSession.ts index da0da02ee..47cb76b9c 100644 --- a/packages/tldraw/src/state/sessions/HandleSession/HandleSession.ts +++ b/packages/tldraw/src/state/sessions/HandleSession/HandleSession.ts @@ -2,7 +2,7 @@ import { Vec } from '@tldraw/vec' import type { TLBounds } from '@tldraw/core' import { SessionType, ShapesWithProp, TLDrawStatus } from '~types' import { Session } from '~types' -import type { Data } from '~types' +import type { TLDrawSnapshot } from '~types' import { TLDR } from '~state/TLDR' export class HandleSession extends Session { @@ -17,7 +17,7 @@ export class HandleSession extends Session { handleId: string constructor( - data: Data, + data: TLDrawSnapshot, viewport: TLBounds, point: number[], handleId: string, @@ -35,7 +35,13 @@ export class HandleSession extends Session { start = () => void null - update = (data: Data, point: number[], shiftKey = false, altKey = false, metaKey = false) => { + update = ( + data: TLDrawSnapshot, + point: number[], + shiftKey = false, + altKey = false, + metaKey = false + ) => { const { initialShape } = this const { currentPageId } = data.appState @@ -77,7 +83,7 @@ export class HandleSession extends Session { } } - cancel = (data: Data) => { + cancel = (data: TLDrawSnapshot) => { const { initialShape } = this const { currentPageId } = data.appState @@ -94,7 +100,7 @@ export class HandleSession extends Session { } } - complete = (data: Data) => { + complete = (data: TLDrawSnapshot) => { const { initialShape } = this const pageId = data.appState.currentPageId diff --git a/packages/tldraw/src/state/sessions/RotateSession/RotateSession.spec.ts b/packages/tldraw/src/state/sessions/RotateSession/RotateSession.spec.ts index 3ffd93a76..50bfb789e 100644 --- a/packages/tldraw/src/state/sessions/RotateSession/RotateSession.spec.ts +++ b/packages/tldraw/src/state/sessions/RotateSession/RotateSession.spec.ts @@ -5,80 +5,78 @@ import { mockDocument } from '~test' import { SessionType, TLDrawStatus } from '~types' describe('Rotate session', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() it('begins, updateSession', () => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) - expect(tlstate.getShape('rect1').rotation).toBe(undefined) + expect(state.getShape('rect1').rotation).toBe(undefined) - tlstate.select('rect1').startSession(SessionType.Rotate, [50, 0]).updateSession([100, 50]) + state.select('rect1').startSession(SessionType.Rotate, [50, 0]).updateSession([100, 50]) - expect(tlstate.getShape('rect1').rotation).toBe(Math.PI / 2) + expect(state.getShape('rect1').rotation).toBe(Math.PI / 2) - tlstate.updateSession([50, 100]) + state.updateSession([50, 100]) - expect(tlstate.getShape('rect1').rotation).toBe(Math.PI) + expect(state.getShape('rect1').rotation).toBe(Math.PI) - tlstate.updateSession([0, 50]) + state.updateSession([0, 50]) - expect(tlstate.getShape('rect1').rotation).toBe((Math.PI * 3) / 2) + expect(state.getShape('rect1').rotation).toBe((Math.PI * 3) / 2) - tlstate.updateSession([50, 0]) + state.updateSession([50, 0]) - expect(tlstate.getShape('rect1').rotation).toBe(0) + expect(state.getShape('rect1').rotation).toBe(0) - tlstate.updateSession([0, 50]) + state.updateSession([0, 50]) - expect(tlstate.getShape('rect1').rotation).toBe((Math.PI * 3) / 2) + expect(state.getShape('rect1').rotation).toBe((Math.PI * 3) / 2) - tlstate.completeSession() + state.completeSession() - expect(tlstate.appState.status).toBe(TLDrawStatus.Idle) + expect(state.appState.status).toBe(TLDrawStatus.Idle) - tlstate.undo() + state.undo() - expect(tlstate.getShape('rect1').rotation).toBe(undefined) + expect(state.getShape('rect1').rotation).toBe(undefined) - tlstate.redo() + state.redo() - expect(tlstate.getShape('rect1').rotation).toBe((Math.PI * 3) / 2) + expect(state.getShape('rect1').rotation).toBe((Math.PI * 3) / 2) }) it('cancels session', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1') .startSession(SessionType.Rotate, [50, 0]) .updateSession([100, 50]) .cancel() - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) }) it.todo('rotates handles only on shapes with handles') describe('when rotating a single shape while pressing shift', () => { it('Clamps rotation to 15 degrees', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() - tlstate + state .loadDocument(mockDocument) .select('rect1') .startSession(SessionType.Rotate, [0, 0]) .updateSession([20, 10], true) .completeSession() - expect(Math.round((tlstate.getShape('rect1').rotation || 0) * (180 / Math.PI)) % 15).toEqual( - 0 - ) + expect(Math.round((state.getShape('rect1').rotation || 0) * (180 / Math.PI)) % 15).toEqual(0) }) it('Clamps rotation to 15 degrees when starting from a rotation', () => { // Rect 1 is a little rotated - const tlstate = new TLDrawState() + const state = new TLDrawState() - tlstate + state .loadDocument(mockDocument) .select('rect1') .startSession(SessionType.Rotate, [0, 0]) @@ -86,56 +84,52 @@ describe('Rotate session', () => { .completeSession() // Rect 1 clamp rotated, starting from a little rotation - tlstate + state .select('rect1') .startSession(SessionType.Rotate, [0, 0]) .updateSession([100, 200], true) .completeSession() - expect(Math.round((tlstate.getShape('rect1').rotation || 0) * (180 / Math.PI)) % 15).toEqual( - 0 - ) + expect(Math.round((state.getShape('rect1').rotation || 0) * (180 / Math.PI)) % 15).toEqual(0) // Try again, too. - tlstate + state .select('rect1') .startSession(SessionType.Rotate, [0, 0]) .updateSession([-100, 5000], true) .completeSession() - expect(Math.round((tlstate.getShape('rect1').rotation || 0) * (180 / Math.PI)) % 15).toEqual( - 0 - ) + expect(Math.round((state.getShape('rect1').rotation || 0) * (180 / Math.PI)) % 15).toEqual(0) }) }) describe('when rotating multiple shapes', () => { it('keeps the center', () => { - tlstate.loadDocument(mockDocument).select('rect1', 'rect2') + state.loadDocument(mockDocument).select('rect1', 'rect2') const centerBefore = Vec.round( Utils.getBoundsCenter( - Utils.getCommonBounds(tlstate.selectedIds.map((id) => tlstate.getShapeBounds(id))) + Utils.getCommonBounds(state.selectedIds.map((id) => state.getShapeBounds(id))) ) ) - tlstate.startSession(SessionType.Rotate, [50, 0]).updateSession([100, 50]).completeSession() + state.startSession(SessionType.Rotate, [50, 0]).updateSession([100, 50]).completeSession() const centerAfterA = Vec.round( Utils.getBoundsCenter( - Utils.getCommonBounds(tlstate.selectedIds.map((id) => tlstate.getShapeBounds(id))) + Utils.getCommonBounds(state.selectedIds.map((id) => state.getShapeBounds(id))) ) ) - tlstate.startSession(SessionType.Rotate, [100, 0]).updateSession([50, 0]).completeSession() + state.startSession(SessionType.Rotate, [100, 0]).updateSession([50, 0]).completeSession() const centerAfterB = Vec.round( Utils.getBoundsCenter( - Utils.getCommonBounds(tlstate.selectedIds.map((id) => tlstate.getShapeBounds(id))) + Utils.getCommonBounds(state.selectedIds.map((id) => state.getShapeBounds(id))) ) ) - expect(tlstate.getShape('rect1').rotation) + expect(state.getShape('rect1').rotation) expect(centerBefore).toStrictEqual(centerAfterA) expect(centerAfterA).toStrictEqual(centerAfterB) }) @@ -147,32 +141,32 @@ describe('Rotate session', () => { it.todo('clears the cached center after any command other than a rotate command, tbh') it('changes the center after nudging', () => { - const tlstate = new TLDrawState().loadDocument(mockDocument).select('rect1', 'rect2') + const state = new TLDrawState().loadDocument(mockDocument).select('rect1', 'rect2') const centerBefore = Vec.round( Utils.getBoundsCenter( - Utils.getCommonBounds(tlstate.selectedIds.map((id) => tlstate.getShapeBounds(id))) + Utils.getCommonBounds(state.selectedIds.map((id) => state.getShapeBounds(id))) ) ) - tlstate.startSession(SessionType.Rotate, [50, 0]).updateSession([100, 50]).completeSession() + state.startSession(SessionType.Rotate, [50, 0]).updateSession([100, 50]).completeSession() const centerAfterA = Vec.round( Utils.getBoundsCenter( - Utils.getCommonBounds(tlstate.selectedIds.map((id) => tlstate.getShapeBounds(id))) + Utils.getCommonBounds(state.selectedIds.map((id) => state.getShapeBounds(id))) ) ) - expect(tlstate.getShape('rect1').rotation) + expect(state.getShape('rect1').rotation) expect(centerBefore).toStrictEqual(centerAfterA) - tlstate.selectAll().nudge([10, 10]) + state.selectAll().nudge([10, 10]) - tlstate.startSession(SessionType.Rotate, [50, 0]).updateSession([100, 50]).completeSession() + state.startSession(SessionType.Rotate, [50, 0]).updateSession([100, 50]).completeSession() const centerAfterB = Vec.round( Utils.getBoundsCenter( - Utils.getCommonBounds(tlstate.selectedIds.map((id) => tlstate.getShapeBounds(id))) + Utils.getCommonBounds(state.selectedIds.map((id) => state.getShapeBounds(id))) ) ) diff --git a/packages/tldraw/src/state/sessions/RotateSession/RotateSession.ts b/packages/tldraw/src/state/sessions/RotateSession/RotateSession.ts index 7eaca6839..c8ae149d7 100644 --- a/packages/tldraw/src/state/sessions/RotateSession/RotateSession.ts +++ b/packages/tldraw/src/state/sessions/RotateSession/RotateSession.ts @@ -1,7 +1,7 @@ import { Utils, TLBounds } from '@tldraw/core' import { Vec } from '@tldraw/vec' import { Session, SessionType, TLDrawShape, TLDrawStatus } from '~types' -import type { Data } from '~types' +import type { TLDrawSnapshot } from '~types' import { TLDR } from '~state/TLDR' export class RotateSession extends Session { @@ -13,7 +13,7 @@ export class RotateSession extends Session { initialAngle: number changes: Record> = {} - constructor(data: Data, viewport: TLBounds, point: number[]) { + constructor(data: TLDrawSnapshot, viewport: TLBounds, point: number[]) { super(viewport) this.origin = point @@ -23,7 +23,13 @@ export class RotateSession extends Session { start = () => void null - update = (data: Data, point: number[], shiftKey = false, altKey = false, metaKey = false) => { + update = ( + data: TLDrawSnapshot, + point: number[], + shiftKey = false, + altKey = false, + metaKey = false + ) => { const { commonBoundsCenter, initialShapes } = this.snapshot const pageId = data.appState.currentPageId @@ -71,7 +77,7 @@ export class RotateSession extends Session { } } - cancel = (data: Data) => { + cancel = (data: TLDrawSnapshot) => { const { initialShapes } = this.snapshot const pageId = data.appState.currentPageId @@ -92,7 +98,7 @@ export class RotateSession extends Session { } } - complete = (data: Data) => { + complete = (data: TLDrawSnapshot) => { const { initialShapes } = this.snapshot const pageId = data.appState.currentPageId @@ -128,7 +134,7 @@ export class RotateSession extends Session { } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -export function getRotateSnapshot(data: Data) { +export function getRotateSnapshot(data: TLDrawSnapshot) { const currentPageId = data.appState.currentPageId const pageState = TLDR.getPageState(data, currentPageId) const initialShapes = TLDR.getSelectedBranchSnapshot(data, currentPageId) diff --git a/packages/tldraw/src/state/sessions/TransformSession/TransformSession.spec.ts b/packages/tldraw/src/state/sessions/TransformSession/TransformSession.spec.ts index c0c960d60..926d9f47f 100644 --- a/packages/tldraw/src/state/sessions/TransformSession/TransformSession.spec.ts +++ b/packages/tldraw/src/state/sessions/TransformSession/TransformSession.spec.ts @@ -4,19 +4,19 @@ import { TLBoundsCorner, Utils } from '@tldraw/core' import { TLDR } from '~state/TLDR' import { SessionType, TLDrawStatus } from '~types' -function getShapeBounds(tlstate: TLDrawState, ...ids: string[]) { +function getShapeBounds(state: TLDrawState, ...ids: string[]) { return Utils.getCommonBounds( - (ids.length ? ids : tlstate.selectedIds).map((id) => TLDR.getBounds(tlstate.getShape(id))) + (ids.length ? ids : state.selectedIds).map((id) => TLDR.getBounds(state.getShape(id))) ) } describe('Transform session', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() it('begins, updateSession', () => { - tlstate.loadDocument(mockDocument) + state.loadDocument(mockDocument) - expect(getShapeBounds(tlstate, 'rect1')).toMatchObject({ + expect(getShapeBounds(state, 'rect1')).toMatchObject({ minX: 0, minY: 0, maxX: 100, @@ -25,15 +25,15 @@ describe('Transform session', () => { height: 100, }) - tlstate + state .select('rect1', 'rect2') .startSession(SessionType.Transform, [0, 0], TLBoundsCorner.TopLeft) .updateSession([10, 10]) .completeSession() - expect(tlstate.appState.status).toBe(TLDrawStatus.Idle) + expect(state.appState.status).toBe(TLDrawStatus.Idle) - expect(getShapeBounds(tlstate, 'rect1')).toMatchObject({ + expect(getShapeBounds(state, 'rect1')).toMatchObject({ minX: 10, minY: 10, maxX: 105, @@ -42,9 +42,9 @@ describe('Transform session', () => { height: 95, }) - tlstate.undo() + state.undo() - expect(getShapeBounds(tlstate, 'rect1')).toMatchObject({ + expect(getShapeBounds(state, 'rect1')).toMatchObject({ minX: 0, minY: 0, maxX: 100, @@ -53,30 +53,30 @@ describe('Transform session', () => { height: 100, }) - tlstate.redo() + state.redo() }) it('cancels session', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1', 'rect2') .startSession(SessionType.Transform, [5, 5], TLBoundsCorner.TopLeft) .updateSession([10, 10]) .cancelSession() - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) }) describe('when transforming from the top-left corner', () => { it('transforms a single shape', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1') .startSession(SessionType.Transform, [0, 0], TLBoundsCorner.TopLeft) .updateSession([10, 10]) .completeSession() - expect(getShapeBounds(tlstate)).toMatchObject({ + expect(getShapeBounds(state)).toMatchObject({ minX: 10, minY: 10, maxX: 100, @@ -87,14 +87,14 @@ describe('Transform session', () => { }) it('transforms a single shape while holding shift', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1') .startSession(SessionType.Transform, [0, 0], TLBoundsCorner.TopLeft) .updateSession([20, 10], true) .completeSession() - expect(getShapeBounds(tlstate, 'rect1')).toMatchObject({ + expect(getShapeBounds(state, 'rect1')).toMatchObject({ minX: 10, minY: 10, maxX: 100, @@ -105,14 +105,14 @@ describe('Transform session', () => { }) it('transforms multiple shapes', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1', 'rect2') .startSession(SessionType.Transform, [0, 0], TLBoundsCorner.TopLeft) .updateSession([10, 10]) .completeSession() - expect(getShapeBounds(tlstate, 'rect1')).toMatchObject({ + expect(getShapeBounds(state, 'rect1')).toMatchObject({ minX: 10, minY: 10, maxX: 105, @@ -121,7 +121,7 @@ describe('Transform session', () => { height: 95, }) - expect(getShapeBounds(tlstate, 'rect2')).toMatchObject({ + expect(getShapeBounds(state, 'rect2')).toMatchObject({ minX: 105, minY: 105, maxX: 200, @@ -132,14 +132,14 @@ describe('Transform session', () => { }) it('transforms multiple shapes while holding shift', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1', 'rect2') .startSession(SessionType.Transform, [0, 0], TLBoundsCorner.TopLeft) .updateSession([20, 10], true) .completeSession() - expect(getShapeBounds(tlstate, 'rect1')).toMatchObject({ + expect(getShapeBounds(state, 'rect1')).toMatchObject({ minX: 10, minY: 10, maxX: 105, @@ -148,7 +148,7 @@ describe('Transform session', () => { height: 95, }) - expect(getShapeBounds(tlstate, 'rect2')).toMatchObject({ + expect(getShapeBounds(state, 'rect2')).toMatchObject({ minX: 105, minY: 105, maxX: 200, @@ -189,8 +189,8 @@ describe('Transform session', () => { describe('when transforming a group', () => { it('transforms the groups children', () => { - const tlstate = new TLDrawState() - tlstate + const state = new TLDrawState() + state .loadDocument(mockDocument) .group(['rect1', 'rect2'], 'groupA') .select('groupA') @@ -198,7 +198,7 @@ describe('Transform session', () => { .updateSession([10, 10]) .completeSession() - expect(getShapeBounds(tlstate, 'rect1')).toMatchObject({ + expect(getShapeBounds(state, 'rect1')).toMatchObject({ minX: 10, minY: 10, maxX: 105, @@ -207,7 +207,7 @@ describe('Transform session', () => { height: 95, }) - expect(getShapeBounds(tlstate, 'rect2')).toMatchObject({ + expect(getShapeBounds(state, 'rect2')).toMatchObject({ minX: 105, minY: 105, maxX: 200, @@ -221,7 +221,7 @@ describe('Transform session', () => { describe('When creating with a transform session', () => { it('Deletes the shape on undo', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .select('rect1') .startSession(SessionType.Transform, [5, 5], TLBoundsCorner.TopLeft, true) @@ -229,7 +229,7 @@ describe('When creating with a transform session', () => { .completeSession() .undo() - expect(tlstate.getShape('rect1')).toBe(undefined) + expect(state.getShape('rect1')).toBe(undefined) }) }) diff --git a/packages/tldraw/src/state/sessions/TransformSession/TransformSession.ts b/packages/tldraw/src/state/sessions/TransformSession/TransformSession.ts index 98a0ee88a..77d4beb1a 100644 --- a/packages/tldraw/src/state/sessions/TransformSession/TransformSession.ts +++ b/packages/tldraw/src/state/sessions/TransformSession/TransformSession.ts @@ -2,7 +2,7 @@ import { TLBounds, TLBoundsCorner, TLBoundsEdge, Utils } from '@tldraw/core' import { Vec } from '@tldraw/vec' import type { TLSnapLine, TLBoundsWithCenter } from '@tldraw/core' import { Session, SessionType, TLDrawShape, TLDrawStatus } from '~types' -import type { Data } from '~types' +import type { TLDrawSnapshot } from '~types' import { TLDR } from '~state/TLDR' import type { Command } from 'rko' import { SLOW_SPEED, SNAP_DISTANCE } from '~constants' @@ -31,7 +31,7 @@ export class TransformSession extends Session { speed = 1 constructor( - data: Data, + data: TLDrawSnapshot, viewport: TLBounds, point: number[], transformType: TLBoundsEdge | TLBoundsCorner = TLBoundsCorner.BottomRight, @@ -46,13 +46,19 @@ export class TransformSession extends Session { Session.cache.selectedIds = [...this.initialSelectedIds] } - start = (data: Data) => { + start = (data: TLDrawSnapshot) => { this.createSnapInfo(data) return void null } // eslint-disable-next-line @typescript-eslint/no-unused-vars - update = (data: Data, point: number[], shiftKey = false, altKey = false, metaKey = false) => { + update = ( + data: TLDrawSnapshot, + point: number[], + shiftKey = false, + altKey = false, + metaKey = false + ) => { const { transformType, snapshot: { shapeBounds, initialBounds, isAllAspectRatioLocked }, @@ -154,7 +160,7 @@ export class TransformSession extends Session { } } - cancel = (data: Data) => { + cancel = (data: TLDrawSnapshot) => { const { shapeBounds } = this.snapshot const shapes = {} as Record @@ -184,7 +190,7 @@ export class TransformSession extends Session { } } - complete = (data: Data): Data | Command | undefined => { + complete = (data: TLDrawSnapshot): TLDrawSnapshot | Command | undefined => { const { hasUnlockedShapes, shapeBounds } = this.snapshot undefined if (!hasUnlockedShapes) return @@ -254,7 +260,7 @@ export class TransformSession extends Session { } } - private createSnapInfo = async (data: Data) => { + private createSnapInfo = async (data: TLDrawSnapshot) => { const { initialShapeIds } = this.snapshot const { currentPageId } = data.appState const page = data.document.pages[currentPageId] @@ -268,7 +274,10 @@ export class TransformSession extends Session { } } -export function getTransformSnapshot(data: Data, transformType: TLBoundsEdge | TLBoundsCorner) { +export function getTransformSnapshot( + data: TLDrawSnapshot, + transformType: TLBoundsEdge | TLBoundsCorner +) { const initialShapes = TLDR.getSelectedBranchSnapshot(data, data.appState.currentPageId) const initialShapeIds = initialShapes.map((shape) => shape.id) diff --git a/packages/tldraw/src/state/sessions/TransformSingleSession/TransformSingleSession.session.spec.ts b/packages/tldraw/src/state/sessions/TransformSingleSession/TransformSingleSession.session.spec.ts index eab596795..8c5f8a216 100644 --- a/packages/tldraw/src/state/sessions/TransformSingleSession/TransformSingleSession.session.spec.ts +++ b/packages/tldraw/src/state/sessions/TransformSingleSession/TransformSingleSession.session.spec.ts @@ -4,36 +4,36 @@ import { TLBoundsCorner } from '@tldraw/core' import { SessionType, TLDrawStatus } from '~types' describe('Transform single session', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() it('begins, updateSession', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1') .startSession(SessionType.TransformSingle, [-10, -10], TLBoundsCorner.TopLeft) .updateSession([10, 10]) .completeSession() - expect(tlstate.appState.status).toBe(TLDrawStatus.Idle) + expect(state.appState.status).toBe(TLDrawStatus.Idle) - tlstate.undo().redo() + state.undo().redo() }) it('cancels session', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1') .startSession(SessionType.TransformSingle, [5, 5], TLBoundsCorner.TopLeft) .updateSession([10, 10]) .cancelSession() - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) }) }) describe('When creating with a transform-single session', () => { it('Deletes the shape on undo', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .select('rect1') .startSession(SessionType.TransformSingle, [5, 5], TLBoundsCorner.TopLeft, true) @@ -41,7 +41,7 @@ describe('When creating with a transform-single session', () => { .completeSession() .undo() - expect(tlstate.getShape('rect1')).toBe(undefined) + expect(state.getShape('rect1')).toBe(undefined) }) }) diff --git a/packages/tldraw/src/state/sessions/TransformSingleSession/TransformSingleSession.session.ts b/packages/tldraw/src/state/sessions/TransformSingleSession/TransformSingleSession.session.ts index 34983a6dc..a500a5f08 100644 --- a/packages/tldraw/src/state/sessions/TransformSingleSession/TransformSingleSession.session.ts +++ b/packages/tldraw/src/state/sessions/TransformSingleSession/TransformSingleSession.session.ts @@ -9,7 +9,7 @@ import { import { Vec } from '@tldraw/vec' import { SessionType, TLDrawShape, TLDrawStatus } from '~types' import { Session } from '~types' -import type { Data } from '~types' +import type { TLDrawSnapshot } from '~types' import { TLDR } from '~state/TLDR' import { SLOW_SPEED, SNAP_DISTANCE } from '~constants' @@ -36,7 +36,7 @@ export class TransformSingleSession extends Session { speed = 1 constructor( - data: Data, + data: TLDrawSnapshot, viewport: TLBounds, point: number[], transformType: TLBoundsEdge | TLBoundsCorner = TLBoundsCorner.BottomRight, @@ -50,12 +50,18 @@ export class TransformSingleSession extends Session { Session.cache.selectedIds = [...this.snapshot.initialShape.id] } - start = (data: Data) => { + start = (data: TLDrawSnapshot) => { this.createSnapInfo(data) return void null } - update = (data: Data, point: number[], shiftKey = false, _altKey = false, metaKey = false) => { + update = ( + data: TLDrawSnapshot, + point: number[], + shiftKey = false, + _altKey = false, + metaKey = false + ) => { const { transformType } = this const { currentPageId, initialShapeBounds, initialShape, id } = this.snapshot @@ -144,7 +150,7 @@ export class TransformSingleSession extends Session { } } - cancel = (data: Data) => { + cancel = (data: TLDrawSnapshot) => { const { initialShape } = this.snapshot const shapes = {} as Record @@ -174,7 +180,7 @@ export class TransformSingleSession extends Session { } } - complete = (data: Data) => { + complete = (data: TLDrawSnapshot) => { if (!this.snapshot.hasUnlockedShape) return data const { initialShape } = this.snapshot @@ -231,7 +237,7 @@ export class TransformSingleSession extends Session { } } - private createSnapInfo = async (data: Data) => { + private createSnapInfo = async (data: TLDrawSnapshot) => { const { initialShape } = this.snapshot const { currentPageId } = data.appState const page = data.document.pages[currentPageId] @@ -246,7 +252,7 @@ export class TransformSingleSession extends Session { } export function getTransformSingleSnapshot( - data: Data, + data: TLDrawSnapshot, transformType: TLBoundsEdge | TLBoundsCorner ) { const { currentPageId } = data.appState diff --git a/packages/tldraw/src/state/sessions/TranslateSession/TranslateSession.spec.ts b/packages/tldraw/src/state/sessions/TranslateSession/TranslateSession.spec.ts index 2367678c9..8669d2c5b 100644 --- a/packages/tldraw/src/state/sessions/TranslateSession/TranslateSession.spec.ts +++ b/packages/tldraw/src/state/sessions/TranslateSession/TranslateSession.spec.ts @@ -3,140 +3,140 @@ import { mockDocument } from '~test' import { GroupShape, SessionType, TLDrawShapeType, TLDrawStatus } from '~types' describe('Translate session', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() it('begins, updateSession', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1') .startSession(SessionType.Translate, [5, 5]) .updateSession([10, 10]) - expect(tlstate.getShape('rect1').point).toStrictEqual([5, 5]) + expect(state.getShape('rect1').point).toStrictEqual([5, 5]) - tlstate.completeSession() + state.completeSession() - expect(tlstate.appState.status).toBe(TLDrawStatus.Idle) + expect(state.appState.status).toBe(TLDrawStatus.Idle) - expect(tlstate.getShape('rect1').point).toStrictEqual([5, 5]) + expect(state.getShape('rect1').point).toStrictEqual([5, 5]) - tlstate.undo() + state.undo() - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) - tlstate.redo() + state.redo() - expect(tlstate.getShape('rect1').point).toStrictEqual([5, 5]) + expect(state.getShape('rect1').point).toStrictEqual([5, 5]) }) it('cancels session', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1', 'rect2') .startSession(SessionType.Translate, [5, 5]) .updateSession([10, 10]) .cancelSession() - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) }) it('moves a single shape', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1') .startSession(SessionType.Translate, [10, 10]) .updateSession([20, 20]) .completeSession() - expect(tlstate.getShape('rect1').point).toStrictEqual([10, 10]) + expect(state.getShape('rect1').point).toStrictEqual([10, 10]) }) it('moves a single shape along a locked axis', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1') .startSession(SessionType.Translate, [10, 10]) .updateSession([20, 20], true) .completeSession() - expect(tlstate.getShape('rect1').point).toStrictEqual([10, 0]) + expect(state.getShape('rect1').point).toStrictEqual([10, 0]) }) it('moves two shapes', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1', 'rect2') .startSession(SessionType.Translate, [10, 10]) .updateSession([20, 20]) .completeSession() - expect(tlstate.getShape('rect1').point).toStrictEqual([10, 10]) - expect(tlstate.getShape('rect2').point).toStrictEqual([110, 110]) + expect(state.getShape('rect1').point).toStrictEqual([10, 10]) + expect(state.getShape('rect2').point).toStrictEqual([110, 110]) }) it('undos and redos clones', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1', 'rect2') .startSession(SessionType.Translate, [10, 10]) .updateSession([20, 20], false, true) .completeSession() - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) - expect(tlstate.getShape('rect2').point).toStrictEqual([100, 100]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect2').point).toStrictEqual([100, 100]) - expect(Object.keys(tlstate.getPage().shapes).length).toBe(5) + expect(Object.keys(state.getPage().shapes).length).toBe(5) - tlstate.undo() + state.undo() - expect(Object.keys(tlstate.getPage().shapes).length).toBe(3) + expect(Object.keys(state.getPage().shapes).length).toBe(3) - tlstate.redo() + state.redo() - expect(Object.keys(tlstate.getPage().shapes).length).toBe(5) + expect(Object.keys(state.getPage().shapes).length).toBe(5) }) it('clones shapes', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1', 'rect2') .startSession(SessionType.Translate, [10, 10]) .updateSession([20, 20], false, true) .completeSession() - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) - expect(tlstate.getShape('rect2').point).toStrictEqual([100, 100]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect2').point).toStrictEqual([100, 100]) - expect(Object.keys(tlstate.getPage().shapes).length).toBe(5) + expect(Object.keys(state.getPage().shapes).length).toBe(5) }) it('destroys clones when last update is not cloning', () => { - tlstate.resetDocument().loadDocument(mockDocument) + state.resetDocument().loadDocument(mockDocument) - expect(Object.keys(tlstate.getPage().shapes).length).toBe(3) + expect(Object.keys(state.getPage().shapes).length).toBe(3) - tlstate + state .select('rect1', 'rect2') .startSession(SessionType.Translate, [10, 10]) .updateSession([20, 20], false, true) - expect(Object.keys(tlstate.getPage().shapes).length).toBe(5) + expect(Object.keys(state.getPage().shapes).length).toBe(5) - tlstate.updateSession([30, 30], false, false) + state.updateSession([30, 30], false, false) - expect(Object.keys(tlstate.getPage().shapes).length).toBe(3) + expect(Object.keys(state.getPage().shapes).length).toBe(3) - tlstate.completeSession() + state.completeSession() // Original position + delta - expect(tlstate.getShape('rect1').point).toStrictEqual([30, 30]) - expect(tlstate.getShape('rect2').point).toStrictEqual([130, 130]) + expect(state.getShape('rect1').point).toStrictEqual([30, 30]) + expect(state.getShape('rect2').point).toStrictEqual([130, 130]) - expect(Object.keys(tlstate.page.shapes)).toStrictEqual(['rect1', 'rect2', 'rect3']) + expect(Object.keys(state.page.shapes)).toStrictEqual(['rect1', 'rect2', 'rect3']) }) it('destroys bindings from the translating shape', () => { - tlstate + state .loadDocument(mockDocument) .selectAll() .delete() @@ -160,33 +160,33 @@ describe('Translate session', () => { .updateSession([50, 50]) .completeSession() - expect(tlstate.bindings.length).toBe(1) + expect(state.bindings.length).toBe(1) - tlstate + state .select('arrow1') .startSession(SessionType.Translate, [10, 10]) .updateSession([30, 30]) .completeSession() - // expect(tlstate.bindings.length).toBe(0) - // expect(tlstate.getShape('arrow1').handles.start.bindingId).toBe(undefined) + // expect(state.bindings.length).toBe(0) + // expect(state.getShape('arrow1').handles.start.bindingId).toBe(undefined) - // tlstate.undo() + // state.undo() - // expect(tlstate.bindings.length).toBe(1) - // expect(tlstate.getShape('arrow1').handles.start.bindingId).toBeTruthy() + // expect(state.bindings.length).toBe(1) + // expect(state.getShape('arrow1').handles.start.bindingId).toBeTruthy() - // tlstate.redo() + // state.redo() - // expect(tlstate.bindings.length).toBe(0) - // expect(tlstate.getShape('arrow1').handles.start.bindingId).toBe(undefined) + // expect(state.bindings.length).toBe(0) + // expect(state.getShape('arrow1').handles.start.bindingId).toBe(undefined) }) // it.todo('clones a shape with a parent shape') describe('when translating a child of a group', () => { it('translates the shape and updates the groups size / point', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1', 'rect2') .group(['rect1', 'rect2'], 'groupA') @@ -195,25 +195,25 @@ describe('Translate session', () => { .updateSession([20, 20], false, false) .completeSession() - expect(tlstate.getShape('groupA').point).toStrictEqual([10, 10]) - expect(tlstate.getShape('rect1').point).toStrictEqual([10, 10]) - expect(tlstate.getShape('rect2').point).toStrictEqual([100, 100]) + expect(state.getShape('groupA').point).toStrictEqual([10, 10]) + expect(state.getShape('rect1').point).toStrictEqual([10, 10]) + expect(state.getShape('rect2').point).toStrictEqual([100, 100]) - tlstate.undo() + state.undo() - expect(tlstate.getShape('groupA').point).toStrictEqual([0, 0]) - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) - expect(tlstate.getShape('rect2').point).toStrictEqual([100, 100]) + expect(state.getShape('groupA').point).toStrictEqual([0, 0]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect2').point).toStrictEqual([100, 100]) - tlstate.redo() + state.redo() - expect(tlstate.getShape('groupA').point).toStrictEqual([10, 10]) - expect(tlstate.getShape('rect1').point).toStrictEqual([10, 10]) - expect(tlstate.getShape('rect2').point).toStrictEqual([100, 100]) + expect(state.getShape('groupA').point).toStrictEqual([10, 10]) + expect(state.getShape('rect1').point).toStrictEqual([10, 10]) + expect(state.getShape('rect2').point).toStrictEqual([100, 100]) }) it('clones the shape and updates the parent', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1', 'rect2') .group(['rect1', 'rect2'], 'groupA') @@ -222,38 +222,38 @@ describe('Translate session', () => { .updateSession([20, 20], false, true) .completeSession() - const children = tlstate.getShape('groupA').children + const children = state.getShape('groupA').children const newShapeId = children[children.length - 1] - expect(tlstate.getShape('groupA').point).toStrictEqual([0, 0]) - expect(tlstate.getShape('groupA').children.length).toBe(3) - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) - expect(tlstate.getShape('rect2').point).toStrictEqual([100, 100]) - expect(tlstate.getShape(newShapeId).point).toStrictEqual([20, 20]) - expect(tlstate.getShape(newShapeId).parentId).toBe('groupA') + expect(state.getShape('groupA').point).toStrictEqual([0, 0]) + expect(state.getShape('groupA').children.length).toBe(3) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect2').point).toStrictEqual([100, 100]) + expect(state.getShape(newShapeId).point).toStrictEqual([20, 20]) + expect(state.getShape(newShapeId).parentId).toBe('groupA') - tlstate.undo() + state.undo() - expect(tlstate.getShape('groupA').point).toStrictEqual([0, 0]) - expect(tlstate.getShape('groupA').children.length).toBe(2) - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) - expect(tlstate.getShape('rect2').point).toStrictEqual([100, 100]) - expect(tlstate.getShape(newShapeId)).toBeUndefined() + expect(state.getShape('groupA').point).toStrictEqual([0, 0]) + expect(state.getShape('groupA').children.length).toBe(2) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect2').point).toStrictEqual([100, 100]) + expect(state.getShape(newShapeId)).toBeUndefined() - tlstate.redo() + state.redo() - expect(tlstate.getShape('groupA').point).toStrictEqual([0, 0]) - expect(tlstate.getShape('groupA').children.length).toBe(3) - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) - expect(tlstate.getShape('rect2').point).toStrictEqual([100, 100]) - expect(tlstate.getShape(newShapeId).point).toStrictEqual([20, 20]) - expect(tlstate.getShape(newShapeId).parentId).toBe('groupA') + expect(state.getShape('groupA').point).toStrictEqual([0, 0]) + expect(state.getShape('groupA').children.length).toBe(3) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect2').point).toStrictEqual([100, 100]) + expect(state.getShape(newShapeId).point).toStrictEqual([20, 20]) + expect(state.getShape(newShapeId).parentId).toBe('groupA') }) }) describe('when translating a shape with children', () => { it('translates the shapes children', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1', 'rect2') .group(['rect1', 'rect2'], 'groupA') @@ -261,25 +261,25 @@ describe('Translate session', () => { .updateSession([20, 20], false, false) .completeSession() - expect(tlstate.getShape('groupA').point).toStrictEqual([10, 10]) - expect(tlstate.getShape('rect1').point).toStrictEqual([10, 10]) - expect(tlstate.getShape('rect2').point).toStrictEqual([110, 110]) + expect(state.getShape('groupA').point).toStrictEqual([10, 10]) + expect(state.getShape('rect1').point).toStrictEqual([10, 10]) + expect(state.getShape('rect2').point).toStrictEqual([110, 110]) - tlstate.undo() + state.undo() - expect(tlstate.getShape('groupA').point).toStrictEqual([0, 0]) - expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) - expect(tlstate.getShape('rect2').point).toStrictEqual([100, 100]) + expect(state.getShape('groupA').point).toStrictEqual([0, 0]) + expect(state.getShape('rect1').point).toStrictEqual([0, 0]) + expect(state.getShape('rect2').point).toStrictEqual([100, 100]) - tlstate.redo() + state.redo() - expect(tlstate.getShape('groupA').point).toStrictEqual([10, 10]) - expect(tlstate.getShape('rect1').point).toStrictEqual([10, 10]) - expect(tlstate.getShape('rect2').point).toStrictEqual([110, 110]) + expect(state.getShape('groupA').point).toStrictEqual([10, 10]) + expect(state.getShape('rect1').point).toStrictEqual([10, 10]) + expect(state.getShape('rect2').point).toStrictEqual([110, 110]) }) it('clones the shapes and children', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1', 'rect2') .group() @@ -289,7 +289,7 @@ describe('Translate session', () => { }) it('deletes clones when not cloning anymore', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1', 'rect2') .group() @@ -299,11 +299,11 @@ describe('Translate session', () => { .updateSession([20, 20], false, true) .completeSession() - expect(tlstate.shapes.filter((shape) => shape.type === TLDrawShapeType.Group).length).toBe(2) + expect(state.shapes.filter((shape) => shape.type === TLDrawShapeType.Group).length).toBe(2) }) it('deletes clones when not cloning anymore', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1', 'rect2') .group() @@ -312,11 +312,11 @@ describe('Translate session', () => { .updateSession([20, 20], false, false) .completeSession() - expect(tlstate.shapes.filter((shape) => shape.type === TLDrawShapeType.Group).length).toBe(1) + expect(state.shapes.filter((shape) => shape.type === TLDrawShapeType.Group).length).toBe(1) }) it('clones the shapes and children when selecting a group and a different shape', () => { - tlstate + state .loadDocument(mockDocument) .select('rect1', 'rect2') .group(['rect1', 'rect2'], 'groupA') @@ -330,7 +330,7 @@ describe('Translate session', () => { describe('When creating with a translate session', () => { it('Deletes the shape on undo', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .select('rect1') .startSession(SessionType.Translate, [5, 5], true) @@ -338,7 +338,7 @@ describe('When creating with a translate session', () => { .completeSession() .undo() - expect(tlstate.getShape('rect1')).toBe(undefined) + expect(state.getShape('rect1')).toBe(undefined) }) }) diff --git a/packages/tldraw/src/state/sessions/TranslateSession/TranslateSession.ts b/packages/tldraw/src/state/sessions/TranslateSession/TranslateSession.ts index ed935096e..0845086fb 100644 --- a/packages/tldraw/src/state/sessions/TranslateSession/TranslateSession.ts +++ b/packages/tldraw/src/state/sessions/TranslateSession/TranslateSession.ts @@ -5,7 +5,7 @@ import { TLDrawShape, TLDrawBinding, Session, - Data, + TLDrawSnapshot, TLDrawCommand, TLDrawStatus, ArrowShape, @@ -58,7 +58,7 @@ export class TranslateSession extends Session { isLinked: 'left' | 'right' | 'center' | false constructor( - data: Data, + data: TLDrawSnapshot, viewport: TLBounds, point: number[], isCreate = false, @@ -72,7 +72,7 @@ export class TranslateSession extends Session { Session.cache.selectedIds = [...TLDR.getSelectedIds(data, data.appState.currentPageId)] } - start = (data: Data) => { + start = (data: TLDrawSnapshot) => { const { bindingsToDelete } = this.snapshot this.createSnapInfo(data) @@ -94,7 +94,13 @@ export class TranslateSession extends Session { } } - update = (data: Data, point: number[], shiftKey = false, altKey = false, metaKey = false) => { + update = ( + data: TLDrawSnapshot, + point: number[], + shiftKey = false, + altKey = false, + metaKey = false + ) => { const { selectedIds, initialParentChildren, initialShapes, bindingsToDelete } = this.snapshot const { currentPageId } = data.appState @@ -328,7 +334,7 @@ export class TranslateSession extends Session { } } - cancel = (data: Data) => { + cancel = (data: TLDrawSnapshot) => { const { initialShapes, bindingsToDelete } = this.snapshot const nextBindings: Record | undefined> = {} @@ -377,7 +383,7 @@ export class TranslateSession extends Session { } } - complete = (data: Data): TLDrawCommand => { + complete = (data: TLDrawSnapshot): TLDrawCommand => { const pageId = data.appState.currentPageId const { initialShapes, initialParentChildren, bindingsToDelete } = this.snapshot @@ -513,7 +519,7 @@ export class TranslateSession extends Session { } } - private createSnapInfo = async (data: Data) => { + private createSnapInfo = async (data: TLDrawSnapshot) => { const { currentPageId } = data.appState const page = data.document.pages[currentPageId] const { idsToMove } = this.snapshot @@ -536,7 +542,7 @@ export class TranslateSession extends Session { } } - private createCloneInfo = (data: Data) => { + private createCloneInfo = (data: TLDrawSnapshot) => { // Create clones when as they're needed. // Consider doing this work in a worker. @@ -634,7 +640,7 @@ export class TranslateSession extends Session { // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export function getTranslateSnapshot( - data: Data, + data: TLDrawSnapshot, linkDirection: 'left' | 'right' | 'center' | false ) { const { currentPageId } = data.appState diff --git a/packages/tldraw/src/state/shapes/DrawUtil/DrawUtil.tsx b/packages/tldraw/src/state/shapes/DrawUtil/DrawUtil.tsx index a31ff28ac..497732fe8 100644 --- a/packages/tldraw/src/state/shapes/DrawUtil/DrawUtil.tsx +++ b/packages/tldraw/src/state/shapes/DrawUtil/DrawUtil.tsx @@ -5,7 +5,11 @@ import { defaultStyle, getShapeStyle } from '../shape-styles' import { DrawShape, DashStyle, TLDrawShapeType, TLDrawTransformInfo, TLDrawMeta } from '~types' import { TLDrawShapeUtil } from '../TLDrawShapeUtil' import { intersectBoundsBounds, intersectBoundsPolyline } from '@tldraw/intersect' -import { getDrawStrokePathData, getFillPath, getSolidStrokePathData } from './drawHelpers' +import { + getDrawStrokePathTLDrawSnapshot, + getFillPath, + getSolidStrokePathTLDrawSnapshot, +} from './drawHelpers' type T = DrawShape type E = SVGSVGElement @@ -42,14 +46,14 @@ export class DrawUtil extends TLDrawShapeUtil { Component = TLDrawShapeUtil.Component(({ shape, meta, events }, ref) => { const { points, style, isComplete } = shape - const polygonPathData = React.useMemo(() => { + const polygonPathTLDrawSnapshot = React.useMemo(() => { return getFillPath(shape) }, [points, style.size]) - const pathData = React.useMemo(() => { + const pathTLDrawSnapshot = React.useMemo(() => { return style.dash === DashStyle.Draw - ? getDrawStrokePathData(shape) - : getSolidStrokePathData(shape) + ? getDrawStrokePathTLDrawSnapshot(shape) + : getSolidStrokePathTLDrawSnapshot(shape) }, [points, style.size, style.dash, isComplete]) const styles = getShapeStyle(style, meta.isDarkMode) @@ -81,7 +85,7 @@ export class DrawUtil extends TLDrawShapeUtil { {shouldFill && ( { /> )} { return ( { pointerEvents={shouldFill ? 'all' : 'stroke'} /> { Indicator = TLDrawShapeUtil.Indicator(({ shape }) => { const { points } = shape - const pathData = React.useMemo(() => { - return getSolidStrokePathData(shape) + const pathTLDrawSnapshot = React.useMemo(() => { + return getSolidStrokePathTLDrawSnapshot(shape) }, [points]) const bounds = this.getBounds(shape) @@ -161,7 +165,7 @@ export class DrawUtil extends TLDrawShapeUtil { return } - return + return }) transform = ( diff --git a/packages/tldraw/src/state/shapes/DrawUtil/drawHelpers.ts b/packages/tldraw/src/state/shapes/DrawUtil/drawHelpers.ts index f4f88dbef..6e288731b 100644 --- a/packages/tldraw/src/state/shapes/DrawUtil/drawHelpers.ts +++ b/packages/tldraw/src/state/shapes/DrawUtil/drawHelpers.ts @@ -43,7 +43,7 @@ export function getDrawStrokePoints(shape: DrawShape, options: StrokeOptions) { /** * Get path data for a stroke with the DashStyle.Draw dash style. */ -export function getDrawStrokePathData(shape: DrawShape) { +export function getDrawStrokePathTLDrawSnapshot(shape: DrawShape) { if (shape.points.length < 2) return '' const options = getFreehandOptions(shape) @@ -60,7 +60,7 @@ export function getDrawStrokePathData(shape: DrawShape) { /** * Get SVG path data for a shape that has a DashStyle other than DashStyles.Draw. */ -export function getSolidStrokePathData(shape: DrawShape) { +export function getSolidStrokePathTLDrawSnapshot(shape: DrawShape) { const { points } = shape if (points.length < 2) return 'M 0 0 L 0 0' diff --git a/packages/tldraw/src/state/shapes/EllipseUtil/EllipseUtil.tsx b/packages/tldraw/src/state/shapes/EllipseUtil/EllipseUtil.tsx index 6cb0e781b..229692430 100644 --- a/packages/tldraw/src/state/shapes/EllipseUtil/EllipseUtil.tsx +++ b/packages/tldraw/src/state/shapes/EllipseUtil/EllipseUtil.tsx @@ -13,7 +13,7 @@ import { import { BINDING_DISTANCE } from '~constants' import { TLDrawShapeUtil } from '../TLDrawShapeUtil' import { intersectLineSegmentEllipse, intersectRayEllipse } from '@tldraw/intersect' -import { getEllipseIndicatorPathData, getEllipsePath } from './ellipseHelpers' +import { getEllipseIndicatorPathTLDrawSnapshot, getEllipsePath } from './ellipseHelpers' type T = EllipseShape type E = SVGSVGElement @@ -71,7 +71,7 @@ export class EllipseUtil extends TLDrawShapeUtil { /> )} { ) Indicator = TLDrawShapeUtil.Indicator(({ shape }) => { - return + return }) getBounds = (shape: T) => { diff --git a/packages/tldraw/src/state/shapes/EllipseUtil/ellipseHelpers.ts b/packages/tldraw/src/state/shapes/EllipseUtil/ellipseHelpers.ts index 6cf6d5e57..860ae40f7 100644 --- a/packages/tldraw/src/state/shapes/EllipseUtil/ellipseHelpers.ts +++ b/packages/tldraw/src/state/shapes/EllipseUtil/ellipseHelpers.ts @@ -78,7 +78,7 @@ export function getEllipsePath(shape: EllipseShape, boundsCenter: number[]) { ) } -export function getEllipseIndicatorPathData(shape: EllipseShape, boundsCenter: number[]) { +export function getEllipseIndicatorPathTLDrawSnapshot(shape: EllipseShape, boundsCenter: number[]) { return Utils.getSvgPathFromStroke( getEllipseStrokePoints(shape, boundsCenter).map((pt) => pt.point.slice(0, 2)), false diff --git a/packages/tldraw/src/state/shapes/RectangleUtil/RectangleUtil.tsx b/packages/tldraw/src/state/shapes/RectangleUtil/RectangleUtil.tsx index dd1bec7bd..fb32efec2 100644 --- a/packages/tldraw/src/state/shapes/RectangleUtil/RectangleUtil.tsx +++ b/packages/tldraw/src/state/shapes/RectangleUtil/RectangleUtil.tsx @@ -40,7 +40,7 @@ export class RectangleUtil extends TLDrawShapeUtil { const strokeWidth = +styles.strokeWidth if (style.dash === DashStyle.Draw) { - const pathData = getRectanglePath(shape) + const pathTLDrawSnapshot = getRectanglePath(shape) return ( @@ -54,14 +54,14 @@ export class RectangleUtil extends TLDrawShapeUtil { /> )} { const sw = strokeWidth if (style.dash === DashStyle.Draw) { - return + return } return ( @@ -262,7 +262,7 @@ function getRectanglePath(shape: RectangleShape) { return Utils.getSvgPathFromStroke(stroke) } -function getRectangleIndicatorPathData(shape: RectangleShape) { +function getRectangleIndicatorPathTLDrawSnapshot(shape: RectangleShape) { const { points, options } = getDrawStrokeInfo(shape) const strokePoints = getStrokePoints(points, options) diff --git a/packages/tldraw/src/state/tools/ArrowTool/ArrowTool.spec.ts b/packages/tldraw/src/state/tools/ArrowTool/ArrowTool.spec.ts index e8a7fb227..7a4bfd9d0 100644 --- a/packages/tldraw/src/state/tools/ArrowTool/ArrowTool.spec.ts +++ b/packages/tldraw/src/state/tools/ArrowTool/ArrowTool.spec.ts @@ -3,7 +3,7 @@ import { ArrowTool } from '.' describe('ArrowTool', () => { it('creates tool', () => { - const tlstate = new TLDrawState() - new ArrowTool(tlstate) + const state = new TLDrawState() + new ArrowTool(state) }) }) diff --git a/packages/tldraw/src/state/tools/DrawTool/DrawTool.spec.ts b/packages/tldraw/src/state/tools/DrawTool/DrawTool.spec.ts index 4bdaf4be0..df465e521 100644 --- a/packages/tldraw/src/state/tools/DrawTool/DrawTool.spec.ts +++ b/packages/tldraw/src/state/tools/DrawTool/DrawTool.spec.ts @@ -3,7 +3,7 @@ import { DrawTool } from '.' describe('DrawTool', () => { it('creates tool', () => { - const tlstate = new TLDrawState() - new DrawTool(tlstate) + const state = new TLDrawState() + new DrawTool(state) }) }) diff --git a/packages/tldraw/src/state/tools/EllipseTool/EllipseTool.spec.ts b/packages/tldraw/src/state/tools/EllipseTool/EllipseTool.spec.ts index a16b9877e..d9fc38f8e 100644 --- a/packages/tldraw/src/state/tools/EllipseTool/EllipseTool.spec.ts +++ b/packages/tldraw/src/state/tools/EllipseTool/EllipseTool.spec.ts @@ -3,7 +3,7 @@ import { EllipseTool } from '.' describe('EllipseTool', () => { it('creates tool', () => { - const tlstate = new TLDrawState() - new EllipseTool(tlstate) + const state = new TLDrawState() + new EllipseTool(state) }) }) diff --git a/packages/tldraw/src/state/tools/RectangleTool/RectangleTool.spec.ts b/packages/tldraw/src/state/tools/RectangleTool/RectangleTool.spec.ts index a90e26657..c56fa17fb 100644 --- a/packages/tldraw/src/state/tools/RectangleTool/RectangleTool.spec.ts +++ b/packages/tldraw/src/state/tools/RectangleTool/RectangleTool.spec.ts @@ -3,7 +3,7 @@ import { RectangleTool } from '.' describe('RectangleTool', () => { it('creates tool', () => { - const tlstate = new TLDrawState() - new RectangleTool(tlstate) + const state = new TLDrawState() + new RectangleTool(state) }) }) diff --git a/packages/tldraw/src/state/tools/SelectTool/SelectTool.spec.ts b/packages/tldraw/src/state/tools/SelectTool/SelectTool.spec.ts index 5b43a5174..d17a7d6d8 100644 --- a/packages/tldraw/src/state/tools/SelectTool/SelectTool.spec.ts +++ b/packages/tldraw/src/state/tools/SelectTool/SelectTool.spec.ts @@ -5,8 +5,8 @@ import { SelectTool } from '.' describe('SelectTool', () => { it('creates tool', () => { - const tlstate = new TLDrawState() - new SelectTool(tlstate) + const state = new TLDrawState() + new SelectTool(state) }) }) @@ -59,8 +59,8 @@ describe('When double clicking link controls', () => { .selectNone().document it('moves all linked shapes when center is dragged', () => { - const tlstate = new TLDrawState().loadDocument(doc).select('rect2') - const tlu = new TLDrawStateUtils(tlstate) + const state = new TLDrawState().loadDocument(doc).select('rect2') + const tlu = new TLDrawStateUtils(state) tlu .pointBoundsHandle('center') @@ -71,7 +71,7 @@ describe('When double clicking link controls', () => { rect3: [300, 100], }) - tlstate.completeSession().undo() + state.completeSession().undo() tlu.expectShapesToBeAtPoints({ rect1: [0, 0], @@ -81,37 +81,37 @@ describe('When double clicking link controls', () => { }) it('moves all upstream shapes when center is dragged', () => { - const tlstate = new TLDrawState().loadDocument(doc).select('rect2') - const tlu = new TLDrawStateUtils(tlstate) + const state = new TLDrawState().loadDocument(doc).select('rect2') + const tlu = new TLDrawStateUtils(state) tlu.pointBoundsHandle('left').movePointer({ x: 100, y: 100 }) - expect(tlstate.getShape('rect1').point).toEqual([100, 100]) - expect(tlstate.getShape('rect2').point).toEqual([200, 100]) - expect(tlstate.getShape('rect3').point).toEqual([200, 0]) + expect(state.getShape('rect1').point).toEqual([100, 100]) + expect(state.getShape('rect2').point).toEqual([200, 100]) + expect(state.getShape('rect3').point).toEqual([200, 0]) }) it('moves all downstream shapes when center is dragged', () => { - const tlstate = new TLDrawState().loadDocument(doc).select('rect2') - const tlu = new TLDrawStateUtils(tlstate) + const state = new TLDrawState().loadDocument(doc).select('rect2') + const tlu = new TLDrawStateUtils(state) tlu.pointBoundsHandle('right').movePointer({ x: 100, y: 100 }) - expect(tlstate.getShape('rect1').point).toEqual([0, 0]) - expect(tlstate.getShape('rect2').point).toEqual([200, 100]) - expect(tlstate.getShape('rect3').point).toEqual([300, 100]) + expect(state.getShape('rect1').point).toEqual([0, 0]) + expect(state.getShape('rect2').point).toEqual([200, 100]) + expect(state.getShape('rect3').point).toEqual([300, 100]) }) it('selects all linked shapes when center is double clicked', () => { - const tlstate = new TLDrawState().loadDocument(doc).select('rect2') - const tlu = new TLDrawStateUtils(tlstate) + const state = new TLDrawState().loadDocument(doc).select('rect2') + const tlu = new TLDrawStateUtils(state) tlu.doubleClickBoundHandle('center').expectSelectedIdsToBe(['rect2', 'rect1', 'rect3']) }) it('selects all linked shapes and arrows when center is double clicked while holding shift', () => { - const tlstate = new TLDrawState().loadDocument(doc).select('rect2') - const tlu = new TLDrawStateUtils(tlstate) + const state = new TLDrawState().loadDocument(doc).select('rect2') + const tlu = new TLDrawStateUtils(state) tlu .doubleClickBoundHandle('center', { shiftKey: true }) @@ -119,15 +119,15 @@ describe('When double clicking link controls', () => { }) it('selects all upstream linked shapes when left is double clicked', () => { - const tlstate = new TLDrawState().loadDocument(doc).select('rect2') - const tlu = new TLDrawStateUtils(tlstate) + const state = new TLDrawState().loadDocument(doc).select('rect2') + const tlu = new TLDrawStateUtils(state) tlu.doubleClickBoundHandle('left').expectSelectedIdsToBe(['rect1', 'rect2']) }) it('selects all upstream linked shapes and arrows when left is double clicked with shift', () => { - const tlstate = new TLDrawState().loadDocument(doc).select('rect2') - const tlu = new TLDrawStateUtils(tlstate) + const state = new TLDrawState().loadDocument(doc).select('rect2') + const tlu = new TLDrawStateUtils(state) tlu .doubleClickBoundHandle('left', { shiftKey: true }) @@ -135,15 +135,15 @@ describe('When double clicking link controls', () => { }) it('selects all downstream linked shapes when right is double clicked', () => { - const tlstate = new TLDrawState().loadDocument(doc).select('rect2') - const tlu = new TLDrawStateUtils(tlstate) + const state = new TLDrawState().loadDocument(doc).select('rect2') + const tlu = new TLDrawStateUtils(state) tlu.doubleClickBoundHandle('right').expectSelectedIdsToBe(['rect2', 'rect3']) }) it('selects all downstream linked shapes and arrows when right is double clicked with shift', () => { - const tlstate = new TLDrawState().loadDocument(doc).select('rect2') - const tlu = new TLDrawStateUtils(tlstate) + const state = new TLDrawState().loadDocument(doc).select('rect2') + const tlu = new TLDrawStateUtils(state) tlu .doubleClickBoundHandle('right', { shiftKey: true }) @@ -153,42 +153,42 @@ describe('When double clicking link controls', () => { describe('When selecting grouped shapes', () => { it('Selects the group on single click', () => { - const tlstate = new TLDrawState().loadDocument(mockDocument).group(['rect1', 'rect2'], 'groupA') + const state = new TLDrawState().loadDocument(mockDocument).group(['rect1', 'rect2'], 'groupA') - new TLDrawStateUtils(tlstate).clickShape('rect1') + new TLDrawStateUtils(state).clickShape('rect1') - expect(tlstate.selectedIds).toStrictEqual(['groupA']) + expect(state.selectedIds).toStrictEqual(['groupA']) }) it('Drills in and selects the child on double click', () => { - const tlstate = new TLDrawState().loadDocument(mockDocument).group(['rect1', 'rect2'], 'groupA') + const state = new TLDrawState().loadDocument(mockDocument).group(['rect1', 'rect2'], 'groupA') - new TLDrawStateUtils(tlstate).doubleClickShape('rect1') + new TLDrawStateUtils(state).doubleClickShape('rect1') - expect(tlstate.selectedIds).toStrictEqual(['rect1']) + expect(state.selectedIds).toStrictEqual(['rect1']) }) it('Selects a sibling on single click after drilling', () => { - const tlstate = new TLDrawState().loadDocument(mockDocument).group(['rect1', 'rect2'], 'groupA') + const state = new TLDrawState().loadDocument(mockDocument).group(['rect1', 'rect2'], 'groupA') - new TLDrawStateUtils(tlstate).doubleClickShape('rect1').clickShape('rect2') + new TLDrawStateUtils(state).doubleClickShape('rect1').clickShape('rect2') - expect(tlstate.selectedIds).toStrictEqual(['rect2']) + expect(state.selectedIds).toStrictEqual(['rect2']) }) it('Selects the group again after selecting a different shape', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .selectAll() .group(['rect1', 'rect2'], 'groupA') - new TLDrawStateUtils(tlstate).doubleClickShape('rect1').clickShape('rect3').clickShape('rect1') + new TLDrawStateUtils(state).doubleClickShape('rect1').clickShape('rect3').clickShape('rect1') - expect(tlstate.selectedIds).toStrictEqual(['groupA']) + expect(state.selectedIds).toStrictEqual(['groupA']) }) it('Selects grouped text on double click', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .createShapes({ id: 'text1', @@ -197,14 +197,14 @@ describe('When selecting grouped shapes', () => { }) .group(['rect1', 'rect2', 'text1'], 'groupA') - new TLDrawStateUtils(tlstate).doubleClickShape('text1') + new TLDrawStateUtils(state).doubleClickShape('text1') - expect(tlstate.selectedIds).toStrictEqual(['text1']) - expect(tlstate.pageState.editingId).toBeUndefined() + expect(state.selectedIds).toStrictEqual(['text1']) + expect(state.pageState.editingId).toBeUndefined() }) it('Edits grouped text on double click after selecting', () => { - const tlstate = new TLDrawState() + const state = new TLDrawState() .loadDocument(mockDocument) .createShapes({ id: 'text1', @@ -213,9 +213,9 @@ describe('When selecting grouped shapes', () => { }) .group(['rect1', 'rect2', 'text1'], 'groupA') - new TLDrawStateUtils(tlstate).doubleClickShape('text1').doubleClickShape('text1') + new TLDrawStateUtils(state).doubleClickShape('text1').doubleClickShape('text1') - expect(tlstate.selectedIds).toStrictEqual(['text1']) - expect(tlstate.pageState.editingId).toBe('text1') + expect(state.selectedIds).toStrictEqual(['text1']) + expect(state.pageState.editingId).toBe('text1') }) }) diff --git a/packages/tldraw/src/state/tools/StickyTool/StickyTool.spec.ts b/packages/tldraw/src/state/tools/StickyTool/StickyTool.spec.ts index e69e0642e..a092cc22f 100644 --- a/packages/tldraw/src/state/tools/StickyTool/StickyTool.spec.ts +++ b/packages/tldraw/src/state/tools/StickyTool/StickyTool.spec.ts @@ -3,7 +3,7 @@ import { StickyTool } from '.' describe('StickyTool', () => { it('creates tool', () => { - const tlstate = new TLDrawState() - new StickyTool(tlstate) + const state = new TLDrawState() + new StickyTool(state) }) }) diff --git a/packages/tldraw/src/state/tools/TextTool/TextTool.spec.ts b/packages/tldraw/src/state/tools/TextTool/TextTool.spec.ts index 26cd203b6..187d2b544 100644 --- a/packages/tldraw/src/state/tools/TextTool/TextTool.spec.ts +++ b/packages/tldraw/src/state/tools/TextTool/TextTool.spec.ts @@ -3,7 +3,7 @@ import { TextTool } from '.' describe('TextTool', () => { it('creates tool', () => { - const tlstate = new TLDrawState() - new TextTool(tlstate) + const state = new TLDrawState() + new TextTool(state) }) }) diff --git a/packages/tldraw/src/test/TLDrawStateUtils.tsx b/packages/tldraw/src/test/TLDrawStateUtils.tsx index b700704d5..0c3b3e338 100644 --- a/packages/tldraw/src/test/TLDrawStateUtils.tsx +++ b/packages/tldraw/src/test/TLDrawStateUtils.tsx @@ -12,30 +12,30 @@ interface PointerOptions { } export class TLDrawStateUtils { - tlstate: TLDrawState + state: TLDrawState - constructor(tlstate: TLDrawState) { - this.tlstate = tlstate + constructor(state: TLDrawState) { + this.state = state } movePointer = (options: PointerOptions = {}) => { - const { tlstate } = this - tlstate.onPointerMove(inputs.pointerMove(this.getPoint(options), ''), {} as React.PointerEvent) + const { state } = this + state.onPointerMove(inputs.pointerMove(this.getPoint(options), ''), {} as React.PointerEvent) return this } hoverShape = (id: string, options: PointerOptions = {}) => { - const { tlstate } = this - tlstate.onHoverShape(inputs.pointerDown(this.getPoint(options), id), {} as React.PointerEvent) + const { state } = this + state.onHoverShape(inputs.pointerDown(this.getPoint(options), id), {} as React.PointerEvent) return this } pointCanvas = (options: PointerOptions = {}) => { - this.tlstate.onPointCanvas( + this.state.onPointCanvas( inputs.pointerDown(this.getPoint(options), 'canvas'), {} as React.PointerEvent ) - this.tlstate.onPointerDown( + this.state.onPointerDown( inputs.pointerDown(this.getPoint(options), 'canvas'), {} as React.PointerEvent ) @@ -43,11 +43,11 @@ export class TLDrawStateUtils { } pointShape = (id: string, options: PointerOptions = {}) => { - this.tlstate.onPointShape( + this.state.onPointShape( inputs.pointerDown(this.getPoint(options), id), {} as React.PointerEvent ) - this.tlstate.onPointerDown( + this.state.onPointerDown( inputs.pointerDown(this.getPoint(options), 'canvas'), {} as React.PointerEvent ) @@ -55,11 +55,11 @@ export class TLDrawStateUtils { } doubleClickShape = (id: string, options: PointerOptions = {}) => { - this.tlstate.onDoubleClickShape( + this.state.onDoubleClickShape( inputs.pointerDown(this.getPoint(options), id), {} as React.PointerEvent ) - this.tlstate.onPointerDown( + this.state.onPointerDown( inputs.pointerDown(this.getPoint(options), 'canvas'), {} as React.PointerEvent ) @@ -67,11 +67,11 @@ export class TLDrawStateUtils { } pointBounds = (options: PointerOptions = {}) => { - this.tlstate.onPointBounds( + this.state.onPointBounds( inputs.pointerDown(this.getPoint(options), 'bounds'), {} as React.PointerEvent ) - this.tlstate.onPointerDown( + this.state.onPointerDown( inputs.pointerDown(this.getPoint(options), 'canvas'), {} as React.PointerEvent ) @@ -79,11 +79,11 @@ export class TLDrawStateUtils { } pointBoundsHandle = (id: TLBoundsHandle, options: PointerOptions = {}) => { - this.tlstate.onPointBoundsHandle( + this.state.onPointBoundsHandle( inputs.pointerDown(this.getPoint(options), id), {} as React.PointerEvent ) - this.tlstate.onPointerDown( + this.state.onPointerDown( inputs.pointerDown(this.getPoint(options), 'canvas'), {} as React.PointerEvent ) @@ -91,11 +91,11 @@ export class TLDrawStateUtils { } doubleClickBoundHandle = (id: TLBoundsHandle, options: PointerOptions = {}) => { - this.tlstate.onDoubleClickBoundsHandle( + this.state.onDoubleClickBoundsHandle( inputs.pointerDown(this.getPoint(options), id), {} as React.PointerEvent ) - this.tlstate.onPointerDown( + this.state.onPointerDown( inputs.pointerDown(this.getPoint(options), 'canvas'), {} as React.PointerEvent ) @@ -103,7 +103,7 @@ export class TLDrawStateUtils { } stopPointing = (target = 'canvas', options: PointerOptions = {}) => { - this.tlstate.onPointerUp( + this.state.onPointerUp( inputs.pointerUp(this.getPoint(options), target), {} as React.PointerEvent ) @@ -152,20 +152,20 @@ export class TLDrawStateUtils { } expectSelectedIdsToBe = (b: string[]) => { - expect(new Set(this.tlstate.selectedIds)).toEqual(new Set(b)) + expect(new Set(this.state.selectedIds)).toEqual(new Set(b)) return this } expectShapesToBeAtPoints = (shapes: Record) => { Object.entries(shapes).forEach(([id, point]) => { - expect(this.tlstate.getShape(id).point).toEqual(point) + expect(this.state.getShape(id).point).toEqual(point) }) return this } expectShapesToHaveProps = (shapes: Record>) => { Object.entries(shapes).forEach(([id, props]) => { - const shape = this.tlstate.getShape(id) + const shape = this.state.getShape(id) Object.entries(props).forEach(([key, value]) => { expect(shape[key as keyof T]).toEqual(value) }) diff --git a/packages/tldraw/src/test/renderWithContext.tsx b/packages/tldraw/src/test/renderWithContext.tsx index 55d139b03..22fa2ff1f 100644 --- a/packages/tldraw/src/test/renderWithContext.tsx +++ b/packages/tldraw/src/test/renderWithContext.tsx @@ -6,9 +6,9 @@ import { mockDocument } from './mockDocument' import { render } from '@testing-library/react' export const Wrapper: React.FC = ({ children }) => { - const [tlstate] = React.useState(() => new TLDrawState()) + const [state] = React.useState(() => new TLDrawState()) const [context] = React.useState(() => { - return { tlstate, useSelector: tlstate.useStore, callbacks: {} } + return { state, useSelector: state.useStore, callbacks: {} } }) const rWrapper = React.useRef(null) @@ -17,8 +17,8 @@ export const Wrapper: React.FC = ({ children }) => { React.useEffect(() => { if (!document) return - tlstate.loadDocument(mockDocument) - }, [document, tlstate]) + state.loadDocument(mockDocument) + }, [document, state]) return ( diff --git a/packages/tldraw/src/types.ts b/packages/tldraw/src/types.ts index 2fdc5421a..f1c920903 100644 --- a/packages/tldraw/src/types.ts +++ b/packages/tldraw/src/types.ts @@ -29,9 +29,9 @@ export interface TLDrawTransformInfo { } // old -export type TLStore = StoreApi +export type TLStore = StoreApi -export type TLChange = Data +export type TLChange = TLDrawSnapshot export type TLDrawPage = TLPage @@ -79,7 +79,7 @@ export type TLDrawShapeProps = TLShape TLDrawMeta > -export interface Data { +export interface TLDrawSnapshot { settings: TLDrawSettings appState: { selectedStyle: ShapeStyles @@ -102,9 +102,9 @@ export interface Data { } } -export type TLDrawPatch = Patch +export type TLDrawPatch = Patch -export type TLDrawCommand = Command +export type TLDrawCommand = Command export type PagePartial = { shapes: Patch @@ -131,16 +131,18 @@ export enum SessionType { export abstract class Session { static type: SessionType abstract status: TLDrawStatus - abstract start: (data: Readonly) => TLDrawPatch | undefined + abstract start: (TLDrawSnapshot: Readonly) => TLDrawPatch | undefined abstract update: ( - data: Readonly, + TLDrawSnapshot: Readonly, point: number[], shiftKey?: boolean, altKey?: boolean, metaKey?: boolean ) => TLDrawPatch | undefined - abstract complete: (data: Readonly) => TLDrawPatch | TLDrawCommand | undefined - abstract cancel: (data: Readonly) => TLDrawPatch | undefined + abstract complete: ( + TLDrawSnapshot: Readonly + ) => TLDrawPatch | TLDrawCommand | undefined + abstract cancel: (TLDrawSnapshot: Readonly) => TLDrawPatch | undefined viewport: TLBounds diff --git a/vscode/editor/src/app.tsx b/vscode/editor/src/app.tsx index 3e797e4eb..b953b9027 100644 --- a/vscode/editor/src/app.tsx +++ b/vscode/editor/src/app.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { TLDraw, TLDrawState, TLDrawDocument, Data, TLDrawFile } from '@tldraw/tldraw' +import { TLDraw, TLDrawState, TLDrawSnapshot, TLDrawFile } from '@tldraw/tldraw' import { vscode } from './utils/vscode' import { eventsRegex } from './utils/eventsRegex' import { defaultDocument } from './utils/defaultDocument' @@ -12,16 +12,14 @@ declare let currentFile: TLDrawFile export default function App(): JSX.Element { const rTLDrawState = React.useRef() - // When the editor mounts, save the tlstate instance in a ref. + // When the editor mounts, save the state instance in a ref. const handleMount = React.useCallback((tldr: TLDrawState) => { rTLDrawState.current = tldr }, []) // When the editor's document changes, post the stringified document to the vscode extension. - const handleChange = React.useCallback((tldr: TLDrawState, data: Data, type: string) => { - if (type.search(eventsRegex) === 0) { - vscode.postMessage({ type: UI_EVENT.TLDRAW_UPDATED, text: JSON.stringify(tldr.document) }) - } + const handlePersist = React.useCallback((tldr: TLDrawState) => { + vscode.postMessage({ type: UI_EVENT.TLDRAW_UPDATED, text: JSON.stringify(tldr.document) }) }, []) return ( @@ -30,7 +28,7 @@ export default function App(): JSX.Element { id={currentFile.document.id} document={currentFile.document ?? defaultDocument} onMount={handleMount} - onChange={handleChange} + onPersist={handlePersist} autofocus />
diff --git a/vscode/editor/tsconfig.tsbuildinfo b/vscode/editor/tsconfig.tsbuildinfo index 039bd6d5c..dec7891d1 100644 --- a/vscode/editor/tsconfig.tsbuildinfo +++ b/vscode/editor/tsconfig.tsbuildinfo @@ -396,7 +396,7 @@ "signature": "1444afb2d3c50b5a15354934187d75bc9a7ca2d10bf20fe9c79cbcd1f8548549", "affectsGlobalScope": false }, - "../tldraw/dist/types/state/tlstate.d.ts": { + "../tldraw/dist/types/state/state.d.ts": { "version": "3a31dc5b6306ee6cff5e03e4a3ab1eda22f07231bc207f77807915e7c01a96a9", "signature": "3a31dc5b6306ee6cff5e03e4a3ab1eda22f07231bc207f77807915e7c01a96a9", "affectsGlobalScope": false @@ -1245,9 +1245,9 @@ "../tldraw/dist/types/types.d.ts" ], "../tldraw/dist/types/state/index.d.ts": [ - "../tldraw/dist/types/state/tlstate.d.ts" + "../tldraw/dist/types/state/state.d.ts" ], - "../tldraw/dist/types/state/tlstate.d.ts": [ + "../tldraw/dist/types/state/state.d.ts": [ "../../node_modules/rko/dist/types/index.d.ts", "../core/dist/types/index.d.ts", "../tldraw/dist/types/types.d.ts" @@ -1621,9 +1621,9 @@ "../tldraw/dist/types/types.d.ts" ], "../tldraw/dist/types/state/index.d.ts": [ - "../tldraw/dist/types/state/tlstate.d.ts" + "../tldraw/dist/types/state/state.d.ts" ], - "../tldraw/dist/types/state/tlstate.d.ts": [ + "../tldraw/dist/types/state/state.d.ts": [ "../../node_modules/rko/dist/types/index.d.ts", "../core/dist/types/index.d.ts", "../tldraw/dist/types/types.d.ts" @@ -1792,7 +1792,7 @@ "../tldraw/dist/types/shape/shape-styles.d.ts", "../tldraw/dist/types/shape/shape-utils.d.ts", "../tldraw/dist/types/state/index.d.ts", - "../tldraw/dist/types/state/tlstate.d.ts", + "../tldraw/dist/types/state/state.d.ts", "../tldraw/dist/types/types.d.ts" ] }, diff --git a/vscode/extension/examples/2.tldr b/vscode/extension/examples/2.tldr index dc92f9eb5..81abec0ff 100644 --- a/vscode/extension/examples/2.tldr +++ b/vscode/extension/examples/2.tldr @@ -1,1026 +1,1023 @@ { + "id": "doc", "name": "New Document", - "fileHandle": {}, - "document": { - "id": "doc", - "version": 13, - "pages": { - "page": { - "id": "page", - "name": "Page 1", - "childIndex": 1, - "shapes": { - "d8f1475a-d37a-402c-3669-01944f9b84e8": { - "id": "d8f1475a-d37a-402c-3669-01944f9b84e8", - "type": "draw", - "name": "Draw", - "parentId": "page", - "childIndex": 1, - "point": [ - 313.77, - 287.84 - ], - "rotation": 0, - "style": { - "color": "black", - "size": "large", - "isFilled": false, - "dash": "draw" - }, - "points": [ - [ - 0, - 38.91, - 0.5 - ], - [ - 0, - 37.08, - 0.5 - ], - [ - 0, - 35.36, - 0.5 - ], - [ - 6.19, - 25.33, - 0.5 - ], - [ - 12.4, - 17.21, - 0.5 - ], - [ - 20.18, - 9.42, - 0.5 - ], - [ - 26.19, - 4.98, - 0.5 - ], - [ - 32.68, - 1.73, - 0.5 - ], - [ - 40.09, - 0, - 0.5 - ], - [ - 48.14, - 0, - 0.5 - ], - [ - 56.18, - 0, - 0.5 - ], - [ - 63.05, - 2.1599999999999966, - 0.5 - ], - [ - 70.5, - 8.429999999999996, - 0.5 - ], - [ - 74.77, - 15.179999999999996, - 0.5 - ], - [ - 76.5, - 23.619999999999997, - 0.5 - ], - [ - 76.5, - 38.91, - 0.5 - ], - [ - 68.56, - 58.839999999999996, - 0.5 - ], - [ - 57.59, - 75.91, - 0.5 - ], - [ - 42.45, - 97.68, - 0.5 - ], - [ - 30.36, - 115.05, - 0.5 - ], - [ - 25.65, - 122.03, - 0.5 - ], - [ - 21.21, - 128.03, - 0.5 - ], - [ - 19.68, - 130.31, - 0.5 - ], - [ - 19.13, - 131.13, - 0.5 - ], - [ - 19.13, - 131.41, - 0.5 - ], - [ - 21.29, - 129.5, - 0.5 - ], - [ - 25.29, - 126.02, - 0.5 - ], - [ - 32.08, - 122.03, - 0.5 - ], - [ - 41.43, - 118.28999999999999, - 0.5 - ], - [ - 52.94, - 116.24, - 0.5 - ], - [ - 64.4, - 115.53999999999999, - 0.5 - ], - [ - 76.53, - 115.53999999999999, - 0.5 - ], - [ - 86.91, - 115.53999999999999, - 0.5 - ], - [ - 97.28, - 117.47999999999999, - 0.5 - ], - [ - 104.2, - 118.56, - 0.5 - ], - [ - 111.61, - 120.28999999999999, - 0.5 - ], - [ - 115.79, - 121.21, - 0.5 - ], - [ - 118.34, - 121.97, - 0.5 - ], - [ - 118.89, - 122.24, - 0.5 - ], - [ - 119.16, - 122.24, - 0.5 - ] - ], - "isComplete": true - }, - "4a8c1095-335e-42d6-30c9-ff4171dd364e": { - "id": "4a8c1095-335e-42d6-30c9-ff4171dd364e", - "type": "draw", - "name": "Draw", - "parentId": "page", - "childIndex": 2, - "point": [ - 239.02, - 167.49 - ], - "rotation": 0.0759349816929582, - "style": { - "color": "black", - "size": "large", - "isFilled": false, - "dash": "draw" - }, - "points": [ - [ - 72.8938650204388, - 14.590000000000002, - 0.5 - ], - [ - 68.8781905397514, - 19.51, - 0.5 - ], - [ - 61.84336030699925, - 27.34, - 0.5 - ], - [ - 54.16720613998498, - 35.12, - 0.5 - ], - [ - 44.458548427463086, - 44.24, - 0.5 - ], - [ - 23.92631600900976, - 62.11, - 0.5 - ], - [ - 17.207214482355887, - 67.74, - 0.5 - ], - [ - 8.31747810127638, - 73.53, - 0.5 - ], - [ - 3.7196788187202796, - 77.64, - 0.5 - ], - [ - 1.4701117877700842, - 79.15, - 0.5 - ], - [ - 0.552525235671978, - 79.75, - 0.5 - ], - [ - 0, - 80.29, - 0.5 - ], - [ - 0, - 80.57, - 0.5 - ], - [ - 1.9338383248519229, - 80.57, - 0.5 - ], - [ - 7.547889380161841, - 80.57, - 0.5 - ], - [ - 15.756835738716942, - 81.75, - 0.5 - ], - [ - 26.99480437140235, - 83.09, - 0.5 - ], - [ - 40.689536998414944, - 83.09, - 0.5 - ], - [ - 51.35524735129723, - 83.09, - 0.5 - ], - [ - 62.71161424876949, - 83.09, - 0.5 - ], - [ - 70.9205606073246, - 83.09, - 0.5 - ], - [ - 76.5346116626345, - 83.09, - 0.5 - ], - [ - 80.6292183198465, - 83.09, - 0.5 - ], - [ - 82.98731709351797, - 83.09, - 0.5 - ], - [ - 84.26996496204221, - 83.41, - 0.5 - ], - [ - 84.81262367564861, - 83.41, - 0.5 - ], - [ - 85.07901977141903, - 83.41, - 0.5 - ], - [ - 84.32916409443563, - 82.04, - 0.5 - ], - [ - 82.82945274046884, - 77.05, - 0.5 - ], - [ - 80.68841745223992, - 70.45, - 0.5 - ], - [ - 78.12312171519146, - 60.059999999999995, - 0.5 - ], - [ - 75.83408859597897, - 44.82, - 0.5 - ], - [ - 75.02503378660215, - 28.34, - 0.5 - ], - [ - 75.02503378660215, - 16.24, - 0.5 - ], - [ - 74.38370985234003, - 6.879999999999999, - 0.5 - ], - [ - 74.38370985234003, - 2.73, - 0.5 - ], - [ - 74.00878201384833, - 0.93, - 0.5 - ], - [ - 74.00878201384833, - 0, - 0.5 - ], - [ - 74.22584549929091, - 3.552713678800501e-15, - 0.5 - ], - [ - 78.34018520063404, - 0.9100000000000037, - 0.5 - ], - [ - 84.84222324184536, - 1.9900000000000038, - 0.5 - ], - [ - 97.31350713272714, - 2.690000000000003, - 0.5 - ], - [ - 113.48473679819806, - 3.450000000000003, - 0.5 - ], - [ - 133.957770084258, - 3.450000000000003, - 0.5 - ], - [ - 157.27236172520233, - 3.450000000000003, - 0.5 - ], - [ - 177.74539501126222, - 3.450000000000003, - 0.5 - ], - [ - 195.16967297906066, - 3.450000000000003, - 0.5 - ], - [ - 204.61193459581213, - 3.450000000000003, - 0.5 - ], - [ - 212.92941269708854, - 2.9000000000000044, - 0.5 - ], - [ - 217.0240193543005, - 2.9000000000000044, - 0.5 - ], - [ - 219.05652289980813, - 2.9000000000000044, - 0.5 - ], - [ - 219.58931509134896, - 2.9000000000000044, - 0.5 - ], - [ - 219.86557770918498, - 2.9000000000000044, - 0.5 - ], - [ - 219.86557770918498, - 3.780000000000003, - 0.5 - ], - [ - 219.86557770918498, - 7.930000000000003, - 0.5 - ], - [ - 219.86557770918498, - 16.260000000000005, - 0.5 - ], - [ - 219.86557770918498, - 28.90000000000001, - 0.5 - ], - [ - 219.86557770918498, - 44.03, - 0.5 - ], - [ - 220.6055668641028, - 57.87, - 0.5 - ], - [ - 222.1447443063319, - 74.34, - 0.5 - ], - [ - 223.69378827062653, - 90.82000000000001, - 0.5 - ], - [ - 225.34149745557687, - 110.10000000000001, - 0.5 - ], - [ - 227.82786101610077, - 139.69, - 0.5 - ], - [ - 229.58410194377242, - 161.95000000000002, - 0.5 - ], - [ - 231.27127721698508, - 182.52, - 0.5 - ], - [ - 232.07046550429635, - 201.8, - 0.5 - ], - [ - 232.91898640193543, - 222.33, - 0.5 - ], - [ - 233.7181746892467, - 240.09, - 0.5 - ], - [ - 233.7181746892467, - 257.88, - 0.5 - ], - [ - 233.7181746892467, - 274.36, - 0.5 - ], - [ - 234.57656210895138, - 293.6, - 0.5 - ], - [ - 234.57656210895138, - 311.41999999999996, - 0.5 - ], - [ - 235.10935430049219, - 320.99, - 0.5 - ], - [ - 235.8000108450822, - 331.47, - 0.5 - ], - [ - 236.2736039042296, - 337.14, - 0.5 - ], - [ - 236.2736039042296, - 339.52, - 0.5 - ], - [ - 236.54000000000002, - 340.45000000000005, - 0.5 - ], - [ - 236.54000000000002, - 341, - 0.5 - ], - [ - 236.54000000000002, - 341.27, - 0.5 - ], - [ - 236.32293651455745, - 341.27, - 0.5 - ], - [ - 232.19873029114876, - 341.27, - 0.5 - ], - [ - 222.96366563777426, - 341.27, - 0.5 - ], - [ - 210.4825152248269, - 343.37, - 0.5 - ], - [ - 195.5643338616835, - 346.39, - 0.5 - ], - [ - 177.97232501877033, - 348.82000000000005, - 0.5 - ], - [ - 160.38031617585716, - 350.42999999999995, - 0.5 - ], - [ - 142.857372987403, - 351.23, - 0.5 - ], - [ - 122.38433970134314, - 352.1, - 0.5 - ], - [ - 101.91130641528324, - 352.1, - 0.5 - ], - [ - 85.66114457328774, - 352.90999999999997, - 0.5 - ], - [ - 74.99543422040544, - 353.45000000000005, - 0.5 - ], - [ - 65.76036956703096, - 354.09000000000003, - 0.5 - ], - [ - 60.146318511721056, - 354.63, - 0.5 - ], - [ - 57.79808626011515, - 355.06000000000006, - 0.5 - ], - [ - 56.39704012680406, - 355.06000000000006, - 0.5 - ], - [ - 56.13064403103365, - 355.06000000000006, - 0.5 - ], - [ - 55.88398097939437, - 355.06000000000006, - 0.5 - ], - [ - 55.65705097188623, - 355.06000000000006, - 0.5 - ], - [ - 55.420254442312526, - 355.06000000000006, - 0.5 - ], - [ - 55.420254442312526, - 354.83000000000004, - 0.5 - ], - [ - 55.420254442312526, - 354.28, - 0.5 - ], - [ - 55.420254442312526, - 352.58000000000004, - 0.5 - ], - [ - 55.420254442312526, - 351.91999999999996, - 0.5 - ], - [ - 53.14108784516562, - 345.85, - 0.5 - ], - [ - 51.64137649119882, - 340.86, - 0.5 - ], - [ - 49.766737298740324, - 331.5, - 0.5 - ], - [ - 47.94143071660968, - 322.14, - 0.5 - ], - [ - 46.65878284808544, - 311.76, - 0.5 - ], - [ - 44.68547843497124, - 300.24, - 0.5 - ], - [ - 43.560694919496136, - 292.61, - 0.5 - ], - [ - 41.47885876366065, - 281.25, - 0.5 - ], - [ - 38.07490865103865, - 269.57, - 0.5 - ], - [ - 36.46666555435057, - 263.65999999999997, - 0.5 - ], - [ - 34.21709852340037, - 256.25, - 0.5 - ], - [ - 32.07606323517146, - 249.75, - 0.5 - ], - [ - 30.477686660548947, - 243.28, - 0.5 - ], - [ - 28.020922666221765, - 233.93, - 0.5 - ], - [ - 26.146283473763262, - 224.57, - 0.5 - ], - [ - 24.86363560523903, - 214.25, - 0.5 - ], - [ - 22.84099858179697, - 202.72999999999996, - 0.5 - ], - [ - 20.759162425961478, - 191.43, - 0.5 - ], - [ - 18.04586885792944, - 181.42000000000002, - 0.5 - ], - [ - 15.579238341536684, - 172.06, - 0.5 - ], - [ - 13.467802619504488, - 165.54, - 0.5 - ], - [ - 10.053985984816912, - 154.01, - 0.5 - ], - [ - 7.912950696587994, - 147.51, - 0.5 - ], - [ - 6.788167181112896, - 140.09, - 0.5 - ], - [ - 6.255374989572053, - 133.62, - 0.5 - ], - [ - 5.712716275965649, - 127.13, - 0.5 - ], - [ - 5.712716275965649, - 122.97, - 0.5 - ], - [ - 5.712716275965649, - 120.09, - 0.5 - ], - [ - 5.712716275965649, - 118.77000000000002, - 0.5 - ], - [ - 5.712716275965649, - 118.22, - 0.5 - ], - [ - 5.712716275965649, - 117.94, - 0.5 - ], - [ - 5.712716275965649, - 117.69999999999999, - 0.5 - ], - [ - 5.712716275965649, - 117.45, - 0.5 - ], - [ - 5.712716275965649, - 114.57000000000001, - 0.5 - ], - [ - 5.712716275965649, - 109.69999999999999, - 0.5 - ], - [ - 5.712716275965649, - 101.36, - 0.5 - ], - [ - 5.712716275965649, - 96.37, - 0.5 - ], - [ - 5.712716275965649, - 92.81, - 0.5 - ], - [ - 5.712716275965649, - 90.52, - 0.5 - ], - [ - 5.712716275965649, - 89.97, - 0.5 - ], - [ - 5.712716275965649, - 89.68999999999998, - 0.5 - ], - [ - 5.712716275965649, - 89.46000000000001, - 0.5 - ], - [ - 6.2060423792442005, - 89.46000000000001, - 0.5 - ] - ], - "isComplete": true - }, - "9fa6dcc1-3d5e-4b7b-1afd-e9f5dd8742aa": { - "id": "9fa6dcc1-3d5e-4b7b-1afd-e9f5dd8742aa", - "type": "text", - "name": "Text", - "parentId": "page", - "childIndex": 3, - "point": [ - 509.18, - 262.72 - ], - "rotation": 0, - "text": "Save files!", - "style": { - "color": "black", - "size": "large", - "isFilled": false, - "dash": "draw" - } - } - }, - "bindings": {} - } - }, - "pageStates": { - "page": { - "id": "page", - "selectedIds": [ - "9fa6dcc1-3d5e-4b7b-1afd-e9f5dd8742aa" - ], - "camera": { + "version": 13, + "pages": { + "page": { + "id": "page", + "name": "Page 1", + "childIndex": 1, + "shapes": { + "d8f1475a-d37a-402c-3669-01944f9b84e8": { + "id": "d8f1475a-d37a-402c-3669-01944f9b84e8", + "type": "draw", + "name": "Draw", + "parentId": "page", + "childIndex": 1, "point": [ - -130.08, - -111.66 + 317.52, + 317.56 ], - "zoom": 1.1462851349067762 + "rotation": 0, + "style": { + "color": "black", + "size": "large", + "isFilled": false, + "dash": "draw" + }, + "points": [ + [ + 0, + 38.91, + 0.5 + ], + [ + 0, + 37.08, + 0.5 + ], + [ + 0, + 35.36, + 0.5 + ], + [ + 6.19, + 25.33, + 0.5 + ], + [ + 12.4, + 17.21, + 0.5 + ], + [ + 20.18, + 9.42, + 0.5 + ], + [ + 26.19, + 4.98, + 0.5 + ], + [ + 32.68, + 1.73, + 0.5 + ], + [ + 40.09, + 0, + 0.5 + ], + [ + 48.14, + 0, + 0.5 + ], + [ + 56.18, + 0, + 0.5 + ], + [ + 63.05, + 2.1599999999999966, + 0.5 + ], + [ + 70.5, + 8.429999999999996, + 0.5 + ], + [ + 74.77, + 15.179999999999996, + 0.5 + ], + [ + 76.5, + 23.619999999999997, + 0.5 + ], + [ + 76.5, + 38.91, + 0.5 + ], + [ + 68.56, + 58.839999999999996, + 0.5 + ], + [ + 57.59, + 75.91, + 0.5 + ], + [ + 42.45, + 97.68, + 0.5 + ], + [ + 30.36, + 115.05, + 0.5 + ], + [ + 25.65, + 122.03, + 0.5 + ], + [ + 21.21, + 128.03, + 0.5 + ], + [ + 19.68, + 130.31, + 0.5 + ], + [ + 19.13, + 131.13, + 0.5 + ], + [ + 19.13, + 131.41, + 0.5 + ], + [ + 21.29, + 129.5, + 0.5 + ], + [ + 25.29, + 126.02, + 0.5 + ], + [ + 32.08, + 122.03, + 0.5 + ], + [ + 41.43, + 118.28999999999999, + 0.5 + ], + [ + 52.94, + 116.24, + 0.5 + ], + [ + 64.4, + 115.53999999999999, + 0.5 + ], + [ + 76.53, + 115.53999999999999, + 0.5 + ], + [ + 86.91, + 115.53999999999999, + 0.5 + ], + [ + 97.28, + 117.47999999999999, + 0.5 + ], + [ + 104.2, + 118.56, + 0.5 + ], + [ + 111.61, + 120.28999999999999, + 0.5 + ], + [ + 115.79, + 121.21, + 0.5 + ], + [ + 118.34, + 121.97, + 0.5 + ], + [ + 118.89, + 122.24, + 0.5 + ], + [ + 119.16, + 122.24, + 0.5 + ] + ], + "isComplete": true }, - "editingId": null - } + "4a8c1095-335e-42d6-30c9-ff4171dd364e": { + "id": "4a8c1095-335e-42d6-30c9-ff4171dd364e", + "type": "draw", + "name": "Draw", + "parentId": "page", + "childIndex": 2, + "point": [ + 246.71, + 199.21 + ], + "rotation": 0, + "style": { + "color": "black", + "size": "large", + "isFilled": false, + "dash": "draw" + }, + "points": [ + [ + 72.8938650204388, + 14.590000000000002, + 0.5 + ], + [ + 68.8781905397514, + 19.51, + 0.5 + ], + [ + 61.84336030699925, + 27.34, + 0.5 + ], + [ + 54.16720613998498, + 35.12, + 0.5 + ], + [ + 44.458548427463086, + 44.24, + 0.5 + ], + [ + 23.92631600900976, + 62.11, + 0.5 + ], + [ + 17.207214482355887, + 67.74, + 0.5 + ], + [ + 8.31747810127638, + 73.53, + 0.5 + ], + [ + 3.7196788187202796, + 77.64, + 0.5 + ], + [ + 1.4701117877700842, + 79.15, + 0.5 + ], + [ + 0.552525235671978, + 79.75, + 0.5 + ], + [ + 0, + 80.29, + 0.5 + ], + [ + 0, + 80.57, + 0.5 + ], + [ + 1.9338383248519229, + 80.57, + 0.5 + ], + [ + 7.547889380161841, + 80.57, + 0.5 + ], + [ + 15.756835738716942, + 81.75, + 0.5 + ], + [ + 26.99480437140235, + 83.09, + 0.5 + ], + [ + 40.689536998414944, + 83.09, + 0.5 + ], + [ + 51.35524735129723, + 83.09, + 0.5 + ], + [ + 62.71161424876949, + 83.09, + 0.5 + ], + [ + 70.9205606073246, + 83.09, + 0.5 + ], + [ + 76.5346116626345, + 83.09, + 0.5 + ], + [ + 80.6292183198465, + 83.09, + 0.5 + ], + [ + 82.98731709351797, + 83.09, + 0.5 + ], + [ + 84.26996496204221, + 83.41, + 0.5 + ], + [ + 84.81262367564861, + 83.41, + 0.5 + ], + [ + 85.07901977141903, + 83.41, + 0.5 + ], + [ + 84.32916409443563, + 82.04, + 0.5 + ], + [ + 82.82945274046884, + 77.05, + 0.5 + ], + [ + 80.68841745223992, + 70.45, + 0.5 + ], + [ + 78.12312171519146, + 60.059999999999995, + 0.5 + ], + [ + 75.83408859597897, + 44.82, + 0.5 + ], + [ + 75.02503378660215, + 28.34, + 0.5 + ], + [ + 75.02503378660215, + 16.24, + 0.5 + ], + [ + 74.38370985234003, + 6.879999999999999, + 0.5 + ], + [ + 74.38370985234003, + 2.73, + 0.5 + ], + [ + 74.00878201384833, + 0.93, + 0.5 + ], + [ + 74.00878201384833, + 0, + 0.5 + ], + [ + 74.22584549929091, + 3.552713678800501e-15, + 0.5 + ], + [ + 78.34018520063404, + 0.9100000000000037, + 0.5 + ], + [ + 84.84222324184536, + 1.9900000000000038, + 0.5 + ], + [ + 97.31350713272714, + 2.690000000000003, + 0.5 + ], + [ + 113.48473679819806, + 3.450000000000003, + 0.5 + ], + [ + 133.957770084258, + 3.450000000000003, + 0.5 + ], + [ + 157.27236172520233, + 3.450000000000003, + 0.5 + ], + [ + 177.74539501126222, + 3.450000000000003, + 0.5 + ], + [ + 195.16967297906066, + 3.450000000000003, + 0.5 + ], + [ + 204.61193459581213, + 3.450000000000003, + 0.5 + ], + [ + 212.92941269708854, + 2.9000000000000044, + 0.5 + ], + [ + 217.0240193543005, + 2.9000000000000044, + 0.5 + ], + [ + 219.05652289980813, + 2.9000000000000044, + 0.5 + ], + [ + 219.58931509134896, + 2.9000000000000044, + 0.5 + ], + [ + 219.86557770918498, + 2.9000000000000044, + 0.5 + ], + [ + 219.86557770918498, + 3.780000000000003, + 0.5 + ], + [ + 219.86557770918498, + 7.930000000000003, + 0.5 + ], + [ + 219.86557770918498, + 16.260000000000005, + 0.5 + ], + [ + 219.86557770918498, + 28.90000000000001, + 0.5 + ], + [ + 219.86557770918498, + 44.03, + 0.5 + ], + [ + 220.6055668641028, + 57.87, + 0.5 + ], + [ + 222.1447443063319, + 74.34, + 0.5 + ], + [ + 223.69378827062653, + 90.82000000000001, + 0.5 + ], + [ + 225.34149745557687, + 110.10000000000001, + 0.5 + ], + [ + 227.82786101610077, + 139.69, + 0.5 + ], + [ + 229.58410194377242, + 161.95000000000002, + 0.5 + ], + [ + 231.27127721698508, + 182.52, + 0.5 + ], + [ + 232.07046550429635, + 201.8, + 0.5 + ], + [ + 232.91898640193543, + 222.33, + 0.5 + ], + [ + 233.7181746892467, + 240.09, + 0.5 + ], + [ + 233.7181746892467, + 257.88, + 0.5 + ], + [ + 233.7181746892467, + 274.36, + 0.5 + ], + [ + 234.57656210895138, + 293.6, + 0.5 + ], + [ + 234.57656210895138, + 311.41999999999996, + 0.5 + ], + [ + 235.10935430049219, + 320.99, + 0.5 + ], + [ + 235.8000108450822, + 331.47, + 0.5 + ], + [ + 236.2736039042296, + 337.14, + 0.5 + ], + [ + 236.2736039042296, + 339.52, + 0.5 + ], + [ + 236.54000000000002, + 340.45000000000005, + 0.5 + ], + [ + 236.54000000000002, + 341, + 0.5 + ], + [ + 236.54000000000002, + 341.27, + 0.5 + ], + [ + 236.32293651455745, + 341.27, + 0.5 + ], + [ + 232.19873029114876, + 341.27, + 0.5 + ], + [ + 222.96366563777426, + 341.27, + 0.5 + ], + [ + 210.4825152248269, + 343.37, + 0.5 + ], + [ + 195.5643338616835, + 346.39, + 0.5 + ], + [ + 177.97232501877033, + 348.82000000000005, + 0.5 + ], + [ + 160.38031617585716, + 350.42999999999995, + 0.5 + ], + [ + 142.857372987403, + 351.23, + 0.5 + ], + [ + 122.38433970134314, + 352.1, + 0.5 + ], + [ + 101.91130641528324, + 352.1, + 0.5 + ], + [ + 85.66114457328774, + 352.90999999999997, + 0.5 + ], + [ + 74.99543422040544, + 353.45000000000005, + 0.5 + ], + [ + 65.76036956703096, + 354.09000000000003, + 0.5 + ], + [ + 60.146318511721056, + 354.63, + 0.5 + ], + [ + 57.79808626011515, + 355.06000000000006, + 0.5 + ], + [ + 56.39704012680406, + 355.06000000000006, + 0.5 + ], + [ + 56.13064403103365, + 355.06000000000006, + 0.5 + ], + [ + 55.88398097939437, + 355.06000000000006, + 0.5 + ], + [ + 55.65705097188623, + 355.06000000000006, + 0.5 + ], + [ + 55.420254442312526, + 355.06000000000006, + 0.5 + ], + [ + 55.420254442312526, + 354.83000000000004, + 0.5 + ], + [ + 55.420254442312526, + 354.28, + 0.5 + ], + [ + 55.420254442312526, + 352.58000000000004, + 0.5 + ], + [ + 55.420254442312526, + 351.91999999999996, + 0.5 + ], + [ + 53.14108784516562, + 345.85, + 0.5 + ], + [ + 51.64137649119882, + 340.86, + 0.5 + ], + [ + 49.766737298740324, + 331.5, + 0.5 + ], + [ + 47.94143071660968, + 322.14, + 0.5 + ], + [ + 46.65878284808544, + 311.76, + 0.5 + ], + [ + 44.68547843497124, + 300.24, + 0.5 + ], + [ + 43.560694919496136, + 292.61, + 0.5 + ], + [ + 41.47885876366065, + 281.25, + 0.5 + ], + [ + 38.07490865103865, + 269.57, + 0.5 + ], + [ + 36.46666555435057, + 263.65999999999997, + 0.5 + ], + [ + 34.21709852340037, + 256.25, + 0.5 + ], + [ + 32.07606323517146, + 249.75, + 0.5 + ], + [ + 30.477686660548947, + 243.28, + 0.5 + ], + [ + 28.020922666221765, + 233.93, + 0.5 + ], + [ + 26.146283473763262, + 224.57, + 0.5 + ], + [ + 24.86363560523903, + 214.25, + 0.5 + ], + [ + 22.84099858179697, + 202.72999999999996, + 0.5 + ], + [ + 20.759162425961478, + 191.43, + 0.5 + ], + [ + 18.04586885792944, + 181.42000000000002, + 0.5 + ], + [ + 15.579238341536684, + 172.06, + 0.5 + ], + [ + 13.467802619504488, + 165.54, + 0.5 + ], + [ + 10.053985984816912, + 154.01, + 0.5 + ], + [ + 7.912950696587994, + 147.51, + 0.5 + ], + [ + 6.788167181112896, + 140.09, + 0.5 + ], + [ + 6.255374989572053, + 133.62, + 0.5 + ], + [ + 5.712716275965649, + 127.13, + 0.5 + ], + [ + 5.712716275965649, + 122.97, + 0.5 + ], + [ + 5.712716275965649, + 120.09, + 0.5 + ], + [ + 5.712716275965649, + 118.77000000000002, + 0.5 + ], + [ + 5.712716275965649, + 118.22, + 0.5 + ], + [ + 5.712716275965649, + 117.94, + 0.5 + ], + [ + 5.712716275965649, + 117.69999999999999, + 0.5 + ], + [ + 5.712716275965649, + 117.45, + 0.5 + ], + [ + 5.712716275965649, + 114.57000000000001, + 0.5 + ], + [ + 5.712716275965649, + 109.69999999999999, + 0.5 + ], + [ + 5.712716275965649, + 101.36, + 0.5 + ], + [ + 5.712716275965649, + 96.37, + 0.5 + ], + [ + 5.712716275965649, + 92.81, + 0.5 + ], + [ + 5.712716275965649, + 90.52, + 0.5 + ], + [ + 5.712716275965649, + 89.97, + 0.5 + ], + [ + 5.712716275965649, + 89.68999999999998, + 0.5 + ], + [ + 5.712716275965649, + 89.46000000000001, + 0.5 + ], + [ + 6.2060423792442005, + 89.46000000000001, + 0.5 + ] + ], + "isComplete": true + }, + "9fa6dcc1-3d5e-4b7b-1afd-e9f5dd8742aa": { + "id": "9fa6dcc1-3d5e-4b7b-1afd-e9f5dd8742aa", + "type": "text", + "name": "Text", + "parentId": "page", + "childIndex": 3, + "point": [ + 513.14, + 289.69 + ], + "rotation": 0, + "text": "Save files!", + "style": { + "color": "black", + "size": "large", + "isFilled": false, + "dash": "draw" + } + } + }, + "bindings": {} } }, - "assets": {} + "pageStates": { + "page": { + "id": "page", + "selectedIds": [ + "9fa6dcc1-3d5e-4b7b-1afd-e9f5dd8742aa" + ], + "camera": { + "point": [ + -212.45, + -132.09 + ], + "zoom": 1.2675878661113014 + }, + "editingId": null, + "hoveredId": "9fa6dcc1-3d5e-4b7b-1afd-e9f5dd8742aa" + } + } } \ No newline at end of file diff --git a/www/.env b/www/.env new file mode 100644 index 000000000..b298903cd --- /dev/null +++ b/www/.env @@ -0,0 +1 @@ +NEXT_PUBLIC_LIVEBLOCKS_PUBLIC_API_KEY=pk_live_1LJGGaqBSNLjLT-4Jalkl-U9 \ No newline at end of file diff --git a/www/components/editor.tsx b/www/components/editor.tsx index 2b039c977..53abb4d3d 100644 --- a/www/components/editor.tsx +++ b/www/components/editor.tsx @@ -1,4 +1,4 @@ -import { TLDraw, TLDrawState, Data, useFileSystem } from '@tldraw/tldraw' +import { TLDraw, TLDrawState, useFileSystem } from '@tldraw/tldraw' import * as gtag from '-utils/gtag' import React from 'react' @@ -7,27 +7,22 @@ interface EditorProps { } export default function Editor({ id = 'home' }: EditorProps) { - // Put the tlstate into the window, for debugging. - const handleMount = React.useCallback((tlstate: TLDrawState) => { + // Put the state into the window, for debugging. + const handleMount = React.useCallback((state: TLDrawState) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - window.tlstate = tlstate + window.state = state }, []) // Send events to gtag as actions. - const handleChange = React.useCallback( - (_tlstate: TLDrawState, _state: Data, reason: string) => { - if (reason.startsWith('command')) { - gtag.event({ - action: reason, - category: 'editor', - label: `page:${id}`, - value: 0, - }) - } - }, - [id] - ) + const handlePersist = React.useCallback((_state: TLDrawState, reason?: string) => { + gtag.event({ + action: reason, + category: 'editor', + label: reason || 'persist', + value: 0, + }) + }, []) const fileSystemEvents = useFileSystem() @@ -36,7 +31,7 @@ export default function Editor({ id = 'home' }: EditorProps) { diff --git a/www/components/multiplayer-editor.tsx b/www/components/multiplayer-editor.tsx index 934129227..1276eba05 100644 --- a/www/components/multiplayer-editor.tsx +++ b/www/components/multiplayer-editor.tsx @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { TLDraw, TLDrawState, Data, TLDrawDocument, TLDrawUser } from '@tldraw/tldraw' +import { TLDraw, TLDrawState, TLDrawDocument, TLDrawUser } from '@tldraw/tldraw' import * as React from 'react' import { createClient, Presence } from '@liveblocks/client' import { LiveblocksProvider, RoomProvider, useObject, useErrorListener } from '@liveblocks/react' @@ -10,85 +10,57 @@ interface TLDrawUserPresence extends Presence { } const client = createClient({ - publicApiKey: process.env.NEXT_PUBLIC_LIVEBLOCKS_PUBLIC_API_KEY, + publicApiKey: process.env.NEXT_PUBLIC_LIVEBLOCKS_PUBLIC_API_KEY || '', throttle: 80, }) -export default function MultiplayerEditor({ id }: { id: string }) { +export default function MultiplayerEditor({ roomId }: { roomId: string }) { return ( - - + + ) } -function Editor({ id }: { id: string }) { +function Editor({ roomId }: { roomId: string }) { const [docId] = React.useState(() => Utils.uniqueId()) + const [state, setState] = React.useState() + const [error, setError] = React.useState() - const [tlstate, setTlstate] = React.useState() - useErrorListener((err) => setError(err)) + // Setup document + const doc = useObject<{ uuid: string; document: TLDrawDocument }>('doc', { uuid: docId, document: { - id: 'test-room', ...TLDrawState.defaultDocument, + id: roomId, }, }) - // Put the tlstate into the window, for debugging. - const handleMount = React.useCallback( - (tlstate: TLDrawState) => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - window.tlstate = tlstate - - tlstate.loadRoom(id) - - setTlstate(tlstate) - }, - [id] - ) - - const handleChange = React.useCallback( - (_tlstate: TLDrawState, state: Data, reason: string) => { - // If the client updates its document, update the room's document - if (reason.startsWith('command') || reason.startsWith('undo') || reason.startsWith('redo')) { - doc?.update({ uuid: docId, document: state.document }) - } - - // When the client updates its presence, update the room - // if (state.room && (reason === 'patch:room:self:update' || reason === 'patch:selected')) { - // const room = client.getRoom(ROOM_ID) - // if (!room) return - // const { userId, users } = state.room - // room.updatePresence({ id: userId, user: users[userId] }) - // } - }, - [docId, doc] - ) + // Setup client React.useEffect(() => { - const room = client.getRoom(id) + const room = client.getRoom(roomId) if (!room) return if (!doc) return - if (!tlstate) return - if (!tlstate.state.room) return + if (!state) return + if (!state.state.room) return // Update the user's presence with the user from state - const { users, userId } = tlstate.state.room + const { users, userId } = state.state.room room.updatePresence({ id: userId, user: users[userId] }) // Subscribe to presence changes; when others change, update the state room.subscribe('others', (others) => { - tlstate.updateUsers( + state.updateUsers( others .toArray() .filter((other) => other.presence) @@ -99,30 +71,26 @@ function Editor({ id }: { id: string }) { room.subscribe('event', (event) => { if (event.event?.name === 'exit') { - tlstate.removeUser(event.event.userId) + state.removeUser(event.event.userId) } }) function handleDocumentUpdates() { if (!doc) return - if (!tlstate) return - if (!tlstate.state.room) return + if (!state) return + if (!state.state.room) return const docObject = doc.toObject() // Only merge the change if it caused by someone else if (docObject.uuid !== docId) { - tlstate.mergeDocument(docObject.document) + state.mergeDocument(docObject.document) } else { - tlstate.updateUsers( - Object.values(tlstate.state.room.users).map((user) => { - // const activeShapes = user.activeShapes - // .map((shape) => docObject.document.pages[tlstate.currentPageId].shapes[shape.id]) - // .filter(Boolean) + state.updateUsers( + Object.values(state.state.room.users).map((user) => { return { ...user, - // activeShapes: activeShapes, - selectedIds: user.selectedIds, // activeShapes.map((shape) => shape.id), + selectedIds: user.selectedIds, } }) ) @@ -130,8 +98,8 @@ function Editor({ id }: { id: string }) { } function handleExit() { - if (!(tlstate && tlstate.state.room)) return - room?.broadcastEvent({ name: 'exit', userId: tlstate.state.room.userId }) + if (!(state && state.state.room)) return + room?.broadcastEvent({ name: 'exit', userId: state.state.room.userId }) } window.addEventListener('beforeunload', handleExit) @@ -143,21 +111,39 @@ function Editor({ id }: { id: string }) { const newDocument = doc.toObject().document if (newDocument) { - tlstate.loadDocument(newDocument) + state.loadDocument(newDocument) } return () => { window.removeEventListener('beforeunload', handleExit) doc.unsubscribe(handleDocumentUpdates) } - }, [doc, docId, tlstate, id]) + }, [doc, docId, state, roomId]) + + const handleMount = React.useCallback( + (state: TLDrawState) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + window.state = state + state.loadRoom(roomId) + setState(state) + }, + [roomId] + ) + + const handlePersist = React.useCallback( + (state: TLDrawState) => { + doc?.update({ uuid: docId, document: state.document }) + }, + [docId, doc] + ) const handleUserChange = React.useCallback( - (tlstate: TLDrawState, user: TLDrawUser) => { - const room = client.getRoom(id) - room?.updatePresence({ id: tlstate.state.room?.userId, user }) + (state: TLDrawState, user: TLDrawUser) => { + const room = client.getRoom(roomId) + room?.updatePresence({ id: state.state.room?.userId, user }) }, - [id] + [roomId] ) if (error) return
Error: {error.message}
@@ -168,7 +154,7 @@ function Editor({ id }: { id: string }) {
{ - if (tlstate.isDirty) { - if (tlstate.fileSystemHandle) { + if (state.isDirty) { + if (state.fileSystemHandle) { if (window.confirm('Do you want to save changes to your current project?')) { - await tlstate.saveProject() + await state.saveProject() } } else { if (window.confirm('Do you want to save your current project?')) { - await tlstate.saveProject() + await state.saveProject() } } } - }, [tlstate]) + }, [state]) const onNewProject = React.useCallback(async () => { await promptSaveBeforeChange() - tlstate.newProject() - }, [tlstate, promptSaveBeforeChange]) + state.newProject() + }, [state, promptSaveBeforeChange]) const onSaveProject = React.useCallback(() => { - tlstate.saveProject() - }, [tlstate]) + state.saveProject() + }, [state]) const onSaveProjectAs = React.useCallback(() => { - tlstate.saveProjectAs() - }, [tlstate]) + state.saveProjectAs() + }, [state]) const onOpenProject = React.useCallback(async () => { await promptSaveBeforeChange() - tlstate.openProject() - }, [tlstate, promptSaveBeforeChange]) + state.openProject() + }, [state, promptSaveBeforeChange]) return { onNewProject, diff --git a/www/pages/k/[id].tsx b/www/pages/k/[id].tsx index dbfbd10ef..03e5befd6 100644 --- a/www/pages/k/[id].tsx +++ b/www/pages/k/[id].tsx @@ -14,7 +14,7 @@ export default function Room({ id }: RoomProps): JSX.Element { tldraw - + ) } diff --git a/www/pages/r/[id].tsx b/www/pages/r/[id].tsx index 96be55462..73b6f7afe 100644 --- a/www/pages/r/[id].tsx +++ b/www/pages/r/[id].tsx @@ -15,7 +15,7 @@ export default function Room({ id }: RoomProps): JSX.Element { tldraw - + ) } diff --git a/www/pages/shhhmp.tsx b/www/pages/shhhmp.tsx index 2b560622b..4ca1428e3 100644 --- a/www/pages/shhhmp.tsx +++ b/www/pages/shhhmp.tsx @@ -9,7 +9,7 @@ export default function Room(): JSX.Element { tldraw - + ) } diff --git a/yarn.lock b/yarn.lock index 92e8ec6a9..2b4687ac6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1114,9 +1114,9 @@ minimatch "^3.0.4" "@humanwhocodes/object-schema@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" - integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -1989,14 +1989,14 @@ write-file-atomic "^2.3.0" "@liveblocks/client@^0.12.0", "@liveblocks/client@^0.12.1": - version "0.12.1" - resolved "https://registry.yarnpkg.com/@liveblocks/client/-/client-0.12.1.tgz#264a7e1af0552d194f0a2f61a9bbeb37df44883d" - integrity sha512-XmjimPlXoCPFpMCevOOJIQdqxTcblu8nCSDXNhWxrbhC9HOFblwIam+UQ/hNS+wn8b7cVgBbVa6hKbV4woziSw== + version "0.12.3" + resolved "https://registry.yarnpkg.com/@liveblocks/client/-/client-0.12.3.tgz#03b957ccc7a6a5dc7474d224fe12c32e065e9c9c" + integrity sha512-n82Ymngpvt4EiZEU3LWnEq7EjDmcd2wb2kjGz4m/4L7wYEd4RygAYi7bp7w5JOD1rt3Srhrwbq9Rz7TikbUheg== "@liveblocks/react@^0.12.0", "@liveblocks/react@^0.12.1": - version "0.12.1" - resolved "https://registry.yarnpkg.com/@liveblocks/react/-/react-0.12.1.tgz#99c14766abcbb46ff7d99cfdea0d4706724371c9" - integrity sha512-dRxdgFXxzr1W0thWZq8rKyjoS4Q4UBJQIcJ03ZTJiumHF9iO6pyKd/aS8C/fq4966QdoXZ2bN0izovC6EyLpdg== + version "0.12.3" + resolved "https://registry.yarnpkg.com/@liveblocks/react/-/react-0.12.3.tgz#82d93a9a3a96401258f6c87c1150026dd9d63504" + integrity sha512-3mHRiEwZ/s1lbGS4/bblUpLCNCBFMzEiUHHfBH3zO9+IKrH40lVdky0OujgF5zEacYcqUnVW7jT4ZvHCchvsYA== "@malept/cross-spawn-promise@^1.1.0": version "1.1.1" @@ -2044,10 +2044,10 @@ require_optional "^1.0.1" typeorm "^0.2.30" -"@next/env@12.0.2": - version "12.0.2" - resolved "https://registry.yarnpkg.com/@next/env/-/env-12.0.2.tgz#e823e0784ba932982568589e8e1fda1bf4949715" - integrity sha512-4ndC5Rj0lTpF6Cs6evHNLtOXXmsD5a09pLFp+KkgbTESuycPASQHlQVqtr5UGEZORa3Q9ljdTiCkVEiI6qtcIg== +"@next/env@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/env/-/env-12.0.3.tgz#e676b4d1454d8b6be433a348e99f2b8276ab6cd7" + integrity sha512-QcdlpcwIH9dYcVlNAU+gXaqHA/omskbRlb+R3vN7LlB2EgLt+9WQwbokcHOsNyt4pI7kDM67W4tr9l7dWnlGdQ== "@next/eslint-plugin-next@11.1.2": version "11.1.2" @@ -2056,15 +2056,15 @@ dependencies: glob "7.1.7" -"@next/polyfill-module@12.0.2": - version "12.0.2" - resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-12.0.2.tgz#1b329fcc8b54092ea936e0aaad259215fd76753f" - integrity sha512-7OcOfn0F4pZr4XqSc4ZvUes50Y01a+PKsnUtEwYlWbbbjuYHYrIXkdAVPSvYtxZ07ECH3ZCFmsmfLlhmOeXwxg== +"@next/polyfill-module@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-12.0.3.tgz#4217e5284762124bf9fe2505622c4de89998f7a2" + integrity sha512-fgjVjdCk0Jq627d/N33oQIJjWrcKtzw6Dfa2PfypoIJ35/xFIKgs6mPyvq8cg3Ao5b7dEn9+Rw45PGjlY5e7JA== -"@next/react-dev-overlay@12.0.2": - version "12.0.2" - resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-12.0.2.tgz#0a8a6b614678ab34a23c4e2165e563489c1e38bd" - integrity sha512-gRSs5OeUuhkbFX7+bS/UhAAocx7V2kbz+VL9s6fcnzQENBah2E9eiLzXi+cxqHblFnTELSIJjR1WNG4PVI4/Nw== +"@next/react-dev-overlay@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-12.0.3.tgz#d85a609bf7d75eb190940d0fc64eff94c0e4a478" + integrity sha512-gHfDEVHFeTUpQMcyytzvkuOu+5DQXjXbCbQHuavFftYrlHqXfzYFsa+wERff+g4/0IzEvcYVp3F4gdmynWfUog== dependencies: "@babel/code-frame" "7.12.11" anser "1.4.9" @@ -2078,65 +2078,65 @@ stacktrace-parser "0.1.10" strip-ansi "6.0.1" -"@next/react-refresh-utils@12.0.2": - version "12.0.2" - resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-12.0.2.tgz#cfed1047ea2be314747e83a95f4d139c6f100742" - integrity sha512-Ifyni4yHnaMhpGObM3tFXxVhIRlt4kJGyGEis13cdnVF9idFgDj2/6SxJXX5Sx86u3YGXYfYdru3DeeLggCCmg== +"@next/react-refresh-utils@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-12.0.3.tgz#1389b0370e258634432d6dd78f889c09a8328e10" + integrity sha512-YPtlfvkYh/4MvNNm5w3uwo+1KPMg67snzr5CuexbRewsu2ITaF7f0bh0Jcayi20wztk8SgWjNz1bmF8j9qbWIw== -"@next/swc-android-arm64@12.0.2": - version "12.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.0.2.tgz#3e08f4781031c8f671a42f7dbf1bd152d4c57207" - integrity sha512-AL1lwtYM1H7/92XvL4GDEUZ3PvSrK+v2QxP3lKELit4s43+DU5XQaZw4DxmCyr7xBGuaXUaoGUS1LoftBMU96w== +"@next/swc-android-arm64@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.0.3.tgz#8b99b3e7f13dda1f4c3c6dc83af73d8f40afecd5" + integrity sha512-40sOl9/50aamX0dEMrecqJQcUrRK47D7S9F66ulrZmz+5Ujp0lnP1rBOXngo0PZMecfU1tr7zbNubiAMDxfCxw== -"@next/swc-darwin-arm64@12.0.2": - version "12.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.0.2.tgz#6d546b230610b3a7417ebef545865192e6cf3590" - integrity sha512-70LzmWK/eEY05rf4u9O11LpkUy7Sd3OY0lx4s8NHejohDF7kW0lYWudjAJjBYVyYaSo09icNskl6LASSwYampw== +"@next/swc-darwin-arm64@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.0.3.tgz#a385e610fb4a20c47355520b82a79d08e0f6441e" + integrity sha512-iKSe2hCMB51Ft41cNAxZk6St1rBlqSRtBSl4oO0zJlGu7bCxXCGCJ058/OLvYxcNWgz7ODOApObm3Yjv8XEvxg== -"@next/swc-darwin-x64@12.0.2": - version "12.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.0.2.tgz#a49e48c188fe083bb23f84ed22db03e9560a0635" - integrity sha512-rxSctbsSZ17A4Lw2g+gbcB86MZAtQAf+GYyUv5XfZjA2H+Upt4mOmTaZGhm8pUI9WvRmoat6saZNvQSy54nLNA== +"@next/swc-darwin-x64@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.0.3.tgz#0405a3838a652b7bb44c5cd5d920c11240194385" + integrity sha512-/BcnfLyhIj4rgU3yVDfD8uXK2TcNYIdflYHKkjFxd3/J1GWOtBN31m0dB8fL0h5LdW11kzaXvVvab3f5ilkEww== -"@next/swc-linux-arm-gnueabihf@12.0.2": - version "12.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.0.2.tgz#154987bd7a5634f87faef13238541cef6d304279" - integrity sha512-/yyD8P8ecnDvaAcRr0Dk3IyC9jdqQZv7CFz7uhMpBp8CVS3OFK9hqXOFXE6IEIcwZ0Hers2CRX8tX9NtcN6dwg== +"@next/swc-linux-arm-gnueabihf@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.0.3.tgz#f5d43be7314044526fd9f1eef34337bb95f02e01" + integrity sha512-2HNPhBJuN9L6JzqqqdYB4TKfFFmaKkpF0X3C1s83Xp61mR2sx8gOthHQtZqWDs4ZLnKZU0j2flGU1uuqpHPCpg== -"@next/swc-linux-arm64-gnu@12.0.2": - version "12.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.0.2.tgz#7c7d3df354960dfd8eaafcd945d5ebc7e218a125" - integrity sha512-cza3ocE/LlnA40JILK6VrHehhMYMZoxE9D8+VQfqz4vbFm89tbZ0aM/97AxbDIcJK68Ej9RZ0XosN3cvMFGiBg== +"@next/swc-linux-arm64-gnu@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.0.3.tgz#6f1cda1dadabcc4d4f13bd6f5ce23b9879bc6d73" + integrity sha512-NXTON1XK7zi2i+A+bY1PVLi1g5b8cSwgzbnuVR0vAgOtU+3at7FqAKOWfuFIXY7eBEK65uu0Fu5gADhMj0uanQ== -"@next/swc-linux-arm64-musl@12.0.2": - version "12.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.0.2.tgz#aad8d6a66d463ca6e6330c2d7ec361beb7ad78df" - integrity sha512-r0fE9rEv+qffFlwSjOseHWFbRrpoEgiPiTMc/celUFDWQf61IFdBspT+HQyPefq2PATbUQsdHEgACmg07ERjSQ== +"@next/swc-linux-arm64-musl@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.0.3.tgz#1eedc1f1fcafc9862ef7e83205ada96bf320a694" + integrity sha512-8D0q22VavhcIl2ZQErEffgh5q6mChaG84uTluAoFfjwrgYtPDZX0M5StqkTZL6T5gA5RLHboNVoscIKGZWMojQ== -"@next/swc-linux-x64-gnu@12.0.2": - version "12.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.0.2.tgz#79a6eb1287c2b185dd7271eec561dac23a1c3506" - integrity sha512-D1uk6BMz0W6BvJlkPEYX2FFkO/ENscjCBiyYUkbafrkHsthnN42yp4BHWc13QNl1Ohdn4ISBPPXk4oWT7BGDhw== +"@next/swc-linux-x64-gnu@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.0.3.tgz#eca85107b01a7571957ae25104d11042e9835a49" + integrity sha512-4mkimH9nMzbuQfLmZ152NYSHdrII9AeqrkrHszexL1Lup2TLMPuxlXj55eVnyyeKFXRLlnqbCu7aOIND68RbOA== -"@next/swc-linux-x64-musl@12.0.1": - version "12.0.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.0.1.tgz#4b7e5fee5a62adb6d9c9aad1a4aa00a6a09b53dc" - integrity sha512-4SAmi7riavU6TFGX7wQFioFi/vx8uJ2/Cx7ZfrYiZzzKmmuu2eM8onW1kcKu+aQD777x/kvzW4+2pWkM2gyPOA== +"@next/swc-linux-x64-musl@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.0.3.tgz#758656b8e36a520c03763d154c366bec889c56b3" + integrity sha512-MXvx+IDYoSsSM7KcwbQAVo9r+ZeklHeDQiUEmyRRzQE1Q4JvkWwMdPu/NfFdyxur+RfKjRoUoWFdPi5MBKTpkw== -"@next/swc-win32-arm64-msvc@12.0.2": - version "12.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.0.2.tgz#0783cf20f211a921facf2dfcd847f7b17048209a" - integrity sha512-dyPQDOPY1itb0+Xv2J2+CDhmXo/06LWWSZHufV14ZUq7kl5NE0fRN+/00Tok5M1i2zyhE2N9N5XC3/w7tJus7Q== +"@next/swc-win32-arm64-msvc@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.0.3.tgz#3f8ab8fa3367d729e49b3072fb24f9d0f8af7c21" + integrity sha512-8GusumFZLp/mtVix+3JZVTGqzqntTsrTIFZ+GpcLMwyVjB3KkBwHiwJaa38WGleUinJSpJvgmhTWgppsiSKW3A== -"@next/swc-win32-ia32-msvc@12.0.2": - version "12.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.0.2.tgz#c48f8f5be7728f8bbb0a864727dcbd816f06784f" - integrity sha512-0btzmSYjQEj/sGNV8BSlIY8zTbsaox1WcnYRuWaRjc4O0RQn4IJbWwLurduYX6Kqhi3YADEQ2ySjEEKfAVxTUA== +"@next/swc-win32-ia32-msvc@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.0.3.tgz#e3df153a4e0c896a5871f1d26c7e176fa1ceec72" + integrity sha512-mF7bkxSZ++QiB+E0HFqay/etvPF+ZFcCuG27lSwFIM00J+TE0IRqMyMx66vJ8g1h6khpwXPI0o2hrwIip/r8cQ== -"@next/swc-win32-x64-msvc@12.0.2": - version "12.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.0.2.tgz#5eb3f054395fb2b1c57b981c446df17d0507febd" - integrity sha512-KKxVh07Nb0QEZjjLGe+0nGu0Zv0P7aT0WftL6TLvEXVNttJ01FT6i1dTP7+TGJtPbJ7yOD5vI3A7wc8fdwSXVA== +"@next/swc-win32-x64-msvc@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.0.3.tgz#c3e4af29cd74190b89461ccc26b932ae4c27f99d" + integrity sha512-eXFwyf46UFFggMQ3k2tJsOmB3SuKjWaSiZJH0tTDUsLw74lyqyzJqMCVA4yY0gWSlEnSjmX5nrCBknVZd3joaA== "@node-rs/helper@^1.0.0", "@node-rs/helper@^1.2.1": version "1.2.1" @@ -2791,24 +2791,24 @@ picomatch "^2.2.2" "@rushstack/eslint-patch@^1.0.6": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.0.9.tgz#1168db664faab4c3bb82c76124b393970e80bf89" - integrity sha512-yk9Xj/3bUxyz3azMXW8qigLqXWEr2R0h9G7PVnnmjNQdlZLN+aESqCTnVN7ubtYUIQfW32/v8+AXsbpL1ryI1A== + version "1.1.0" + resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz#7f698254aadf921e48dda8c0a6b304026b8a9323" + integrity sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A== -"@sentry/browser@6.13.3": - version "6.13.3" - resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.13.3.tgz#d4511791b1e484ad48785eba3bce291fdf115c1e" - integrity sha512-jwlpsk2/u1cofvfYsjmqcnx50JJtf/T6HTgdW+ih8+rqWC5ABEZf4IiB/H+KAyjJ3wVzCOugMq5irL83XDCfqQ== +"@sentry/browser@6.14.1": + version "6.14.1" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.14.1.tgz#4d255caf9de6e07f12b6d9b350fe391439dd932e" + integrity sha512-xOrKt6jT6rGhJDVwUtHtD/lLrCOEDNYCtLAh8SoJH7jE0JRSI7WK0UDPQ56M8z3II11lEw3F0TOXoK1rZ9BdrQ== dependencies: - "@sentry/core" "6.13.3" - "@sentry/types" "6.13.3" - "@sentry/utils" "6.13.3" + "@sentry/core" "6.14.1" + "@sentry/types" "6.14.1" + "@sentry/utils" "6.14.1" tslib "^1.9.3" "@sentry/cli@^1.70.1": - version "1.70.1" - resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.70.1.tgz#908517b699c0714eff88bedb68c6ea72e94945e8" - integrity sha512-pab3nU3rNsq1HKKmJP2ITDl5cGi+QbQ5eLX6ELaeAbN2eAzfndHu2rTqlnjJAKcYQg6l9gFBn8vvY2xAeRJb6Q== + version "1.71.0" + resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.71.0.tgz#1e33e05d7651b68f501764ab24dce3d5932b195d" + integrity sha512-Z8TzH7PkiRfjWSzjXOfPWWp6wxjr+n39Jdrt26OcInVQZM1sx/gZULrDiQZ1L2dy9Fe9AR4SF4nt2/7h2GmLQQ== dependencies: https-proxy-agent "^5.0.0" mkdirp "^0.5.5" @@ -2817,94 +2817,94 @@ progress "^2.0.3" proxy-from-env "^1.1.0" -"@sentry/core@6.13.3": - version "6.13.3" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.13.3.tgz#5cbbb995128e793ebebcbf1d3b7514e0e5e8b221" - integrity sha512-obm3SjgCk8A7nB37b2AU1eq1q7gMoJRrGMv9VRIyfcG0Wlz/5lJ9O3ohUk+YZaaVfZMxXn6hFtsBiOWmlv7IIA== +"@sentry/core@6.14.1": + version "6.14.1" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.14.1.tgz#cbb6eae808279ae2147dd5da22ce6ab5a1cd69d1" + integrity sha512-x2MOax+adphal0ytBsvQukwN5mcxZzb5zsPZ1YWzewQk3BY+2T/DFo50iVpaWdUXsJL2FtoZVVgtpTmf+/3JPw== dependencies: - "@sentry/hub" "6.13.3" - "@sentry/minimal" "6.13.3" - "@sentry/types" "6.13.3" - "@sentry/utils" "6.13.3" + "@sentry/hub" "6.14.1" + "@sentry/minimal" "6.14.1" + "@sentry/types" "6.14.1" + "@sentry/utils" "6.14.1" tslib "^1.9.3" -"@sentry/hub@6.13.3": - version "6.13.3" - resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.13.3.tgz#cc09623a69b5343315fdb61c7fdd0be74b72299f" - integrity sha512-eYppBVqvhs5cvm33snW2sxfcw6G20/74RbBn+E4WDo15hozis89kU7ZCJDOPkXuag3v1h9igns/kM6PNBb41dw== +"@sentry/hub@6.14.1": + version "6.14.1" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.14.1.tgz#6a82cae35de834bd92bbcd3912a1e3029a5369de" + integrity sha512-IqANj5qKG1N+nqBsuYIwAZsXDMmO/Sc4H2zZ2MP7QvRyp0ptpJmu1oTE0r0fohIcGgIWbnIphJjw990Lp507eA== dependencies: - "@sentry/types" "6.13.3" - "@sentry/utils" "6.13.3" + "@sentry/types" "6.14.1" + "@sentry/utils" "6.14.1" tslib "^1.9.3" "@sentry/integrations@^6.13.2": - version "6.13.3" - resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-6.13.3.tgz#9d80229de6e815c53fa52ca2422a0d13820b8d4e" - integrity sha512-iC8LkbBTxlRo9FNxRqFfEm85FrELltc3E9gFsFSBkCnf7S/3nDCDW+mJX92KpRk97Wqid6/JwlXttKz8lsdF2A== + version "6.14.1" + resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-6.14.1.tgz#2293d954cdb14e1992f6bdc6484275defb2667f2" + integrity sha512-GZFp03w0c/o2iY1oxebsqpumsDQsoOWiUXpfPriG6UoXZdCDrSoiotaRHPAagsWKQ1XjbILph8Vdz5dSMIuQTg== dependencies: - "@sentry/types" "6.13.3" - "@sentry/utils" "6.13.3" + "@sentry/types" "6.14.1" + "@sentry/utils" "6.14.1" localforage "^1.8.1" tslib "^1.9.3" -"@sentry/minimal@6.13.3": - version "6.13.3" - resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.13.3.tgz#a675a79bcc830142e4f95e6198a2efde2cd3901e" - integrity sha512-63MlYYRni3fs5Bh8XBAfVZ+ctDdWg0fapSTP1ydIC37fKvbE+5zhyUqwrEKBIiclEApg1VKX7bkKxVdu/vsFdw== +"@sentry/minimal@6.14.1": + version "6.14.1" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.14.1.tgz#6fbce5b873fb096411dbb9a01ff6706ed684f2e8" + integrity sha512-rxS0YUggCSuA7EzS1ai5jU8XArk4FBHZ02gmSoSSLtwFXmeQIa9XBKY0OEFmG2LMQYNOpvcGsezDO51EB6/X9w== dependencies: - "@sentry/hub" "6.13.3" - "@sentry/types" "6.13.3" + "@sentry/hub" "6.14.1" + "@sentry/types" "6.14.1" tslib "^1.9.3" "@sentry/node@^6.13.2": - version "6.13.3" - resolved "https://registry.yarnpkg.com/@sentry/node/-/node-6.13.3.tgz#94c646c31fd240ab68ee8b85aa663e65eb499d06" - integrity sha512-ZeZSw+TcPcf4e0j7iEqNMtoVmz+WFW/TEoGokXIwysZqSgchKdAXDHqn+CqUqFan7d76JcJmzztAUK2JruQ2Kg== + version "6.14.1" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-6.14.1.tgz#475229eb0a3c7032a905a7f49888e1374499ecf3" + integrity sha512-tnEfcaF5Z7I4D619XL76sjRd7VMDitZZ7ydfA8sWGC1BPaPyyIJzVxE/a7qJBQGW7W0Oo7ctwOI1hpmfyOpPxg== dependencies: - "@sentry/core" "6.13.3" - "@sentry/hub" "6.13.3" - "@sentry/tracing" "6.13.3" - "@sentry/types" "6.13.3" - "@sentry/utils" "6.13.3" + "@sentry/core" "6.14.1" + "@sentry/hub" "6.14.1" + "@sentry/tracing" "6.14.1" + "@sentry/types" "6.14.1" + "@sentry/utils" "6.14.1" cookie "^0.4.1" https-proxy-agent "^5.0.0" lru_map "^0.3.3" tslib "^1.9.3" "@sentry/react@^6.13.2": - version "6.13.3" - resolved "https://registry.yarnpkg.com/@sentry/react/-/react-6.13.3.tgz#f9607e0a60d52efd0baa96a14e694b6059f9379a" - integrity sha512-fdfmD9XNpGDwdkeLyd+iq+kqtNeghpH3wiez2rD81ZBvrn70uKaO2/yYDE71AXC6fUOwQuJmdfAuqBcNJkYIEw== + version "6.14.1" + resolved "https://registry.yarnpkg.com/@sentry/react/-/react-6.14.1.tgz#26889c2c6d61a1d9ffa2f82c72438e7c3ad8bdc7" + integrity sha512-A+GEb0g8EW3JmTRSAEws2Sx9QIldHuDW3P6R45Qq6T/g6nzxUtAa6gVdmGt40JwfHofzQgQDRca4baqtrHDsHw== dependencies: - "@sentry/browser" "6.13.3" - "@sentry/minimal" "6.13.3" - "@sentry/types" "6.13.3" - "@sentry/utils" "6.13.3" + "@sentry/browser" "6.14.1" + "@sentry/minimal" "6.14.1" + "@sentry/types" "6.14.1" + "@sentry/utils" "6.14.1" hoist-non-react-statics "^3.3.2" tslib "^1.9.3" -"@sentry/tracing@6.13.3", "@sentry/tracing@^6.13.2": - version "6.13.3" - resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.13.3.tgz#ca657d4afa99c50f15e638fe38405bac33e780ee" - integrity sha512-yyOFIhqlprPM0g4f35Icear3eZk2mwyYcGEzljJfY2iU6pJwj1lzia5PfSwiCW7jFGMmlBJNhOAIpfhlliZi8Q== +"@sentry/tracing@6.14.1", "@sentry/tracing@^6.13.2": + version "6.14.1" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.14.1.tgz#fadea88b505078f61b949ecd99891ddb5538f08e" + integrity sha512-Bv/+S5Wn9OPxP7sA9VYMV1wpmXWptFVIMFoG4BuyV4aFYdIAMxSNE/ktqXwmqn+nkBic04nP9rF6lMJBLIvIaA== dependencies: - "@sentry/hub" "6.13.3" - "@sentry/minimal" "6.13.3" - "@sentry/types" "6.13.3" - "@sentry/utils" "6.13.3" + "@sentry/hub" "6.14.1" + "@sentry/minimal" "6.14.1" + "@sentry/types" "6.14.1" + "@sentry/utils" "6.14.1" tslib "^1.9.3" -"@sentry/types@6.13.3": - version "6.13.3" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.13.3.tgz#63ad5b6735b0dfd90b3a256a9f8e77b93f0f66b2" - integrity sha512-Vrz5CdhaTRSvCQjSyIFIaV9PodjAVFkzJkTRxyY7P77RcegMsRSsG1yzlvCtA99zG9+e6MfoJOgbOCwuZids5A== +"@sentry/types@6.14.1": + version "6.14.1" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.14.1.tgz#0d562a7aa91253b7843723344b4ba03a010e6376" + integrity sha512-RIk3ZwQKZnASrYWfV5i4wbzVveHz8xLFAS2ySIMqh+hICKnB0N4/r8a1Of/84j7pj+iAbf5vPS85639eIf+9qg== -"@sentry/utils@6.13.3": - version "6.13.3" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.13.3.tgz#188754d40afe693c3fcae410f9322531588a9926" - integrity sha512-zYFuFH3MaYtBZTeJ4Yajg7pDf0pM3MWs3+9k5my9Fd+eqNcl7dYQYJbT9gyC0HXK1QI4CAMNNlHNl4YXhF91ag== +"@sentry/utils@6.14.1": + version "6.14.1" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.14.1.tgz#cb746858665314c07cfe9b0f307b410e377032ad" + integrity sha512-GVvf0z18L4DN0a6vIBdHSlrK/Dj8QFhuiiJ8NtccSoY8xiKXQNz9FKN5d52NUNqm59aopAxcVAcs57yQSdxrZQ== dependencies: - "@sentry/types" "6.13.3" + "@sentry/types" "6.14.1" tslib "^1.9.3" "@sentry/webpack-plugin@^1.17.1": @@ -2944,9 +2944,9 @@ type-detect "4.0.8" "@sinonjs/fake-timers@^8.0.1": - version "8.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.0.1.tgz#1c1c9a91419f804e59ae8df316a07dd1c3a76b94" - integrity sha512-AU7kwFxreVd6OAXcAFlKSmZquiRUU0FvYm44k1Y1QbK7Co4m0aqfGMhjykIeQp/H6rcl+nFmj0zfdUcGVs9Dew== + version "8.1.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" + integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== dependencies: "@sinonjs/commons" "^1.7.0" @@ -2983,85 +2983,85 @@ "@node-rs/xxhash" "^1.0.0" "@swc-node/core" "^1.7.1" -"@swc/core-android-arm64@^1.2.106": - version "1.2.106" - resolved "https://registry.yarnpkg.com/@swc/core-android-arm64/-/core-android-arm64-1.2.106.tgz#86fb16a40d112502051252dfa29c8482b341ce36" - integrity sha512-F5T6kP3yV9S0/oXyco305QaAyE6rLNsNSdR0QI4CtACwKadiPwTOptwNIDCiTNLNgWlWLQmIRkPpxg+G4doT6Q== +"@swc/core-android-arm64@^1.2.107": + version "1.2.107" + resolved "https://registry.yarnpkg.com/@swc/core-android-arm64/-/core-android-arm64-1.2.107.tgz#b9dfd19b9b1659e7dc47d2a1fe4578e56b5c01f1" + integrity sha512-gnMkRn6DPDFiPcH1VC15XsQzR1/9SW0CqwYUiBUEuS5wZbOnyEkgY3UChu8SgeMDbzDx5KJMrVjed1UrMZU26w== -"@swc/core-darwin-arm64@^1.2.106": - version "1.2.106" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.106.tgz#02a9ce94fee6d7f8a81a94233e4c455d55273fed" - integrity sha512-bgKzzYLFnc+mv2mDS/DLwzBvx5DCC9ZCKYY46b4dAnBfasr+SMHj+v/WI84HtilbjLBMUfYZ2hgYKls3CebIIQ== +"@swc/core-darwin-arm64@^1.2.107": + version "1.2.107" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.107.tgz#c6e2a9372728a3ce7f3daf33169809940c39577c" + integrity sha512-N1NG6SHAyJqhkPzMj2+jBbeY4jgS/ShIY8s1GyvRKKSjgqjBKiZvNwgFzWZ7lf16kTJO4rSG//NnPr8noL19yw== -"@swc/core-darwin-x64@^1.2.106": - version "1.2.106" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.2.106.tgz#72448061266e9fb44427898bd13154ec3b63ebba" - integrity sha512-I5Uhit5RqbXaMIV2+v9jL+MIQeR3lT1DqVwzxZs1bTARclAheFZQpTmg+h6QmichjCiUT74SXQb6Apc/vqYKog== +"@swc/core-darwin-x64@^1.2.107": + version "1.2.107" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.2.107.tgz#8fab5165123792d7a7d7289a6c8d251269fc00cf" + integrity sha512-cYA4YsrUtOTHMWnKUk/X3l5UTdpOt90SExg+v7hSonhJSg84yBoXOwNzfPVcsP5Af5rWLABrLxpOOQKNCWNf6g== -"@swc/core-freebsd-x64@^1.2.106": - version "1.2.106" - resolved "https://registry.yarnpkg.com/@swc/core-freebsd-x64/-/core-freebsd-x64-1.2.106.tgz#070ec3ab798009ac14a18c692ede1542b5640ef1" - integrity sha512-ZSK3vgzbA2Pkpw2LgHlAkUdx4okIpdXXTbLXuc5jkZMw1KhRWpeQaDlwbrN7XVynAYjkj2qgGQ7wv1tD43vQig== +"@swc/core-freebsd-x64@^1.2.107": + version "1.2.107" + resolved "https://registry.yarnpkg.com/@swc/core-freebsd-x64/-/core-freebsd-x64-1.2.107.tgz#ea5b6c1c29d4905ade00a11cd9344e0a70be73d2" + integrity sha512-G4RPAZnrBIAoUoAddpbPqeM6CM71m1PM/Y6mXA4iriRo0ro74Og8hlJ9IjsxWM3YXrTgXH6xZ9F0iewhtaKpYA== -"@swc/core-linux-arm-gnueabihf@^1.2.106": - version "1.2.106" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.106.tgz#a94a29bfe81425c6f90a27d5977e58cb469db0a0" - integrity sha512-WZh6XV8cQ9Fh3IQNX9z87Tv68+sLtfnT51ghMQxceRhfvc5gIaYW+PCppezDDdlPJnWXhybGWNPAl5SHppWb2g== +"@swc/core-linux-arm-gnueabihf@^1.2.107": + version "1.2.107" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.107.tgz#f782ff6dd99e405826bd7e192a6e51f15f2070e0" + integrity sha512-7fAK/jSQAnZ9qtZvxwaoCcegT4BDDEyOIulm+fBVZCAcQb/2zZwZuG2P7t2Pzfj1ftEy2A8YPKaUhHl6llOhUg== -"@swc/core-linux-arm64-gnu@^1.2.106": - version "1.2.106" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.106.tgz#6ec685cd37ab3655dbc8eb07a38df6d9532ac32c" - integrity sha512-OSI9VUWPsRrCbUlRQ4KdYqdwV63VYBC5ahSNq+72DXhtRwVbLSFuF7MNsnXgUSMHidxbc0No3/bPPamshqHdsQ== +"@swc/core-linux-arm64-gnu@^1.2.107": + version "1.2.107" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.107.tgz#0b63e2330604fe07f18aa0e1842dd1c498514b8e" + integrity sha512-E0l2hhlsTzl70OqBKqcm8+8rz6zYdNAtce8FM8vmezvgKgIfqlONz2tQyHNkkSKytV6uL5gjla9Ot+aLk2DrLw== -"@swc/core-linux-arm64-musl@^1.2.106": - version "1.2.106" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.2.106.tgz#805841ab7bcca134a2712fb1084c09338ef61168" - integrity sha512-de8AAUOP8D2/tZIpQ399xw+pGGKlR1+l5Jmy4lW7ixarEI4xKkBSF4bS9eXtC1jckmenzrLPiK/5sSbQSf6BWQ== +"@swc/core-linux-arm64-musl@^1.2.107": + version "1.2.107" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.2.107.tgz#8451f84867c17e6cc79259f40438481c5951dc47" + integrity sha512-mdmZ34H3tolvIfjeoFPSxy4AqyM4NucNAVDPU6vRf9imlPcMiI6mFhqwwI0pa4edYVB0pSU34z6Te5LpcrbhrQ== -"@swc/core-linux-x64-gnu@^1.2.106": - version "1.2.106" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.106.tgz#d403dfce5d31dafd5d87491ce80ed603545e4864" - integrity sha512-QzFC7+lBSuVBmX5tS2pdM+74voiJcGgIMJ+x9pcjUu3GkDl3ow6WC6ta2WUzlgGopCGNp6IdZaFemKRzjLr3lw== +"@swc/core-linux-x64-gnu@^1.2.107": + version "1.2.107" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.107.tgz#7381920c40021c3aecd999e61bcfad4b3580102e" + integrity sha512-zY80CTn5h35pHJw+cg2WbAhBICdbzHtEU4o3DJKkx1y26gk3XjvLnEUSsot+eTezthzQyaPuiN1DsHEX1kSouQ== -"@swc/core-linux-x64-musl@^1.2.106": - version "1.2.106" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.106.tgz#0fce602e60cf1c9d9e72d33083dda18b963b3b6d" - integrity sha512-QZ1gFqNiCJefkNMihbmYc7nr5stERyjoQpWgAIN6dzrgMUzRHXHGDRl/p1qsXW2VKos+okSdLwPFEmRT94H+1A== +"@swc/core-linux-x64-musl@^1.2.107": + version "1.2.107" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.107.tgz#a8ca63c3ebca01d02e1dd1ec10b7dbea48fdefa7" + integrity sha512-CIs9oh6QsAiIiZyAS47WcpHklXonNBQ6dg7NXKXXsz9tpAsYqfjs/RWQSH8O6cPihfj0JR2KdfTVyIzXhKfOjg== -"@swc/core-win32-arm64-msvc@^1.2.106": - version "1.2.106" - resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.2.106.tgz#c0e6f5b38a6eac8a5ce438492f23c4ffc47f326f" - integrity sha512-MbuQwk+s43bfBNnAZTKnoQlfo4UPSOsy6t9F15yU4P3rVUuFtcxdZg6CpDnUqNPbojILXujp8z4SSigRYh5cgg== +"@swc/core-win32-arm64-msvc@^1.2.107": + version "1.2.107" + resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.2.107.tgz#03abc92592edbed72e510d04415297a8a3ae14bf" + integrity sha512-5fJTruURSwpLYjoEpc/ZM8LZHB5zbChbEuZn5+Nb5EnxM5vsgHJe6+ZozQ3rpN7BS46nvlWiz14AeuLBsHIH6w== -"@swc/core-win32-ia32-msvc@^1.2.106": - version "1.2.106" - resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.106.tgz#5cf4a824a5bc3cfa6346733a3407e423c1920020" - integrity sha512-BFxWpcPxsG2LLQZ+8K8ma45rbTckjpPbnvOOhybQ0hEhLgoVzMVPp3RIUGmC+RMZe6DkGSaEQf/Rjn2cbMdQhw== +"@swc/core-win32-ia32-msvc@^1.2.107": + version "1.2.107" + resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.107.tgz#38c66fe44e3b78ba3b8855874e48e258219050e4" + integrity sha512-mmgdLtv72Axa/fEkXcw/cv5FkNWzxz9wv+6cy2FQy9xDeY8hTD/GBDdgIolkbFfDiY+NS1N7dUzYArsxUJLBow== -"@swc/core-win32-x64-msvc@^1.2.106": - version "1.2.106" - resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.106.tgz#237d699d25944538fda57dd879478f2cc7835827" - integrity sha512-Emn5akqApGXzPsA7ntSXEohL0AH0WjQMHy6mT3MS9Yil42yTJ96dJGf68ejKVptxwg7Iz798mT+J9r1JbAFBgg== +"@swc/core-win32-x64-msvc@^1.2.107": + version "1.2.107" + resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.107.tgz#fe42bdc3be99de56f289fb34b9e0b35edca8345e" + integrity sha512-W13K5ezQRGBYIgVIy8SIdnoAFWqLX6dYa3KN/Ox75usej+tukP42+CdRJloE/wsdIb12xiKkTU3fpNodJOe2+A== "@swc/core@^1.2.104": - version "1.2.106" - resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.2.106.tgz#d1ae8d5745b6b37fcc5c076e433eb31312c81372" - integrity sha512-9uw8gqU+lsk7KROAcSNhsrnBgNiC5H4MIaps5LlnnEevJmKu/o1ws22tXc2qjJg+F4/V1ynUbh8E0rYlmo1XGw== + version "1.2.107" + resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.2.107.tgz#e269cad35cc03d39016d7747fc3f2f7e3691b92e" + integrity sha512-tkXwcDHdcC8cTaeH5ezpAK3BwDk6H7jmo5/+zsbMJiJgHjQGUGf+81whXIE9iwUUBVISi75FP/VHPEC+qNtg+Q== dependencies: "@node-rs/helper" "^1.0.0" optionalDependencies: - "@swc/core-android-arm64" "^1.2.106" - "@swc/core-darwin-arm64" "^1.2.106" - "@swc/core-darwin-x64" "^1.2.106" - "@swc/core-freebsd-x64" "^1.2.106" - "@swc/core-linux-arm-gnueabihf" "^1.2.106" - "@swc/core-linux-arm64-gnu" "^1.2.106" - "@swc/core-linux-arm64-musl" "^1.2.106" - "@swc/core-linux-x64-gnu" "^1.2.106" - "@swc/core-linux-x64-musl" "^1.2.106" - "@swc/core-win32-arm64-msvc" "^1.2.106" - "@swc/core-win32-ia32-msvc" "^1.2.106" - "@swc/core-win32-x64-msvc" "^1.2.106" + "@swc/core-android-arm64" "^1.2.107" + "@swc/core-darwin-arm64" "^1.2.107" + "@swc/core-darwin-x64" "^1.2.107" + "@swc/core-freebsd-x64" "^1.2.107" + "@swc/core-linux-arm-gnueabihf" "^1.2.107" + "@swc/core-linux-arm64-gnu" "^1.2.107" + "@swc/core-linux-arm64-musl" "^1.2.107" + "@swc/core-linux-x64-gnu" "^1.2.107" + "@swc/core-linux-x64-musl" "^1.2.107" + "@swc/core-win32-arm64-msvc" "^1.2.107" + "@swc/core-win32-ia32-msvc" "^1.2.107" + "@swc/core-win32-x64-msvc" "^1.2.107" "@szmarczak/http-timer@^1.1.2": version "1.1.2" @@ -3071,9 +3071,9 @@ defer-to-connect "^1.0.1" "@testing-library/dom@^8.0.0": - version "8.10.1" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.10.1.tgz#e24fed92ad51c619cf304c6f1410b4c76b1000c0" - integrity sha512-rab7vpf1uGig5efWwsCOn9j4/doy+W3VBoUyzX7C4y77u0wAckwc7R8nyH6e2rw0rRzKJR+gWPiAg8zhiFbxWQ== + version "8.11.0" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.11.0.tgz#3679dfb4db58e0d2b95e4b0929eaf45237b60d94" + integrity sha512-8Ay4UDiMlB5YWy+ZvCeRyFFofs53ebxrWnOFvCoM1HpMAX4cHyuSrCuIM9l2lVuUWUt+Gr3loz/nCwdrnG6ShQ== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" @@ -3320,9 +3320,9 @@ "@types/react" "^16" "@types/react-dom@^17.0.10", "@types/react-dom@^17.0.9": - version "17.0.10" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.10.tgz#d6972ec018d23cf22b99597f1289343d99ea9d9d" - integrity sha512-8oz3NAUId2z/zQdFI09IMhQPNgIbiP8Lslhv39DIDamr846/0spjZK0vnrMak0iB8EKb9QFTTIdg2Wj2zH5a3g== + version "17.0.11" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.11.tgz#e1eadc3c5e86bdb5f7684e00274ae228e7bcc466" + integrity sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q== dependencies: "@types/react" "*" @@ -3344,9 +3344,9 @@ "@types/react" "*" "@types/react@*", "@types/react@^17.0.19", "@types/react@^17.0.33": - version "17.0.33" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.33.tgz#e01ae3de7613dac1094569880bb3792732203ad5" - integrity sha512-pLWntxXpDPaU+RTAuSGWGSEL2FRTNyRQOjSWDke/rxRg14ncsZvx8AKWMWZqvc1UOaJIAoObdZhAWvRaHFi5rw== + version "17.0.34" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.34.tgz#797b66d359b692e3f19991b6b07e4b0c706c0102" + integrity sha512-46FEGrMjc2+8XhHXILr+3+/sTe3OfzSPU9YGKILLrUYbQ1CLQC9Daqo1KzENGXAWwrFwiY0l4ZbF20gRvgpWTg== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -3920,9 +3920,9 @@ asn1.js@^5.2.0: safer-buffer "^2.1.0" asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== dependencies: safer-buffer "~2.1.0" @@ -4322,13 +4322,13 @@ browserslist@4.16.6: escalade "^3.1.1" node-releases "^1.1.71" -browserslist@^4.16.6, browserslist@^4.17.5: - version "4.17.5" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.5.tgz#c827bbe172a4c22b123f5e337533ceebadfdd559" - integrity sha512-I3ekeB92mmpctWBoLXe0d5wPS2cBuRvvW0JyyJHMrk9/HmP2ZjrTboNAZ8iuGqaEIlKguljbQY32OkOJIRrgoA== +browserslist@^4.16.6, browserslist@^4.17.6: + version "4.17.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.6.tgz#c76be33e7786b497f66cad25a73756c8b938985d" + integrity sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw== dependencies: - caniuse-lite "^1.0.30001271" - electron-to-chromium "^1.3.878" + caniuse-lite "^1.0.30001274" + electron-to-chromium "^1.3.886" escalade "^3.1.1" node-releases "^2.0.1" picocolors "^1.0.0" @@ -4608,10 +4608,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== -caniuse-lite@^1.0.30001202, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001228, caniuse-lite@^1.0.30001271: - version "1.0.30001274" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001274.tgz#26ca36204d15b17601ba6fc35dbdad950a647cc7" - integrity sha512-+Nkvv0fHyhISkiMIjnyjmf5YJcQ1IQHZN6U9TLUMroWR38FNwpsC51Gb68yueafX1V6ifOisInSgP9WJFS13ew== +caniuse-lite@^1.0.30001202, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001228, caniuse-lite@^1.0.30001274: + version "1.0.30001278" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001278.tgz#51cafc858df77d966b17f59b5839250b24417fff" + integrity sha512-mpF9KeH8u5cMoEmIic/cr7PNS+F5LWBk0t2ekGT60lFf0Wq+n9LspAj0g3P+o7DQhD3sUdlMln4YFAWhFYn9jg== caseless@~0.12.0: version "0.12.0" @@ -5209,17 +5209,17 @@ copy-descriptor@^0.1.0: integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= core-js-compat@^3.18.0, core-js-compat@^3.19.0: - version "3.19.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.19.0.tgz#b3b93f93c8721b3ed52b91f12f964cc410967f8b" - integrity sha512-R09rKZ56ccGBebjTLZHvzDxhz93YPT37gBm6qUhnwj3Kt7aCjjZWD1injyNbyeFHxNKfeZBSyds6O9n3MKq1sw== + version "3.19.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.19.1.tgz#fe598f1a9bf37310d77c3813968e9f7c7bb99476" + integrity sha512-Q/VJ7jAF/y68+aUsQJ/afPOewdsGkDtcMb40J8MbuWKlK3Y+wtHq8bTHKPj2WKWLIqmS5JhHs4CzHtz6pT2W6g== dependencies: - browserslist "^4.17.5" + browserslist "^4.17.6" semver "7.0.0" core-js-pure@^3.19.0: - version "3.19.0" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.19.0.tgz#db6fdadfdd4dc280ec93b64c3c2e8460e6f10094" - integrity sha512-UEQk8AxyCYvNAs6baNoPqDADv7BX0AmBLGxVsrAifPPx/C8EAzV4Q+2ZUJqVzfI2TQQEZITnwUkWcHpgc/IubQ== + version "3.19.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.19.1.tgz#edffc1fc7634000a55ba05e95b3f0fe9587a5aa4" + integrity sha512-Q0Knr8Es84vtv62ei6/6jXH/7izKmOrtrxH9WJTHLCMAVeU+8TF8z8Nr08CsH4Ot0oJKzBzJJL9SJBYIv7WlfQ== core-js@^2.5.3: version "2.6.12" @@ -5866,6 +5866,16 @@ dotenv-expand@^5.1.0: resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== +dotenv@8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" + integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + +dotenv@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" + integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== + dotenv@^8.2.0: version "8.6.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" @@ -5996,10 +6006,10 @@ electron-publish@22.13.1: lazy-val "^1.0.5" mime "^2.5.2" -electron-to-chromium@^1.3.723, electron-to-chromium@^1.3.878: - version "1.3.886" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.886.tgz#ac039c4001b665b1dd0f0ed9c2e4da90ff3c9267" - integrity sha512-+vYdeBosI63VkCtNWnEVFjgNd/IZwvnsWkKyPtWAvrhA+XfByKoBJcbsMgudVU/bUcGAF9Xp3aXn96voWlc3oQ== +electron-to-chromium@^1.3.723, electron-to-chromium@^1.3.886: + version "1.3.890" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.890.tgz#e7143b659f73dc4d0512d1ae4baeb0fb9e7bc835" + integrity sha512-VWlVXSkv0cA/OOehrEyqjUTHwV8YXCPTfPvbtoeU2aHR21vI4Ejh5aC4AxUwOmbLbBgb6Gd3URZahoCxtBqCYQ== electron-util@^0.17.2: version "0.17.2" @@ -6189,6 +6199,13 @@ esbuild-darwin-arm64@0.13.12: resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.12.tgz#8abae74c2956a8aa568fc52c78829338c4a4b988" integrity sha512-JvAMtshP45Hd8A8wOzjkY1xAnTKTYuP/QUaKp5eUQGX+76GIie3fCdUUr2ZEKdvpSImNqxiZSIMziEiGB5oUmQ== +esbuild-envfile-plugin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/esbuild-envfile-plugin/-/esbuild-envfile-plugin-1.0.1.tgz#e822dc5819d983083c24324b73510745040a7a73" + integrity sha512-azfmAYYcywJ9GAcT13yyPA8kI2ETc1szPcU7wLRYZwfJv15qljWgd0A50InOpX8wa+KP165W8vfRpqDPza3MrQ== + dependencies: + dotenv "8.2.0" + esbuild-freebsd-64@0.13.12: version "0.13.12" resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.12.tgz#6ad2ab8c0364ee7dd2d6e324d876a8e60ae75d12" @@ -6712,9 +6729,9 @@ extsprintf@1.3.0: integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== fake-indexeddb@^3.1.3: version "3.1.7" @@ -7735,12 +7752,12 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -idb-keyval@^5.1.3: - version "5.1.5" - resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-5.1.5.tgz#be11174bac0cb756dba4cc86fb36b6cd63f5ce6d" - integrity sha512-J1utxYWQokYjy01LvDQ7WmiAtZCGUSkVi9EIBfUSyLOr/BesnMIxNGASTh9A1LzeISSjSqEPsfFdTss7EE7ofQ== +idb-keyval@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-6.0.3.tgz#e47246a15e55d0fff9fa204fd9ca06f90ff30c52" + integrity sha512-yh8V7CnE6EQMu9YDwQXhRxwZh4nv+8xm/HV4ZqK4IiYFJBWYGjJuykADJbSP+F/GDXUBwCSSNn/14IpGL81TuA== dependencies: - safari-14-idb-fix "^1.0.6" + safari-14-idb-fix "^3.0.0" idb@^6.0.0: version "6.1.5" @@ -7770,9 +7787,9 @@ ignore@^4.0.3, ignore@^4.0.6: integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== ignore@^5.1.1, ignore@^5.1.4, ignore@^5.1.8: - version "5.1.8" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" - integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + version "5.1.9" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.9.tgz#9ec1a5cbe8e1446ec60d4420060d43aa6e7382fb" + integrity sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ== image-size@1.0.0: version "1.0.0" @@ -10050,17 +10067,17 @@ next-transpile-modules@^9.0.0: escalade "^3.1.1" next@^12.0.1: - version "12.0.2" - resolved "https://registry.yarnpkg.com/next/-/next-12.0.2.tgz#269512cdf14d12c535c12240280da95e7be2d006" - integrity sha512-8YfNLK1pPYZzAhTB1EkaRdpT3KiRUlK8ad3gJOweeclAL7O5c96/GnrEZKMoSkpq1U0Lxa0qRf8ciUmzGfmDmA== + version "12.0.3" + resolved "https://registry.yarnpkg.com/next/-/next-12.0.3.tgz#325732ceb4193306a9a31912815fc570d1a66641" + integrity sha512-GGdhTBcerdMZbitrO67IVetmB+AHa2X69xrkXKClUT8SRu8pEVto/2QMSnfI+uYc5czCUWPsVtVY3aMoMRMaCA== dependencies: "@babel/runtime" "7.15.4" "@hapi/accept" "5.0.2" "@napi-rs/triples" "1.0.3" - "@next/env" "12.0.2" - "@next/polyfill-module" "12.0.2" - "@next/react-dev-overlay" "12.0.2" - "@next/react-refresh-utils" "12.0.2" + "@next/env" "12.0.3" + "@next/polyfill-module" "12.0.3" + "@next/react-dev-overlay" "12.0.3" + "@next/react-refresh-utils" "12.0.3" acorn "8.5.0" assert "2.0.0" browserify-zlib "0.2.0" @@ -10104,17 +10121,17 @@ next@^12.0.1: vm-browserify "1.1.2" watchpack "2.1.1" optionalDependencies: - "@next/swc-android-arm64" "12.0.2" - "@next/swc-darwin-arm64" "12.0.2" - "@next/swc-darwin-x64" "12.0.2" - "@next/swc-linux-arm-gnueabihf" "12.0.2" - "@next/swc-linux-arm64-gnu" "12.0.2" - "@next/swc-linux-arm64-musl" "12.0.2" - "@next/swc-linux-x64-gnu" "12.0.2" - "@next/swc-linux-x64-musl" "12.0.1" - "@next/swc-win32-arm64-msvc" "12.0.2" - "@next/swc-win32-ia32-msvc" "12.0.2" - "@next/swc-win32-x64-msvc" "12.0.2" + "@next/swc-android-arm64" "12.0.3" + "@next/swc-darwin-arm64" "12.0.3" + "@next/swc-darwin-x64" "12.0.3" + "@next/swc-linux-arm-gnueabihf" "12.0.3" + "@next/swc-linux-arm64-gnu" "12.0.3" + "@next/swc-linux-arm64-musl" "12.0.3" + "@next/swc-linux-x64-gnu" "12.0.3" + "@next/swc-linux-x64-musl" "12.0.3" + "@next/swc-win32-arm64-msvc" "12.0.3" + "@next/swc-win32-ia32-msvc" "12.0.3" + "@next/swc-win32-x64-msvc" "12.0.3" nice-try@^1.0.4: version "1.0.5" @@ -11911,13 +11928,13 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rko@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/rko/-/rko-0.6.0.tgz#fa640384b4e82fdcd90fc58c958256148c4eb10c" - integrity sha512-u05SAiyz02Sw+QyGaQb3NGPXf3xXxQ9AwNG+tItHx2MpAsPEEH84NqYDyG9jem/ji/FPQPQHuRKcy2MHb1a1Ow== +rko@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rko/-/rko-0.6.2.tgz#34dd6c68623db4f5147e3d2697d51971106083d8" + integrity sha512-94Fy/y17u7+CQ1DcIXQ8BBkGs/Wu+4pqf1ooFWMOIGVkQPgbB6N9sPBBCmoVoRlFrQQlxARHiqeIWjRRcwMmFQ== dependencies: - idb-keyval "^5.1.3" - zustand "^3.5.9" + idb-keyval "^6.0.3" + zustand "^3.6.4" roarr@^2.15.3: version "2.15.4" @@ -11981,10 +11998,10 @@ rxjs@^7.4.0: dependencies: tslib "~2.1.0" -safari-14-idb-fix@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/safari-14-idb-fix/-/safari-14-idb-fix-1.0.6.tgz#cbaabc33a4500c44b5c432d6c525b0ed9b68bb65" - integrity sha512-oTEQOdMwRX+uCtWCKT1nx2gAeSdpr8elg/2gcaKUH00SJU2xWESfkx11nmXwTRHy7xfQoj1o4TTQvdmuBosTnA== +safari-14-idb-fix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/safari-14-idb-fix/-/safari-14-idb-fix-3.0.0.tgz#450fc049b996ec7f3fd9ca2f89d32e0761583440" + integrity sha512-eBNFLob4PMq8JA1dGyFn6G97q3/WzNtFK4RnzT1fnLq+9RyrGknzYiM/9B12MnKAxuj1IXr7UKYtTNtjyKMBog== safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" @@ -12833,12 +12850,11 @@ symbol-tree@^3.2.4: integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== table@^6.0.9: - version "6.7.2" - resolved "https://registry.yarnpkg.com/table/-/table-6.7.2.tgz#a8d39b9f5966693ca8b0feba270a78722cbaf3b0" - integrity sha512-UFZK67uvyNivLeQbVtkiUs8Uuuxv24aSL4/Vil2PJVtMgU8Lx0CYkP12uCGa3kjyQzOSgV1+z9Wkb82fCGsO0g== + version "6.7.3" + resolved "https://registry.yarnpkg.com/table/-/table-6.7.3.tgz#255388439715a738391bd2ee4cbca89a4d05a9b7" + integrity sha512-5DkIxeA7XERBqMwJq0aHZOdMadBx4e6eDoFRuyT5VR82J0Ycg2DwM6GfA/EQAhJ+toRTaS1lIdSQCqgrmhPnlw== dependencies: ajv "^8.0.1" - lodash.clonedeep "^4.5.0" lodash.truncate "^4.4.2" slice-ansi "^4.0.0" string-width "^4.2.3" @@ -13289,9 +13305,9 @@ typedarray@^0.0.6: integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= typedoc@^0.22.3: - version "0.22.7" - resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.22.7.tgz#e5e095ab14676296f4b12ac3355321eae650e447" - integrity sha512-ndxxp+tU1Wczvdxp4u2/PvT1qjD6hdFdSdehpORHjE+JXmMkl2bftXCR0upHmsnesBG7VCcr8vfgloGHIH8glQ== + version "0.22.8" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.22.8.tgz#cd488290bd31d2cd4099004e1104807895d4eb17" + integrity sha512-92S+YzyhospdXN5rnkYUTgirdTYqNWY7NP9vco+IqQQoiSXzVSUsawVro+tMyEEsWUS7EMaJ2YOjB9uE0CBi6A== dependencies: glob "^7.2.0" lunr "^2.3.9" @@ -14238,7 +14254,7 @@ zen-observable@0.8.15: resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== -zustand@^3.5.9: - version "3.6.2" - resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.6.2.tgz#76bd927c8f82c14d7aae9f592323371b583e564a" - integrity sha512-Jw+tM8T3koFjwLLj8ihlYcMqp3nHfpRNOrc/qqwFhmVp7nmrBD/N73bEb5cjveFKNQtAt+7n2S+gnIbhrV/4qA== +zustand@^3.6.4: + version "3.6.4" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.6.4.tgz#2aed404072e3d4538fbc380bcf4eee88b174051d" + integrity sha512-liH2ZaEOSiEaVEl7N0CVzoKYZCQPpibfsIgB2ksrjvfu17WME8Eh7XV/MCi5OQM5AnbuYbLowplR03UP5yrNYw==