[improvement] Migrations (#217)

* Add better migrations

* Improves migrations

* rename dev to example

* renames migrate

* Removes workers from git

* Remove rogue dependency

* Fix dropdown navigation by keyboard
This commit is contained in:
Steve Ruiz 2021-11-04 15:48:39 +00:00 committed by GitHub
parent cb777c85d1
commit fe2e3c81fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 516 additions and 271 deletions

4
.gitignore vendored
View file

@ -8,3 +8,7 @@ docs/
.DS_Store .DS_Store
coverage coverage
*.log *.log
www/public/worker-*
www/public/sw.js
www/public/sw.js.map

193
README.md
View file

@ -1,19 +1,23 @@
<div style="text-align: center; transform: scale(.5);">
<img src="card-repo.png"/>
</div>
# @tldraw/tldraw # @tldraw/tldraw
> `This library is not yet released and these docs are partially out of date!` This package contains the [tldraw](https://tldraw.com) editor as a React component named `<TLDraw>`. You can use this package to embed the editor in any React application.
This package contains the [tldraw](https://tldraw.com) editor as a standalone React component. 🎨 Want to build your own tldraw-ish app instead? Try [@tldraw/core](https://github.com/tldraw/core).
💕 Love this library? Consider [becoming a sponsor](https://github.com/sponsors/steveruizok?frequency=recurring&sponsor=steveruizok).
## Installation ## Installation
```bash Use your package manager of choice to install `@tldraw/core` and its peer dependencies.
npm i @tldraw/tldraw
```
or
```bash ```bash
yarn add @tldraw/tldraw yarn add @tldraw/tldraw
# or
npm i @tldraw/tldraw
``` ```
## Usage ## Usage
@ -28,31 +32,70 @@ function App() {
} }
``` ```
You can control the `TLDraw` component through props:
```tsx
import { TLDraw, TLDrawDocument } from '@tldraw/tldraw'
function App() {
const myDocument: TLDrawDocument = {}
return <TLDraw document={document} />
}
```
Or imperatively through the `TLDrawState` instance:
```tsx
import { TLDraw, TLDrawState } from '@tldraw/tldraw'
function App() {
const handleMount = React.useCallback((tlstate: TLDrawState) => {
const myDocument: TLDrawDocument = {}
tlstate.loadDocument(myDocument).selectAll()
}, [])
return <TLDraw onMount={handleMount} />
}
```
## Documentation ## Documentation
### `TLDraw` ### `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. 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 | | Prop | Type | Description |
| --------------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | --------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id` | `string` | (optional) An id under which to persist the component's state. | | `id` | `string` | An id under which to persist the component's state. |
| `document` | `TLDrawDocument` | (optional) An initial [`TLDrawDocument`](#tldrawdocument) object. | | `document` | `TLDrawDocument` | An initial [`TLDrawDocument`](#tldrawdocument) object. |
| `currentPageId` | `string` | (optional) A current page id, referencing the `TLDrawDocument` object provided via the `document` prop. | | `currentPageId` | `string` | A current page id, referencing the `TLDrawDocument` object provided via the `document` prop. |
| `onMount` | `(TLDrawState) => void` | (optional) A callback function that will be called when the editor first mounts, receiving the current `TLDrawState`. | | `onMount` | `Function` | A callback function that will be called when the editor first mounts, receiving the current `TLDrawState`. |
| `onChange` | `(TLDrawState, string) => void` | (optional) A callback function that will be called whenever the `TLDrawState` updates. The update will include the current `TLDrawState` and the reason for the change. | | `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. |
### `TLDrawDocument` ### `TLDrawDocument`
A `TLDrawDocument` is an object with three properties: A `TLDrawDocument` is an object with three properties:
- `id` - A unique ID for this document - `id` - A unique ID for this document
- `pages` - A table of `TLPage` objects - `pages` - A table of `TLDrawPage` objects
- `pageStates` - A table of `TLPageState` objects - `pageStates` - A table of `TLPageState` objects
- `version` - The document's version, used internally for migrations.
```ts ```ts
import { TLDrawDocument, TLDrawState } from '@tldraw/tldraw'
const tldocument: TLDrawDocument = { const tldocument: TLDrawDocument = {
id: 'doc', id: 'doc',
version: TLDrawState.version,
pages: { pages: {
page1: { page1: {
id: 'page1', id: 'page1',
@ -74,30 +117,30 @@ const tldocument: TLDrawDocument = {
} }
``` ```
**Important:** In the `pages` object, each `TLPage` object must be keyed under its `id` property. Likewise, each `TLPageState` object must be keyed under its `id`. In addition, each `TLPageState` object must have an `id` that matches its corresponding page. **Tip:** TLDraw is built [@tldraw/core](https://github.com/tldraw/core). The pages and pagestates in TLDraw are just objects containing `TLPage` and `TLPageState` objects from the core library. For more about these types, check out the [@tldraw/core](https://github.com/tldraw/core) documentation.
In the example above, the page above with the id `page1`is at `tldocument.pages["page1"]`. Its corresponding page state has the same id (`page1`) and is at `tldocument.pageStates["page1"]`. **Important:** In the `pages` object, each `TLPage` object must be keyed under its `id` property. Likewise, each `TLPageState` object must be keyed under its `id`. In addition, each `TLPageState` object must have an `id` that matches its corresponding page.
### Shapes ### Shapes
Your `TLPage` objects may include shapes: objects that fit one of the `TLDrawShape` interfaces listed below. All `TLDrawShapes` extends a common interface: Your `TLPage` objects may include shapes: objects that fit one of the `TLDrawShape` interfaces listed below. All `TLDrawShapes` extends a common interface:
| Property | Type | Description | | Property | Type | Description |
| --------------------- | ------------ | --------------------------------------------------------------- | | --------------------- | ---------------- | --------------------------------------------------------------- |
| `id` | `string` | A unique ID for the shape. | | `id` | `string` | A unique ID for the shape. |
| `name` | `string` | The shape's name. | | `name` | `string` | The shape's name. |
| `type` | `string` | The shape's type. | | `type` | `string` | The shape's type. |
| `parentId` | `string` | The ID of the shape's parent (a shape or its page). | | `parentId` | `string` | The ID of the shape's parent (a shape or its page). |
| `childIndex` | `number` | The shape's order within its parent's children, indexed from 1. | | `childIndex` | `number` | The shape's order within its parent's children, indexed from 1. |
| `point` | `number[]` | The `[x, y]` position of the shape. | | `point` | `number[]` | The `[x, y]` position of the shape. |
| `rotation` | `number[]` | (optional) The shape's rotation in radians. | | `rotation` | `number[]` | (optional) The shape's rotation in radians. |
| `children` | `string[]` | (optional) The shape's child shape ids. | | `children` | `string[]` | (optional) The shape's child shape ids. |
| `handles` | `TLHandle{}` | (optional) A table of `TLHandle` objects. | | `handles` | `TLDrawHandle{}` | (optional) A table of `TLHandle` objects. |
| `isLocked` | `boolean` | True if the shape is locked. | | `isLocked` | `boolean` | (optional) True if the shape is locked. |
| `isHidden` | `boolean` | True if the shape is hidden. | | `isHidden` | `boolean` | (optional) True if the shape is hidden. |
| `isEditing` | `boolean` | True if the shape is currently editing. | | `isEditing` | `boolean` | (optional) True if the shape is currently editing. |
| `isGenerated` | `boolean` | True if the shape is generated. | | `isGenerated` | `boolean` | (optional) True if the shape is generated. |
| `isAspectRatioLocked` | `boolean` | True if the shape's aspect ratio is locked. | | `isAspectRatioLocked` | `boolean` | (optional) True if the shape's aspect ratio is locked. |
> **Important:** In order for re-ordering to work correctly, a shape's `childIndex` values _must_ start from 1, not 0. The page or parent shape's "bottom-most" child should have a `childIndex` of 1. > **Important:** In order for re-ordering to work correctly, a shape's `childIndex` values _must_ start from 1, not 0. The page or parent shape's "bottom-most" child should have a `childIndex` of 1.
@ -110,38 +153,98 @@ The `ShapeStyle` object is a common style API for all shapes.
| `color` | `ColorStyle` | The shape's color. | | `color` | `ColorStyle` | The shape's color. |
| `isFilled` | `boolean` | (optional) True if the shape is filled. | | `isFilled` | `boolean` | (optional) True if the shape is filled. |
#### Draw #### `DrawShape`
A hand-drawn line.
| Property | Type | Description | | Property | Type | Description |
| -------- | ------------ | ----------------------------------------- | | -------- | ------------ | ----------------------------------------- |
| `points` | `number[][]` | An array of points as `[x, y, pressure]`. | | `points` | `number[][]` | An array of points as `[x, y, pressure]`. |
##### Rectangle ##### `RectangleShape`
A rectangular shape.
| Property | Type | Description | | Property | Type | Description |
| -------- | ---------- | --------------------------------------- | | -------- | ---------- | --------------------------------------- |
| `size` | `number[]` | The `[width, height]` of the rectangle. | | `size` | `number[]` | The `[width, height]` of the rectangle. |
#### Ellipse #### `EllipseShape`
An elliptical shape.
| Property | Type | Description | | Property | Type | Description |
| -------- | ---------- | ----------------------------------- | | -------- | ---------- | ----------------------------------- |
| `radius` | `number[]` | The `[x, y]` radius of the ellipse. | | `radius` | `number[]` | The `[x, y]` radius of the ellipse. |
#### Arrow #### `ArrowShape`
| Property | Type | Description | An arrow that can connect shapes.
| --------- | -------- | ----------------------------------------------------------------------- |
| `handles` | `object` | An object with three `TLHandle` properties: `start`, `end`, and `bend`. |
#### Text | Property | Type | Description |
| ------------- | -------- | ----------------------------------------------------------------------- |
| `handles` | `object` | An object with three `TLHandle` properties: `start`, `end`, and `bend`. |
| `decorations` | `object` | An object with two properties `start`, `end`, and `bend`. |
#### `TextShape`
A line of text.
| Property | Type | Description | | Property | Type | Description |
| -------- | -------- | ------------------------- | | -------- | -------- | ------------------------- |
| `text` | `string` | The shape's text content. | | `text` | `string` | The shape's text content. |
## Development #### `StickyShape`
### Running unit tests A sticky note.
Run `nx test tldraw` to execute the unit tests via [Jest](https://jestjs.io). | Property | Type | Description |
| -------- | -------- | ------------------------- |
| `text` | `string` | The shape's text content. |
### Bindings
A binding is a connection **from** one shape and **to** another shape. At the moment, only arrows may be bound "from". Most shapes may be bound "to", except other `ArrowShape` and `DrawShape`s.
| Property | Type | Description |
| ---------- | ---------------- | -------------------------------------------------------- |
| `id` | `string` | The binding's own unique ID. |
| `fromId` | `string` | The id of the `ArrowShape` that the binding is bound to. |
| `toId` | `string` | The id of the other shape that the binding is bound to. |
| `handleId` | `start` or `end` | The connected arrow handle. |
| `distance` | `number` | The distance from the bound point. |
| `point` | `number[]` | A normalized point representing the bound point. |
## Local Development
- Run `yarn` to install dependencies.
- Run `yarn start` to start the development server for the package and for the example.
- Open `localhost:5000` to view the example project.
- Run `yarn test` to execute unit tests via [Jest](https://jestjs.io).
- Run `yarn docs` to build the docs via [ts-doc](https://typedoc.org/).
## Example
See the `example` folder.
## Community
### Support
Need help? Please [open an issue](https://github.com/tldraw/tldraw/issues/new) for support.
### Discussion
Want to connect with other devs? Visit the [Discord channel](https://discord.gg/s4FXZ6fppJ).
### License
This project is licensed under MIT. If you're using the library in a commercial product, please consider [becoming a sponsor](https://github.com/sponsors/steveruizok?frequency=recurring&sponsor=steveruizok).
## Author
- [@steveruizok](https://twitter.com/steveruizok)

View file

@ -1,6 +0,0 @@
# @tldraw/dev
A very fast dev server.
You probably do not need to start the server here: it is started as
part of `yarn start` in the root directory.

6
example/README.md Normal file
View file

@ -0,0 +1,6 @@
# @tldraw/tldraw-example
An example for @tldraw/tldraw with a very fast dev server.
**Note:** You probably do not need to start the server here: it is started as
part of `yarn start` in the root directory.

View file

@ -1,8 +1,8 @@
{ {
"name": "@tldraw/dev", "name": "@tldraw/tldraw-example",
"version": "0.1.1", "version": "0.1.1",
"private": true, "private": true,
"description": "A tiny little drawing app (dev)", "description": "A tiny little drawing app example.",
"author": "@steveruizok", "author": "@steveruizok",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [
@ -18,14 +18,10 @@
"src" "src"
], ],
"sideEffects": false, "sideEffects": false,
"dependencies": { "dependencies": {},
"@liveblocks/client": "^0.12.1", "peerDependencies": {
"@liveblocks/react": "^0.12.1",
"@tldraw/tldraw": "^0.1.1",
"react": ">=16.8", "react": ">=16.8",
"react-dom": "^16.8 || ^17.0", "react-dom": "^16.8 || ^17.0"
"react-router": "^5.2.1",
"react-router-dom": "^5.3.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^14.14.35", "@types/node": "^14.14.35",
@ -36,7 +32,12 @@
"create-serve": "1.0.1", "create-serve": "1.0.1",
"esbuild": "^0.13.8", "esbuild": "^0.13.8",
"rimraf": "3.0.2", "rimraf": "3.0.2",
"typescript": "4.2.3" "typescript": "4.2.3",
"@liveblocks/client": "^0.12.1",
"@liveblocks/react": "^0.12.1",
"@tldraw/tldraw": "^0.1.1",
"react-router": "^5.2.1",
"react-router-dom": "^5.3.0"
}, },
"gitHead": "a7dac0f83ad998e205c2aab58182cb4ba4e099a6" "gitHead": "a7dac0f83ad998e205c2aab58182cb4ba4e099a6"
} }

View file

@ -40,24 +40,8 @@ function TLDrawWrapper() {
const doc = useObject<{ uuid: string; document: TLDrawDocument }>('doc', { const doc = useObject<{ uuid: string; document: TLDrawDocument }>('doc', {
uuid: docId, uuid: docId,
document: { document: {
...TLDrawState.defaultDocument,
id: 'test-room', id: 'test-room',
pages: {
page: {
id: 'page',
shapes: {},
bindings: {},
},
},
pageStates: {
page: {
id: 'page',
selectedIds: [],
camera: {
point: [0, 0],
zoom: 1,
},
},
},
}, },
}) })

View file

@ -10,7 +10,7 @@
"license": "MIT", "license": "MIT",
"workspaces": [ "workspaces": [
"packages/tldraw", "packages/tldraw",
"dev", "example",
"www" "www"
], ],
"scripts": { "scripts": {
@ -45,9 +45,6 @@
"typedoc": "^0.22.3", "typedoc": "^0.22.3",
"typescript": "^4.4.2" "typescript": "^4.4.2"
}, },
"dependencies": {
"www": "0.0.133"
},
"prettier": { "prettier": {
"trailingComma": "es5", "trailingComma": "es5",
"singleQuote": true, "singleQuote": true,
@ -80,4 +77,4 @@
"\\~(.*)": "<rootDir>/packages/tldraw/src/$1" "\\~(.*)": "<rootDir>/packages/tldraw/src/$1"
} }
} }
} }

View file

@ -1,19 +1,23 @@
<div style="text-align: center; transform: scale(.5);">
<img src="card-repo.png"/>
</div>
# @tldraw/tldraw # @tldraw/tldraw
> `This library is not yet released and these docs are partially out of date!` This package contains the [tldraw](https://tldraw.com) editor as a React component named `<TLDraw>`. You can use this package to embed the editor in any React application.
This package contains the [tldraw](https://tldraw.com) editor as a standalone React component. 🎨 Want to build your own tldraw-ish app instead? Try [@tldraw/core](https://github.com/tldraw/core).
💕 Love this library? Consider [becoming a sponsor](https://github.com/sponsors/steveruizok?frequency=recurring&sponsor=steveruizok).
## Installation ## Installation
```bash Use your package manager of choice to install `@tldraw/core` and its peer dependencies.
npm i @tldraw/tldraw
```
or
```bash ```bash
yarn add @tldraw/tldraw yarn add @tldraw/tldraw
# or
npm i @tldraw/tldraw
``` ```
## Usage ## Usage
@ -28,31 +32,70 @@ function App() {
} }
``` ```
You can control the `TLDraw` component through props:
```tsx
import { TLDraw, TLDrawDocument } from '@tldraw/tldraw'
function App() {
const myDocument: TLDrawDocument = {}
return <TLDraw document={document} />
}
```
Or imperatively through the `TLDrawState` instance:
```tsx
import { TLDraw, TLDrawState } from '@tldraw/tldraw'
function App() {
const handleMount = React.useCallback((tlstate: TLDrawState) => {
const myDocument: TLDrawDocument = {}
tlstate.loadDocument(myDocument).selectAll()
}, [])
return <TLDraw onMount={handleMount} />
}
```
## Documentation ## Documentation
### `TLDraw` ### `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. 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 | | Prop | Type | Description |
| --------------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | --------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id` | `string` | (optional) An id under which to persist the component's state. | | `id` | `string` | An id under which to persist the component's state. |
| `document` | `TLDrawDocument` | (optional) An initial [`TLDrawDocument`](#tldrawdocument) object. | | `document` | `TLDrawDocument` | An initial [`TLDrawDocument`](#tldrawdocument) object. |
| `currentPageId` | `string` | (optional) A current page id, referencing the `TLDrawDocument` object provided via the `document` prop. | | `currentPageId` | `string` | A current page id, referencing the `TLDrawDocument` object provided via the `document` prop. |
| `onMount` | `(TLDrawState) => void` | (optional) A callback function that will be called when the editor first mounts, receiving the current `TLDrawState`. | | `onMount` | `Function` | A callback function that will be called when the editor first mounts, receiving the current `TLDrawState`. |
| `onChange` | `(TLDrawState, string) => void` | (optional) A callback function that will be called whenever the `TLDrawState` updates. The update will include the current `TLDrawState` and the reason for the change. | | `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. |
### `TLDrawDocument` ### `TLDrawDocument`
A `TLDrawDocument` is an object with three properties: A `TLDrawDocument` is an object with three properties:
- `id` - A unique ID for this document - `id` - A unique ID for this document
- `pages` - A table of `TLPage` objects - `pages` - A table of `TLDrawPage` objects
- `pageStates` - A table of `TLPageState` objects - `pageStates` - A table of `TLPageState` objects
- `version` - The document's version, used internally for migrations.
```ts ```ts
import { TLDrawDocument, TLDrawState } from '@tldraw/tldraw'
const tldocument: TLDrawDocument = { const tldocument: TLDrawDocument = {
id: 'doc', id: 'doc',
version: TLDrawState.version,
pages: { pages: {
page1: { page1: {
id: 'page1', id: 'page1',
@ -74,30 +117,30 @@ const tldocument: TLDrawDocument = {
} }
``` ```
**Important:** In the `pages` object, each `TLPage` object must be keyed under its `id` property. Likewise, each `TLPageState` object must be keyed under its `id`. In addition, each `TLPageState` object must have an `id` that matches its corresponding page. **Tip:** TLDraw is built [@tldraw/core](https://github.com/tldraw/core). The pages and pagestates in TLDraw are just objects containing `TLPage` and `TLPageState` objects from the core library. For more about these types, check out the [@tldraw/core](https://github.com/tldraw/core) documentation.
In the example above, the page above with the id `page1`is at `tldocument.pages["page1"]`. Its corresponding page state has the same id (`page1`) and is at `tldocument.pageStates["page1"]`. **Important:** In the `pages` object, each `TLPage` object must be keyed under its `id` property. Likewise, each `TLPageState` object must be keyed under its `id`. In addition, each `TLPageState` object must have an `id` that matches its corresponding page.
### Shapes ### Shapes
Your `TLPage` objects may include shapes: objects that fit one of the `TLDrawShape` interfaces listed below. All `TLDrawShapes` extends a common interface: Your `TLPage` objects may include shapes: objects that fit one of the `TLDrawShape` interfaces listed below. All `TLDrawShapes` extends a common interface:
| Property | Type | Description | | Property | Type | Description |
| --------------------- | ------------ | --------------------------------------------------------------- | | --------------------- | ---------------- | --------------------------------------------------------------- |
| `id` | `string` | A unique ID for the shape. | | `id` | `string` | A unique ID for the shape. |
| `name` | `string` | The shape's name. | | `name` | `string` | The shape's name. |
| `type` | `string` | The shape's type. | | `type` | `string` | The shape's type. |
| `parentId` | `string` | The ID of the shape's parent (a shape or its page). | | `parentId` | `string` | The ID of the shape's parent (a shape or its page). |
| `childIndex` | `number` | The shape's order within its parent's children, indexed from 1. | | `childIndex` | `number` | The shape's order within its parent's children, indexed from 1. |
| `point` | `number[]` | The `[x, y]` position of the shape. | | `point` | `number[]` | The `[x, y]` position of the shape. |
| `rotation` | `number[]` | (optional) The shape's rotation in radians. | | `rotation` | `number[]` | (optional) The shape's rotation in radians. |
| `children` | `string[]` | (optional) The shape's child shape ids. | | `children` | `string[]` | (optional) The shape's child shape ids. |
| `handles` | `TLHandle{}` | (optional) A table of `TLHandle` objects. | | `handles` | `TLDrawHandle{}` | (optional) A table of `TLHandle` objects. |
| `isLocked` | `boolean` | True if the shape is locked. | | `isLocked` | `boolean` | (optional) True if the shape is locked. |
| `isHidden` | `boolean` | True if the shape is hidden. | | `isHidden` | `boolean` | (optional) True if the shape is hidden. |
| `isEditing` | `boolean` | True if the shape is currently editing. | | `isEditing` | `boolean` | (optional) True if the shape is currently editing. |
| `isGenerated` | `boolean` | True if the shape is generated. | | `isGenerated` | `boolean` | (optional) True if the shape is generated. |
| `isAspectRatioLocked` | `boolean` | True if the shape's aspect ratio is locked. | | `isAspectRatioLocked` | `boolean` | (optional) True if the shape's aspect ratio is locked. |
> **Important:** In order for re-ordering to work correctly, a shape's `childIndex` values _must_ start from 1, not 0. The page or parent shape's "bottom-most" child should have a `childIndex` of 1. > **Important:** In order for re-ordering to work correctly, a shape's `childIndex` values _must_ start from 1, not 0. The page or parent shape's "bottom-most" child should have a `childIndex` of 1.
@ -110,38 +153,98 @@ The `ShapeStyle` object is a common style API for all shapes.
| `color` | `ColorStyle` | The shape's color. | | `color` | `ColorStyle` | The shape's color. |
| `isFilled` | `boolean` | (optional) True if the shape is filled. | | `isFilled` | `boolean` | (optional) True if the shape is filled. |
#### Draw #### `DrawShape`
A hand-drawn line.
| Property | Type | Description | | Property | Type | Description |
| -------- | ------------ | ----------------------------------------- | | -------- | ------------ | ----------------------------------------- |
| `points` | `number[][]` | An array of points as `[x, y, pressure]`. | | `points` | `number[][]` | An array of points as `[x, y, pressure]`. |
##### Rectangle ##### `RectangleShape`
A rectangular shape.
| Property | Type | Description | | Property | Type | Description |
| -------- | ---------- | --------------------------------------- | | -------- | ---------- | --------------------------------------- |
| `size` | `number[]` | The `[width, height]` of the rectangle. | | `size` | `number[]` | The `[width, height]` of the rectangle. |
#### Ellipse #### `EllipseShape`
An elliptical shape.
| Property | Type | Description | | Property | Type | Description |
| -------- | ---------- | ----------------------------------- | | -------- | ---------- | ----------------------------------- |
| `radius` | `number[]` | The `[x, y]` radius of the ellipse. | | `radius` | `number[]` | The `[x, y]` radius of the ellipse. |
#### Arrow #### `ArrowShape`
| Property | Type | Description | An arrow that can connect shapes.
| --------- | -------- | ----------------------------------------------------------------------- |
| `handles` | `object` | An object with three `TLHandle` properties: `start`, `end`, and `bend`. |
#### Text | Property | Type | Description |
| ------------- | -------- | ----------------------------------------------------------------------- |
| `handles` | `object` | An object with three `TLHandle` properties: `start`, `end`, and `bend`. |
| `decorations` | `object` | An object with two properties `start`, `end`, and `bend`. |
#### `TextShape`
A line of text.
| Property | Type | Description | | Property | Type | Description |
| -------- | -------- | ------------------------- | | -------- | -------- | ------------------------- |
| `text` | `string` | The shape's text content. | | `text` | `string` | The shape's text content. |
## Development #### `StickyShape`
### Running unit tests A sticky note.
Run `nx test tldraw` to execute the unit tests via [Jest](https://jestjs.io). | Property | Type | Description |
| -------- | -------- | ------------------------- |
| `text` | `string` | The shape's text content. |
### Bindings
A binding is a connection **from** one shape and **to** another shape. At the moment, only arrows may be bound "from". Most shapes may be bound "to", except other `ArrowShape` and `DrawShape`s.
| Property | Type | Description |
| ---------- | ---------------- | -------------------------------------------------------- |
| `id` | `string` | The binding's own unique ID. |
| `fromId` | `string` | The id of the `ArrowShape` that the binding is bound to. |
| `toId` | `string` | The id of the other shape that the binding is bound to. |
| `handleId` | `start` or `end` | The connected arrow handle. |
| `distance` | `number` | The distance from the bound point. |
| `point` | `number[]` | A normalized point representing the bound point. |
## Local Development
- Run `yarn` to install dependencies.
- Run `yarn start` to start the development server for the package and for the example.
- Open `localhost:5000` to view the example project.
- Run `yarn test` to execute unit tests via [Jest](https://jestjs.io).
- Run `yarn docs` to build the docs via [ts-doc](https://typedoc.org/).
## Example
See the `example` folder.
## Community
### Support
Need help? Please [open an issue](https://github.com/tldraw/tldraw/issues/new) for support.
### Discussion
Want to connect with other devs? Visit the [Discord channel](https://discord.gg/s4FXZ6fppJ).
### License
This project is licensed under MIT. If you're using the library in a commercial product, please consider [becoming a sponsor](https://github.com/sponsors/steveruizok?frequency=recurring&sponsor=steveruizok).
## Author
- [@steveruizok](https://twitter.com/steveruizok)

View file

@ -50,7 +50,7 @@
"@tldraw/vec": "^0.1.3", "@tldraw/vec": "^0.1.3",
"perfect-freehand": "^1.0.16", "perfect-freehand": "^1.0.16",
"react-hotkeys-hook": "^3.4.0", "react-hotkeys-hook": "^3.4.0",
"rko": "^0.5.25" "rko": "^0.6.0"
}, },
"devDependencies": { "devDependencies": {
"tsconfig-replace-paths": "^0.0.5" "tsconfig-replace-paths": "^0.0.5"

View file

@ -4,6 +4,7 @@ import { Tooltip } from '~components/Tooltip'
import styled from '~styles' import styled from '~styles'
export interface ToolButtonProps { export interface ToolButtonProps {
onClick?: () => void
onSelect?: () => void onSelect?: () => void
onDoubleClick?: () => void onDoubleClick?: () => void
isActive?: boolean isActive?: boolean
@ -12,12 +13,13 @@ export interface ToolButtonProps {
} }
export const ToolButton = React.forwardRef<HTMLButtonElement, ToolButtonProps>( export const ToolButton = React.forwardRef<HTMLButtonElement, ToolButtonProps>(
({ onSelect, onDoubleClick, isActive = false, variant, children, ...rest }, ref) => { ({ onSelect, onClick, onDoubleClick, isActive = false, variant, children, ...rest }, ref) => {
return ( return (
<StyledToolButton <StyledToolButton
ref={ref} ref={ref}
isActive={isActive} isActive={isActive}
variant={variant} variant={variant}
onClick={onClick}
onPointerDown={onSelect} onPointerDown={onSelect}
onDoubleClick={onDoubleClick} onDoubleClick={onDoubleClick}
bp={breakpoints} bp={breakpoints}

View file

@ -4,14 +4,14 @@ import { strokes } from '~shape-utils'
import { useTheme, useTLDrawContext } from '~hooks' import { useTheme, useTLDrawContext } from '~hooks'
import type { Data, ColorStyle } from '~types' import type { Data, ColorStyle } from '~types'
import CircleIcon from '~components/icons/CircleIcon' import CircleIcon from '~components/icons/CircleIcon'
import { DMContent, DMRadioItem, DMTriggerIcon } from '~components/DropdownMenu' import { DMContent, DMTriggerIcon } from '~components/DropdownMenu'
import { BoxIcon } from '~components/icons' import { BoxIcon } from '~components/icons'
import { IconButton } from '~components/IconButton'
import { ToolButton } from '~components/ToolButton' import { ToolButton } from '~components/ToolButton'
import { Tooltip } from '~components/Tooltip'
const selectColor = (s: Data) => s.appState.selectedStyle.color const selectColor = (s: Data) => s.appState.selectedStyle.color
const preventEvent = (e: Event) => e.preventDefault()
export const ColorMenu = React.memo((): JSX.Element => { export const ColorMenu = React.memo((): JSX.Element => {
const { theme } = useTheme() const { theme } = useTheme()
const { tlstate, useSelector } = useTLDrawContext() const { tlstate, useSelector } = useTLDrawContext()
@ -25,17 +25,18 @@ export const ColorMenu = React.memo((): JSX.Element => {
</DMTriggerIcon> </DMTriggerIcon>
<DMContent variant="grid"> <DMContent variant="grid">
{Object.keys(strokes[theme]).map((colorStyle: string) => ( {Object.keys(strokes[theme]).map((colorStyle: string) => (
<ToolButton <DropdownMenu.Item key={colorStyle} onSelect={preventEvent} asChild>
key={colorStyle} <ToolButton
variant="icon" variant="icon"
isActive={color === colorStyle} isActive={color === colorStyle}
onSelect={() => tlstate.style({ color: colorStyle as ColorStyle })} onClick={() => tlstate.style({ color: colorStyle as ColorStyle })}
> >
<BoxIcon <BoxIcon
fill={strokes[theme][colorStyle as ColorStyle]} fill={strokes[theme][colorStyle as ColorStyle]}
stroke={strokes[theme][colorStyle as ColorStyle]} stroke={strokes[theme][colorStyle as ColorStyle]}
/> />
</ToolButton> </ToolButton>
</DropdownMenu.Item>
))} ))}
</DMContent> </DMContent>
</DropdownMenu.Root> </DropdownMenu.Root>

View file

@ -15,6 +15,8 @@ const dashes = {
const selectDash = (s: Data) => s.appState.selectedStyle.dash const selectDash = (s: Data) => s.appState.selectedStyle.dash
const preventEvent = (e: Event) => e.preventDefault()
export const DashMenu = React.memo((): JSX.Element => { export const DashMenu = React.memo((): JSX.Element => {
const { tlstate, useSelector } = useTLDrawContext() const { tlstate, useSelector } = useTLDrawContext()
@ -24,15 +26,16 @@ export const DashMenu = React.memo((): JSX.Element => {
<DropdownMenu.Root dir="ltr"> <DropdownMenu.Root dir="ltr">
<DMTriggerIcon>{dashes[dash]}</DMTriggerIcon> <DMTriggerIcon>{dashes[dash]}</DMTriggerIcon>
<DMContent variant="horizontal"> <DMContent variant="horizontal">
{Object.keys(DashStyle).map((dashStyle) => ( {Object.values(DashStyle).map((dashStyle) => (
<ToolButton <DropdownMenu.Item key={dashStyle} onSelect={preventEvent} asChild>
key={dashStyle} <ToolButton
variant="icon" variant="icon"
isActive={dash === dashStyle} isActive={dash === dashStyle}
onSelect={() => tlstate.style({ dash: dashStyle as DashStyle })} onClick={() => tlstate.style({ dash: dashStyle as DashStyle })}
> >
{dashes[dashStyle as DashStyle]} {dashes[dashStyle as DashStyle]}
</ToolButton> </ToolButton>
</DropdownMenu.Item>
))} ))}
</DMContent> </DMContent>
</DropdownMenu.Root> </DropdownMenu.Root>

View file

@ -128,6 +128,8 @@ export const StyledDialogOverlay = styled(Dialog.Overlay, {
right: 0, right: 0,
bottom: 0, bottom: 0,
left: 0, left: 0,
width: '100%',
height: '100%',
}) })
function DialogAction({ onSelect, ...rest }: RowButtonProps) { function DialogAction({ onSelect, ...rest }: RowButtonProps) {

View file

@ -2,7 +2,7 @@ import * as React from 'react'
import * as DropdownMenu from '@radix-ui/react-dropdown-menu' import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
import { Data, SizeStyle } from '~types' import { Data, SizeStyle } from '~types'
import { useTLDrawContext } from '~hooks' import { useTLDrawContext } from '~hooks'
import { DMContent, DMTriggerIcon } from '~components/DropdownMenu' import { DMContent, DMItem, DMTriggerIcon } from '~components/DropdownMenu'
import { ToolButton } from '~components/ToolButton' import { ToolButton } from '~components/ToolButton'
import { SizeSmallIcon, SizeMediumIcon, SizeLargeIcon } from '~components/icons' import { SizeSmallIcon, SizeMediumIcon, SizeLargeIcon } from '~components/icons'
@ -14,6 +14,8 @@ const sizes = {
const selectSize = (s: Data) => s.appState.selectedStyle.size const selectSize = (s: Data) => s.appState.selectedStyle.size
const preventEvent = (e: Event) => e.preventDefault()
export const SizeMenu = React.memo((): JSX.Element => { export const SizeMenu = React.memo((): JSX.Element => {
const { tlstate, useSelector } = useTLDrawContext() const { tlstate, useSelector } = useTLDrawContext()
@ -23,14 +25,16 @@ export const SizeMenu = React.memo((): JSX.Element => {
<DropdownMenu.Root dir="ltr"> <DropdownMenu.Root dir="ltr">
<DMTriggerIcon>{sizes[size as SizeStyle]}</DMTriggerIcon> <DMTriggerIcon>{sizes[size as SizeStyle]}</DMTriggerIcon>
<DMContent variant="horizontal"> <DMContent variant="horizontal">
{Object.keys(SizeStyle).map((sizeStyle: string) => ( {Object.values(SizeStyle).map((sizeStyle: string) => (
<ToolButton <DropdownMenu.Item key={sizeStyle} onSelect={preventEvent} asChild>
key={sizeStyle} <ToolButton
isActive={size === sizeStyle} isActive={size === sizeStyle}
onSelect={() => tlstate.style({ size: sizeStyle as SizeStyle })} variant="icon"
> onClick={() => tlstate.style({ size: sizeStyle as SizeStyle })}
{sizes[sizeStyle as SizeStyle]} >
</ToolButton> {sizes[sizeStyle as SizeStyle]}
</ToolButton>
</DropdownMenu.Item>
))} ))}
</DMContent> </DMContent>
</DropdownMenu.Root> </DropdownMenu.Root>

View file

@ -5,7 +5,7 @@ Object {
"bend": 0, "bend": 0,
"childIndex": 1, "childIndex": 1,
"decorations": Object { "decorations": Object {
"end": "Arrow", "end": "arrow",
}, },
"handles": Object { "handles": Object {
"bend": Object { "bend": Object {
@ -44,10 +44,10 @@ Object {
], ],
"rotation": 0, "rotation": 0,
"style": Object { "style": Object {
"color": "Black", "color": "black",
"dash": "Draw", "dash": "draw",
"isFilled": false, "isFilled": false,
"size": "Small", "size": "small",
}, },
"type": "arrow", "type": "arrow",
} }

View file

@ -14,10 +14,10 @@ Object {
"points": Array [], "points": Array [],
"rotation": 0, "rotation": 0,
"style": Object { "style": Object {
"color": "Black", "color": "black",
"dash": "Draw", "dash": "draw",
"isFilled": false, "isFilled": false,
"size": "Small", "size": "small",
}, },
"type": "draw", "type": "draw",
} }

View file

@ -16,10 +16,10 @@ Object {
], ],
"rotation": 0, "rotation": 0,
"style": Object { "style": Object {
"color": "Black", "color": "black",
"dash": "Draw", "dash": "draw",
"isFilled": false, "isFilled": false,
"size": "Small", "size": "small",
}, },
"type": "ellipse", "type": "ellipse",
} }

View file

@ -17,10 +17,10 @@ Object {
100, 100,
], ],
"style": Object { "style": Object {
"color": "Black", "color": "black",
"dash": "Draw", "dash": "draw",
"isFilled": false, "isFilled": false,
"size": "Small", "size": "small",
}, },
"type": "group", "type": "group",
} }

View file

@ -16,10 +16,10 @@ Object {
1, 1,
], ],
"style": Object { "style": Object {
"color": "Black", "color": "black",
"dash": "Draw", "dash": "draw",
"isFilled": false, "isFilled": false,
"size": "Small", "size": "small",
}, },
"type": "rectangle", "type": "rectangle",
} }

View file

@ -12,10 +12,10 @@ Object {
], ],
"rotation": 0, "rotation": 0,
"style": Object { "style": Object {
"color": "Black", "color": "black",
"dash": "Draw", "dash": "draw",
"isFilled": false, "isFilled": false,
"size": "Small", "size": "small",
}, },
"text": " ", "text": " ",
"type": "text", "type": "text",

View file

@ -33,10 +33,10 @@ Array [
200, 200,
], ],
"style": Object { "style": Object {
"color": "Black", "color": "black",
"dash": "Draw", "dash": "draw",
"isFilled": false, "isFilled": false,
"size": "Small", "size": "small",
}, },
"type": "rectangle", "type": "rectangle",
}, },
@ -93,10 +93,10 @@ Array [
200, 200,
], ],
"style": Object { "style": Object {
"color": "Black", "color": "black",
"dash": "Draw", "dash": "draw",
"isFilled": false, "isFilled": false,
"size": "Small", "size": "small",
}, },
"type": "rectangle", "type": "rectangle",
}, },

View file

@ -0,0 +1,53 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { Decoration, TLDrawDocument, TLDrawShapeType } from '~types'
export function migrate(document: TLDrawDocument, newVersion: number): TLDrawDocument {
const { version = 0 } = document
if (version === newVersion) return document
if (version <= 12) {
Object.values(document.pages).forEach((page) => {
Object.values(page.bindings).forEach((binding) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Object.assign(binding, (binding as any).meta)
})
Object.values(page.shapes).forEach((shape) => {
Object.entries(shape.style).forEach(([id, style]) => {
if (typeof style === 'string') {
// @ts-ignore
shape.style[id] = style.toLowerCase()
}
})
if (shape.type === TLDrawShapeType.Arrow) {
if (shape.decorations) {
Object.entries(shape.decorations).forEach(([id, decoration]) => {
if ((decoration as unknown) === 'Arrow') {
shape.decorations = {
...shape.decorations,
[id]: Decoration.Arrow,
}
}
})
}
}
})
})
}
Object.values(document.pageStates).forEach((pageState) => {
pageState.selectedIds = pageState.selectedIds.filter((id) => {
return document.pages[pageState.id].shapes[id] !== undefined
})
pageState.bindingId = undefined
pageState.editingId = undefined
pageState.hoveredId = undefined
pageState.pointedId = undefined
})
document.version = newVersion
return document
}

View file

@ -47,6 +47,7 @@ import { sample } from './utils'
import { createTools, ToolType } from './tool' import { createTools, ToolType } from './tool'
import type { BaseTool } from './tool/BaseTool' import type { BaseTool } from './tool/BaseTool'
import { USER_COLORS, FIT_TO_SCREEN_PADDING } from '~constants' import { USER_COLORS, FIT_TO_SCREEN_PADDING } from '~constants'
import { migrate } from './migrate'
const uuid = Utils.uniqueId() const uuid = Utils.uniqueId()
@ -100,21 +101,15 @@ export class TLDrawState extends StateManager<Data> {
onUserChange?: (tlstate: TLDrawState, user: TLDrawUser) => void onUserChange?: (tlstate: TLDrawState, user: TLDrawUser) => void
) { ) {
super(TLDrawState.defaultState, id, TLDrawState.version, (prev, next) => { super(TLDrawState.defaultState, id, TLDrawState.version, (prev, next) => {
Object.values(prev.document.pages).forEach((page) => {
Object.values(page.bindings).forEach((binding) => {
if ('meta' in binding) {
// @ts-ignore
Object.assign(binding, binding.meta)
}
})
})
return { return {
...next, ...next,
document: { ...next.document, ...prev.document }, document: { ...next.document, ...prev.document },
} }
}) })
this.loadDocument(this.document)
this.patchState({ document: migrate(this.document, TLDrawState.version) })
this._onChange = onChange this._onChange = onChange
this._onMount = onMount this._onMount = onMount
this._onUserChange = onUserChange this._onUserChange = onUserChange
@ -130,6 +125,7 @@ export class TLDrawState extends StateManager<Data> {
appState: { appState: {
status: TLDrawStatus.Idle, status: TLDrawStatus.Idle,
}, },
document: migrate(this.document, TLDrawState.version),
}) })
} catch (e) { } catch (e) {
console.error('The data appears to be corrupted. Resetting!', e) console.error('The data appears to be corrupted. Resetting!', e)
@ -641,7 +637,7 @@ export class TLDrawState extends StateManager<Data> {
...this.appState, ...this.appState,
currentPageId: Object.keys(document.pages)[0], currentPageId: Object.keys(document.pages)[0],
}, },
document, document: migrate(document, TLDrawState.version),
}) })
return this return this
} }
@ -709,7 +705,7 @@ export class TLDrawState extends StateManager<Data> {
...this.state, ...this.state,
appState: nextAppState, appState: nextAppState,
document: { document: {
...document, ...migrate(document, TLDrawState.version),
pageStates: currentPageStates, pageStates: currentPageStates,
}, },
}, },
@ -803,7 +799,7 @@ export class TLDrawState extends StateManager<Data> {
return this.replaceState( return this.replaceState(
{ {
...TLDrawState.defaultState, ...TLDrawState.defaultState,
document, document: migrate(document, TLDrawState.version),
appState: { appState: {
...TLDrawState.defaultState.appState, ...TLDrawState.defaultState.appState,
currentPageId: Object.keys(document.pages)[0], currentPageId: Object.keys(document.pages)[0],
@ -1068,8 +1064,6 @@ export class TLDrawState extends StateManager<Data> {
Utils.deepClone(this.getShape(id, this.currentPageId)) Utils.deepClone(this.getShape(id, this.currentPageId))
) )
console.log(copyingShapes.length)
if (copyingShapes.length === 0) return this if (copyingShapes.length === 0) return this
const copyingBindings: TLDrawBinding[] = Object.values(this.page.bindings).filter( const copyingBindings: TLDrawBinding[] = Object.values(this.page.bindings).filter(
@ -2463,10 +2457,11 @@ export class TLDrawState extends StateManager<Data> {
} }
} }
static version = 11 static version = 12.5
static defaultDocument: TLDrawDocument = { static defaultDocument: TLDrawDocument = {
id: 'doc', id: 'doc',
version: 12.4,
pages: { pages: {
page: { page: {
id: 'page', id: 'page',

View file

@ -46,8 +46,6 @@ export abstract class BaseTool<T extends string = any> {
} }
onCancel = () => { onCancel = () => {
console.log('cancelling')
if (this.status === Status.Idle) { if (this.status === Status.Idle) {
this.state.selectTool('select') this.state.selectTool('select')
} else { } else {

View file

@ -11,8 +11,6 @@ export class TextTool extends BaseTool {
stopEditingShape = () => { stopEditingShape = () => {
this.setStatus(Status.Idle) this.setStatus(Status.Idle)
console.log(this.state.appState.isToolLocked)
if (!this.state.appState.isToolLocked) { if (!this.state.appState.isToolLocked) {
this.state.selectTool('select') this.state.selectTool('select')
} }

View file

@ -5,14 +5,14 @@ import oldDoc from './old-doc'
describe('When migrating bindings', () => { describe('When migrating bindings', () => {
it('migrates', () => { it('migrates', () => {
Object.values((oldDoc as unknown as TLDrawDocument).pages).forEach((page) => { // Object.values((oldDoc as unknown as TLDrawDocument).pages).forEach((page) => {
Object.values(page.bindings).forEach((binding) => { // Object.values(page.bindings).forEach((binding) => {
if ('meta' in binding) { // if ('meta' in binding) {
// @ts-ignore // // @ts-ignore
Object.assign(binding, binding.meta) // Object.assign(binding, binding.meta)
} // }
}) // })
}) // })
new TLDrawState().loadDocument(oldDoc as unknown as TLDrawDocument) new TLDrawState().loadDocument(oldDoc as unknown as TLDrawDocument)
}) })

View file

@ -1,6 +1,7 @@
import { TLDrawDocument, ColorStyle, DashStyle, SizeStyle, TLDrawShapeType } from '~types' import { TLDrawDocument, ColorStyle, DashStyle, SizeStyle, TLDrawShapeType } from '~types'
export const mockDocument: TLDrawDocument = { export const mockDocument: TLDrawDocument = {
version: 0,
id: 'doc', id: 'doc',
pages: { pages: {
page1: { page1: {

View file

@ -9,7 +9,6 @@ import type {
TLHandle, TLHandle,
TLBounds, TLBounds,
TLSnapLine, TLSnapLine,
TLComponentProps,
} from '@tldraw/core' } from '@tldraw/core'
import type { TLPage, TLUser, TLPageState } from '@tldraw/core' import type { TLPage, TLUser, TLPageState } from '@tldraw/core'
import type { StoreApi } from 'zustand' import type { StoreApi } from 'zustand'
@ -28,12 +27,6 @@ export interface TLDrawTransformInfo<T extends TLShape> {
transformOrigin: number[] transformOrigin: number[]
} }
export type TLDrawComponentProps<T extends TLDrawShape, E extends Element = any> = TLComponentProps<
T,
E,
TLDrawMeta
>
// old // old
export type TLStore = StoreApi<Data> export type TLStore = StoreApi<Data>
@ -45,6 +38,7 @@ export interface TLDrawDocument {
id: string id: string
pages: Record<string, TLDrawPage> pages: Record<string, TLDrawPage>
pageStates: Record<string, TLPageState> pageStates: Record<string, TLPageState>
version: number
} }
export interface TLDrawSettings { export interface TLDrawSettings {
@ -229,7 +223,7 @@ export enum TLDrawShapeType {
} }
export enum Decoration { export enum Decoration {
Arrow = 'Arrow', Arrow = 'arrow',
} }
export interface TLDrawBaseShape extends TLShape { export interface TLDrawBaseShape extends TLShape {
@ -304,38 +298,38 @@ export interface ArrowBinding extends TLBinding {
export type TLDrawBinding = ArrowBinding export type TLDrawBinding = ArrowBinding
export enum ColorStyle { export enum ColorStyle {
White = 'White', White = 'white',
LightGray = 'LightGray', LightGray = 'lightGray',
Gray = 'Gray', Gray = 'gray',
Black = 'Black', Black = 'black',
Green = 'Green', Green = 'green',
Cyan = 'Cyan', Cyan = 'cyan',
Blue = 'Blue', Blue = 'blue',
Indigo = 'Indigo', Indigo = 'indigo',
Violet = 'Violet', Violet = 'violet',
Red = 'Red', Red = 'red',
Orange = 'Orange', Orange = 'orange',
Yellow = 'Yellow', Yellow = 'yellow',
} }
export enum SizeStyle { export enum SizeStyle {
Small = 'Small', Small = 'small',
Medium = 'Medium', Medium = 'medium',
Large = 'Large', Large = 'large',
} }
export enum DashStyle { export enum DashStyle {
Draw = 'Draw', Draw = 'draw',
Solid = 'Solid', Solid = 'solid',
Dashed = 'Dashed', Dashed = 'dashed',
Dotted = 'Dotted', Dotted = 'dotted',
} }
export enum FontSize { export enum FontSize {
Small = 'Small', Small = 'small',
Medium = 'Medium', Medium = 'medium',
Large = 'Large', Large = 'large',
ExtraLarge = 'ExtraLarge', ExtraLarge = 'extraLarge',
} }
export type ShapeStyles = { export type ShapeStyles = {

11
www/.babelrc Normal file
View file

@ -0,0 +1,11 @@
{
"presets": [
[
"next/babel",
{
"preset-env": { "targets": { "node": true } }
}
]
],
"plugins": []
}

View file

@ -37,23 +37,7 @@ function Editor({ id }: { id: string }) {
uuid: docId, uuid: docId,
document: { document: {
id: 'test-room', id: 'test-room',
pages: { ...TLDrawState.defaultDocument,
page: {
id: 'page',
shapes: {},
bindings: {},
},
},
pageStates: {
page: {
id: 'page',
selectedIds: [],
camera: {
point: [0, 0],
zoom: 1,
},
},
},
}, },
}) })
@ -156,7 +140,11 @@ function Editor({ id }: { id: string }) {
doc.subscribe(handleDocumentUpdates) doc.subscribe(handleDocumentUpdates)
// Load the shared document // Load the shared document
tlstate.loadDocument(doc.toObject().document) const newDocument = doc.toObject().document
if (newDocument) {
tlstate.loadDocument(newDocument)
}
return () => { return () => {
window.removeEventListener('beforeunload', handleExit) window.removeEventListener('beforeunload', handleExit)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,7 +1,7 @@
{ {
"compilerOptions": { "compilerOptions": {
"composite": true, "composite": true,
"target": "es5", "target": "es6",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,

View file

@ -9920,7 +9920,7 @@ raw-body@2.4.1:
iconv-lite "0.4.24" iconv-lite "0.4.24"
unpipe "1.0.0" unpipe "1.0.0"
react-dom@17.0.2, "react-dom@^16.8 || ^17.0": react-dom@17.0.2:
version "17.0.2" version "17.0.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
@ -10008,7 +10008,7 @@ react-style-singleton@^2.1.0:
invariant "^2.2.4" invariant "^2.2.4"
tslib "^1.0.0" tslib "^1.0.0"
react@17.0.2, react@>=16.8: react@17.0.2:
version "17.0.2" version "17.0.2"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
@ -10445,10 +10445,10 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0" hash-base "^3.0.0"
inherits "^2.0.1" inherits "^2.0.1"
rko@^0.5.25: rko@^0.6.0:
version "0.5.25" version "0.6.0"
resolved "https://registry.yarnpkg.com/rko/-/rko-0.5.25.tgz#1095803900e3f912f6adf8a1c113b8227d3d88bf" resolved "https://registry.yarnpkg.com/rko/-/rko-0.6.0.tgz#fa640384b4e82fdcd90fc58c958256148c4eb10c"
integrity sha512-HU6M3PxK3VEqrr6QZKAsqO98juQX24kEgJkKSdFJhw8U/DBUGAnU/fgyxNIaTw7TCI7vjIy/RzBEXf5I4sijKg== integrity sha512-u05SAiyz02Sw+QyGaQb3NGPXf3xXxQ9AwNG+tItHx2MpAsPEEH84NqYDyG9jem/ji/FPQPQHuRKcy2MHb1a1Ow==
dependencies: dependencies:
idb-keyval "^5.1.3" idb-keyval "^5.1.3"
zustand "^3.5.9" zustand "^3.5.9"