diff --git a/components/canvas/canvas.tsx b/components/canvas/canvas.tsx
index 3e7e30290..12d74c24f 100644
--- a/components/canvas/canvas.tsx
+++ b/components/canvas/canvas.tsx
@@ -1,5 +1,6 @@
import styled from 'styles'
-import { useSelector } from 'state'
+import { ErrorBoundary } from 'react-error-boundary'
+import state, { useSelector } from 'state'
import React, { useRef } from 'react'
import useZoomEvents from 'hooks/useZoomEvents'
import useCamera from 'hooks/useCamera'
@@ -27,16 +28,23 @@ export default function Canvas(): JSX.Element {
return (
-
- {isReady && (
-
-
-
-
-
-
-
- )}
+ {
+ // reset the state of your app so the error doesn't happen again
+ }}
+ >
+
+ {isReady && (
+
+
+
+
+
+
+
+ )}
+
)
@@ -59,3 +67,20 @@ const MainSVG = styled('svg', {
userSelect: 'none',
},
})
+
+function ErrorFallback({ error, resetErrorBoundary }) {
+ React.useEffect(() => {
+ console.error(error)
+ const copy = 'Sorry, something went wrong. Clear canvas and continue?'
+ if (window.confirm(copy)) {
+ state.send('CLEARED_PAGE')
+ resetErrorBoundary()
+ }
+ }, [])
+
+ return (
+
+ Oops
+
+ )
+}
diff --git a/components/code-panel/types-import.ts b/components/code-panel/types-import.ts
index 3ba44582e..9d9c525cb 100644
--- a/components/code-panel/types-import.ts
+++ b/components/code-panel/types-import.ts
@@ -199,7 +199,7 @@ interface GroupShape extends BaseShape {
size: number[]
}
-type ShapeProps = Partial & {
+type ShapeProps = Partial> & {
style?: Partial
}
@@ -608,57 +608,235 @@ interface ShapeUtility {
return { ...this._shape }
}
+ /**
+ * Destroy the shape.
+ */
destroy(): void {
codeShapes.delete(this)
}
+ /**
+ * Move the shape to a point.
+ * @param delta
+ */
moveTo(point: number[]): CodeShape {
- this.utils.setProperty(this._shape, 'point', point)
+ return this.translateTo(point)
+ }
+
+ /**
+ * Move the shape to a point.
+ * @param delta
+ */
+ translateTo(point: number[]): CodeShape {
+ this.utils.translateTo(this._shape, point)
return this
}
- translate(delta: number[]): CodeShape {
- this.utils.setProperty(
- this._shape,
- 'point',
- vec.add(this._shape.point, delta)
- )
+ /**
+ * Move the shape by a delta.
+ * @param delta
+ */
+ translateBy(delta: number[]): CodeShape {
+ this.utils.translateTo(this._shape, delta)
return this
}
- rotate(rotation: number): CodeShape {
- this.utils.setProperty(this._shape, 'rotation', rotation)
+ /**
+ * Rotate the shape.
+ */
+ rotateTo(rotation: number): CodeShape {
+ this.utils.rotateTo(this._shape, rotation, this.shape.rotation - rotation)
return this
}
+ /**
+ * Rotate the shape by a delta.
+ */
+ rotateBy(rotation: number): CodeShape {
+ this.utils.rotateBy(this._shape, rotation)
+ return this
+ }
+
+ /**
+ * Get the shape's bounding box.
+ */
getBounds(): CodeShape {
this.utils.getBounds(this.shape)
return this
}
+ /**
+ * Test whether a point is inside of the shape.
+ */
hitTest(point: number[]): CodeShape {
this.utils.hitTest(this.shape, point)
return this
}
+ /**
+ * Move the shape to the back of the painting order.
+ */
+ moveToBack(): CodeShape {
+ const sorted = getOrderedShapes()
+
+ if (sorted.length <= 1) return
+
+ const first = sorted[0].childIndex
+ sorted.forEach((shape) => shape.childIndex++)
+ this.childIndex = first
+
+ codeShapes.clear()
+ sorted.forEach((shape) => codeShapes.add(shape))
+
+ return this
+ }
+
+ /**
+ * Move the shape to the top of the painting order.
+ */
+ moveToFront(): CodeShape {
+ const sorted = getOrderedShapes()
+
+ if (sorted.length <= 1) return
+
+ const ahead = sorted.slice(sorted.indexOf(this))
+ const last = ahead[ahead.length - 1].childIndex
+ ahead.forEach((shape) => shape.childIndex--)
+ this.childIndex = last
+
+ codeShapes.clear()
+ sorted.forEach((shape) => codeShapes.add(shape))
+
+ return this
+ }
+
+ /**
+ * Move the shape backward in the painting order.
+ */
+ moveBackward(): CodeShape {
+ const sorted = getOrderedShapes()
+
+ if (sorted.length <= 1) return
+
+ const next = sorted[sorted.indexOf(this) - 1]
+
+ if (!next) return
+
+ const index = next.childIndex
+ next.childIndex = this.childIndex
+ this.childIndex = index
+
+ codeShapes.clear()
+ sorted.forEach((shape) => codeShapes.add(shape))
+
+ return this
+ }
+
+ /**
+ * Move the shape forward in the painting order.
+ */
+ moveForward(): CodeShape {
+ const sorted = getOrderedShapes()
+
+ if (sorted.length <= 1) return
+
+ const next = sorted[sorted.indexOf(this) + 1]
+
+ if (!next) return
+
+ const index = next.childIndex
+ next.childIndex = this.childIndex
+ this.childIndex = index
+
+ codeShapes.clear()
+ sorted.forEach((shape) => codeShapes.add(shape))
+
+ return this
+ }
+
+ /**
+ * The shape's underlying shape.
+ */
get shape(): T {
return this._shape
}
+ /**
+ * The shape's current point.
+ */
get point(): number[] {
return [...this.shape.point]
}
+ set point(point: number[]) {
+ getShapeUtils(this.shape).translateTo(this._shape, point)
+ }
+
+ /**
+ * The shape's rotation.
+ */
get rotation(): number {
return this.shape.rotation
}
+
+ set rotation(rotation: number) {
+ getShapeUtils(this.shape).rotateTo(
+ this._shape,
+ rotation,
+ rotation - this.shape.rotation
+ )
+ }
+
+ /**
+ * The shape's color style.
+ */
+ get color(): ColorStyle {
+ return this.shape.style.color
+ }
+
+ set color(color: ColorStyle) {
+ getShapeUtils(this.shape).applyStyles(this._shape, { color })
+ }
+
+ /**
+ * The shape's dash style.
+ */
+ get dash(): DashStyle {
+ return this.shape.style.dash
+ }
+
+ set dash(dash: DashStyle) {
+ getShapeUtils(this.shape).applyStyles(this._shape, { dash })
+ }
+
+ /**
+ * The shape's stroke width.
+ */
+ get strokeWidth(): SizeStyle {
+ return this.shape.style.size
+ }
+
+ set strokeWidth(size: SizeStyle) {
+ getShapeUtils(this.shape).applyStyles(this._shape, { size })
+ }
+
+ /**
+ * The shape's index in the painting order.
+ */
+ get childIndex(): number {
+ return this.shape.childIndex
+ }
+
+ set childIndex(childIndex: number) {
+ getShapeUtils(this.shape).setProperty(this._shape, 'childIndex', childIndex)
+ }
}
/**
* ## Dot
*/
class Dot extends CodeShape {
- constructor(props = {} as Partial & Partial) {
+ constructor(props = {} as ShapeProps) {
super({
id: uniqueId(),
seed: Math.random(),
@@ -686,7 +864,7 @@ interface ShapeUtility {
* ## Ellipse
*/
class Ellipse extends CodeShape {
- constructor(props = {} as Partial & Partial) {
+ constructor(props = {} as ShapeProps) {
super({
id: uniqueId(),
seed: Math.random(),
@@ -720,7 +898,7 @@ interface ShapeUtility {
* ## Line
*/
class Line extends CodeShape {
- constructor(props = {} as Partial & Partial) {
+ constructor(props = {} as ShapeProps) {
super({
id: uniqueId(),
seed: Math.random(),
@@ -753,7 +931,7 @@ interface ShapeUtility {
* ## Polyline
*/
class Polyline extends CodeShape {
- constructor(props = {} as Partial & Partial) {
+ constructor(props = {} as ShapeProps) {
super({
id: uniqueId(),
seed: Math.random(),
@@ -782,7 +960,7 @@ interface ShapeUtility {
* ## Ray
*/
class Ray extends CodeShape {
- constructor(props = {} as Partial & Partial) {
+ constructor(props = {} as ShapeProps) {
super({
id: uniqueId(),
seed: Math.random(),
@@ -846,8 +1024,7 @@ interface ShapeUtility {
*/
class Arrow extends CodeShape {
constructor(
- props = {} as Partial &
- Partial & { start?: number[]; end?: number[] }
+ props = {} as ShapeProps & { start: number[]; end: number[] }
) {
const { start = [0, 0], end = [0, 0] } = props
@@ -940,7 +1117,7 @@ interface ShapeUtility {
* ## Draw
*/
class Draw extends CodeShape {
- constructor(props = {} as Partial) {
+ constructor(props = {} as ShapeProps) {
super({
id: uniqueId(),
seed: Math.random(),
@@ -1459,12 +1636,24 @@ interface ShapeUtility {
return -c / 2 + -step
}
- static getPointsBetween(a: number[], b: number[], steps = 6): number[][] {
+ /**
+ * Get an array of points between two points.
+ * @param a
+ * @param b
+ * @param options
+ */
+ static getPointsBetween(
+ a: number[],
+ b: number[],
+ options = {} as {
+ steps?: number
+ ease?: (t: number) => number
+ }
+ ): number[][] {
+ const { steps = 6, ease = (t) => t * t * t } = options
+
return Array.from(Array(steps))
- .map((_, i) => {
- const t = i / steps
- return t * t * t
- })
+ .map((_, i) => ease(i / steps))
.map((t) => [...vec.lrp(a, b, t), (1 - t) / 2])
}
diff --git a/package.json b/package.json
index c2c47b641..b61b2ef80 100644
--- a/package.json
+++ b/package.json
@@ -59,6 +59,7 @@
"perfect-freehand": "^0.4.9",
"react": "^17.0.2",
"react-dom": "^17.0.2",
+ "react-error-boundary": "^3.1.3",
"react-feather": "^2.0.9",
"react-use-gesture": "^9.1.3",
"sucrase": "^3.19.0",
diff --git a/state/code/arrow.ts b/state/code/arrow.ts
index 8345db485..684dedee8 100644
--- a/state/code/arrow.ts
+++ b/state/code/arrow.ts
@@ -1,6 +1,6 @@
import CodeShape from './index'
import { uniqueId } from 'utils'
-import { ArrowShape, Decoration, ShapeStyles, ShapeType } from 'types'
+import { ArrowShape, Decoration, ShapeProps, ShapeType } from 'types'
import { defaultStyle } from 'state/shape-styles'
import { getShapeUtils } from 'state/shape-utils'
import Vec from 'utils/vec'
@@ -10,8 +10,7 @@ import Vec from 'utils/vec'
*/
export default class Arrow extends CodeShape {
constructor(
- props = {} as Partial &
- Partial & { start?: number[]; end?: number[] }
+ props = {} as ShapeProps & { start: number[]; end: number[] }
) {
const { start = [0, 0], end = [0, 0] } = props
diff --git a/state/code/dot.ts b/state/code/dot.ts
index c7395a8f2..2cef9af7f 100644
--- a/state/code/dot.ts
+++ b/state/code/dot.ts
@@ -1,13 +1,13 @@
import CodeShape from './index'
import { uniqueId } from 'utils'
-import { DotShape, ShapeStyles, ShapeType } from 'types'
+import { DotShape, ShapeProps, ShapeType } from 'types'
import { defaultStyle } from 'state/shape-styles'
/**
* ## Dot
*/
export default class Dot extends CodeShape {
- constructor(props = {} as Partial & Partial) {
+ constructor(props = {} as ShapeProps) {
super({
id: uniqueId(),
seed: Math.random(),
diff --git a/state/code/draw.ts b/state/code/draw.ts
index 7b00a59a6..0c400e78f 100644
--- a/state/code/draw.ts
+++ b/state/code/draw.ts
@@ -1,13 +1,13 @@
import CodeShape from './index'
import { uniqueId } from 'utils'
-import { DrawShape, ShapeType } from 'types'
+import { DrawShape, ShapeProps, ShapeType } from 'types'
import { defaultStyle } from 'state/shape-styles'
/**
* ## Draw
*/
export default class Draw extends CodeShape {
- constructor(props = {} as Partial) {
+ constructor(props = {} as ShapeProps) {
super({
id: uniqueId(),
seed: Math.random(),
diff --git a/state/code/ellipse.ts b/state/code/ellipse.ts
index 4774b7fce..79e6fe2ba 100644
--- a/state/code/ellipse.ts
+++ b/state/code/ellipse.ts
@@ -1,13 +1,13 @@
import CodeShape from './index'
import { uniqueId } from 'utils'
-import { EllipseShape, ShapeStyles, ShapeType } from 'types'
+import { EllipseShape, ShapeProps, ShapeType } from 'types'
import { defaultStyle } from 'state/shape-styles'
/**
* ## Ellipse
*/
export default class Ellipse extends CodeShape {
- constructor(props = {} as Partial & Partial) {
+ constructor(props = {} as ShapeProps) {
super({
id: uniqueId(),
seed: Math.random(),
diff --git a/state/code/generate.ts b/state/code/generate.ts
index f934fb5a6..501e50427 100644
--- a/state/code/generate.ts
+++ b/state/code/generate.ts
@@ -10,8 +10,15 @@ import Utils from './utils'
import Vec from 'utils/vec'
import { NumberControl, VectorControl, codeControls, controls } from './control'
import { codeShapes } from './index'
-import { CodeControl, Data, Shape } from 'types'
-import { getPage } from 'utils'
+import {
+ CodeControl,
+ Data,
+ Shape,
+ DashStyle,
+ ColorStyle,
+ SizeStyle,
+} from 'types'
+import { getPage, getShapes } from 'utils'
import { transform } from 'sucrase'
const baseScope = {
@@ -27,6 +34,9 @@ const baseScope = {
Draw,
VectorControl,
NumberControl,
+ DashStyle,
+ ColorStyle,
+ SizeStyle,
}
/**
@@ -53,11 +63,19 @@ export function generateFromCode(
new Function(...Object.keys(scope), `${transformed}`)(...Object.values(scope))
- const generatedShapes = Array.from(codeShapes.values()).map((instance) => ({
- ...instance.shape,
- isGenerated: true,
- parentId: getPage(data).id,
- }))
+ const startingChildIndex =
+ getShapes(data)
+ .filter((shape) => shape.parentId === data.currentPageId)
+ .sort((a, b) => a.childIndex - b.childIndex)[0]?.childIndex || 1
+
+ const generatedShapes = Array.from(codeShapes.values())
+ .sort((a, b) => a.shape.childIndex - b.shape.childIndex)
+ .map((instance, i) => ({
+ ...instance.shape,
+ isGenerated: true,
+ parentId: getPage(data).id,
+ childIndex: startingChildIndex + i,
+ }))
const generatedControls = Array.from(codeControls.values())
diff --git a/state/code/index.ts b/state/code/index.ts
index a5ba8bc3e..59e5dcfb0 100644
--- a/state/code/index.ts
+++ b/state/code/index.ts
@@ -1,9 +1,22 @@
-import { Mutable, Shape, ShapeUtility } from 'types'
+import {
+ ColorStyle,
+ DashStyle,
+ Mutable,
+ Shape,
+ ShapeUtility,
+ SizeStyle,
+} from 'types'
import { createShape, getShapeUtils } from 'state/shape-utils'
-import vec from 'utils/vec'
+import { setToArray } from 'utils'
export const codeShapes = new Set>([])
+function getOrderedShapes() {
+ return setToArray(codeShapes).sort(
+ (a, b) => a.shape.childIndex - b.shape.childIndex
+ )
+}
+
/**
* A base class for code shapes. Note that creating a shape adds it to the
* shape map, while deleting it removes it from the collected shapes set
@@ -22,48 +35,226 @@ export default class CodeShape {
return { ...this._shape }
}
+ /**
+ * Destroy the shape.
+ */
destroy(): void {
codeShapes.delete(this)
}
+ /**
+ * Move the shape to a point.
+ * @param delta
+ */
moveTo(point: number[]): CodeShape {
- this.utils.setProperty(this._shape, 'point', point)
+ return this.translateTo(point)
+ }
+
+ /**
+ * Move the shape to a point.
+ * @param delta
+ */
+ translateTo(point: number[]): CodeShape {
+ this.utils.translateTo(this._shape, point)
return this
}
- translate(delta: number[]): CodeShape {
- this.utils.setProperty(
- this._shape,
- 'point',
- vec.add(this._shape.point, delta)
- )
+ /**
+ * Move the shape by a delta.
+ * @param delta
+ */
+ translateBy(delta: number[]): CodeShape {
+ this.utils.translateTo(this._shape, delta)
return this
}
- rotate(rotation: number): CodeShape {
- this.utils.setProperty(this._shape, 'rotation', rotation)
+ /**
+ * Rotate the shape.
+ */
+ rotateTo(rotation: number): CodeShape {
+ this.utils.rotateTo(this._shape, rotation, this.shape.rotation - rotation)
return this
}
+ /**
+ * Rotate the shape by a delta.
+ */
+ rotateBy(rotation: number): CodeShape {
+ this.utils.rotateBy(this._shape, rotation)
+ return this
+ }
+
+ /**
+ * Get the shape's bounding box.
+ */
getBounds(): CodeShape {
this.utils.getBounds(this.shape)
return this
}
+ /**
+ * Test whether a point is inside of the shape.
+ */
hitTest(point: number[]): CodeShape {
this.utils.hitTest(this.shape, point)
return this
}
+ /**
+ * Move the shape to the back of the painting order.
+ */
+ moveToBack(): CodeShape {
+ const sorted = getOrderedShapes()
+
+ if (sorted.length <= 1) return
+
+ const first = sorted[0].childIndex
+ sorted.forEach((shape) => shape.childIndex++)
+ this.childIndex = first
+
+ codeShapes.clear()
+ sorted.forEach((shape) => codeShapes.add(shape))
+
+ return this
+ }
+
+ /**
+ * Move the shape to the top of the painting order.
+ */
+ moveToFront(): CodeShape {
+ const sorted = getOrderedShapes()
+
+ if (sorted.length <= 1) return
+
+ const ahead = sorted.slice(sorted.indexOf(this))
+ const last = ahead[ahead.length - 1].childIndex
+ ahead.forEach((shape) => shape.childIndex--)
+ this.childIndex = last
+
+ codeShapes.clear()
+ sorted.forEach((shape) => codeShapes.add(shape))
+
+ return this
+ }
+
+ /**
+ * Move the shape backward in the painting order.
+ */
+ moveBackward(): CodeShape {
+ const sorted = getOrderedShapes()
+
+ if (sorted.length <= 1) return
+
+ const next = sorted[sorted.indexOf(this) - 1]
+
+ if (!next) return
+
+ const index = next.childIndex
+ next.childIndex = this.childIndex
+ this.childIndex = index
+
+ codeShapes.clear()
+ sorted.forEach((shape) => codeShapes.add(shape))
+
+ return this
+ }
+
+ /**
+ * Move the shape forward in the painting order.
+ */
+ moveForward(): CodeShape {
+ const sorted = getOrderedShapes()
+
+ if (sorted.length <= 1) return
+
+ const next = sorted[sorted.indexOf(this) + 1]
+
+ if (!next) return
+
+ const index = next.childIndex
+ next.childIndex = this.childIndex
+ this.childIndex = index
+
+ codeShapes.clear()
+ sorted.forEach((shape) => codeShapes.add(shape))
+
+ return this
+ }
+
+ /**
+ * The shape's underlying shape.
+ */
get shape(): T {
return this._shape
}
+ /**
+ * The shape's current point.
+ */
get point(): number[] {
return [...this.shape.point]
}
+ set point(point: number[]) {
+ getShapeUtils(this.shape).translateTo(this._shape, point)
+ }
+
+ /**
+ * The shape's rotation.
+ */
get rotation(): number {
return this.shape.rotation
}
+
+ set rotation(rotation: number) {
+ getShapeUtils(this.shape).rotateTo(
+ this._shape,
+ rotation,
+ rotation - this.shape.rotation
+ )
+ }
+
+ /**
+ * The shape's color style.
+ */
+ get color(): ColorStyle {
+ return this.shape.style.color
+ }
+
+ set color(color: ColorStyle) {
+ getShapeUtils(this.shape).applyStyles(this._shape, { color })
+ }
+
+ /**
+ * The shape's dash style.
+ */
+ get dash(): DashStyle {
+ return this.shape.style.dash
+ }
+
+ set dash(dash: DashStyle) {
+ getShapeUtils(this.shape).applyStyles(this._shape, { dash })
+ }
+
+ /**
+ * The shape's stroke width.
+ */
+ get strokeWidth(): SizeStyle {
+ return this.shape.style.size
+ }
+
+ set strokeWidth(size: SizeStyle) {
+ getShapeUtils(this.shape).applyStyles(this._shape, { size })
+ }
+
+ /**
+ * The shape's index in the painting order.
+ */
+ get childIndex(): number {
+ return this.shape.childIndex
+ }
+
+ set childIndex(childIndex: number) {
+ getShapeUtils(this.shape).setProperty(this._shape, 'childIndex', childIndex)
+ }
}
diff --git a/state/code/line.ts b/state/code/line.ts
index c53e6ee19..a973c0162 100644
--- a/state/code/line.ts
+++ b/state/code/line.ts
@@ -1,13 +1,13 @@
import CodeShape from './index'
import { uniqueId } from 'utils'
-import { LineShape, ShapeStyles, ShapeType } from 'types'
+import { LineShape, ShapeProps, ShapeType } from 'types'
import { defaultStyle } from 'state/shape-styles'
/**
* ## Line
*/
export default class Line extends CodeShape {
- constructor(props = {} as Partial & Partial) {
+ constructor(props = {} as ShapeProps) {
super({
id: uniqueId(),
seed: Math.random(),
diff --git a/state/code/polyline.ts b/state/code/polyline.ts
index 0e7d9c883..cac0f3be8 100644
--- a/state/code/polyline.ts
+++ b/state/code/polyline.ts
@@ -1,13 +1,13 @@
import CodeShape from './index'
import { uniqueId } from 'utils'
-import { PolylineShape, ShapeStyles, ShapeType } from 'types'
+import { PolylineShape, ShapeProps, ShapeType } from 'types'
import { defaultStyle } from 'state/shape-styles'
/**
* ## Polyline
*/
export default class Polyline extends CodeShape {
- constructor(props = {} as Partial & Partial) {
+ constructor(props = {} as ShapeProps) {
super({
id: uniqueId(),
seed: Math.random(),
diff --git a/state/code/ray.ts b/state/code/ray.ts
index 1f561230d..5d655fccc 100644
--- a/state/code/ray.ts
+++ b/state/code/ray.ts
@@ -1,13 +1,13 @@
import CodeShape from './index'
import { uniqueId } from 'utils'
-import { RayShape, ShapeStyles, ShapeType } from 'types'
+import { RayShape, ShapeProps, ShapeType } from 'types'
import { defaultStyle } from 'state/shape-styles'
/**
* ## Ray
*/
export default class Ray extends CodeShape {
- constructor(props = {} as Partial & Partial) {
+ constructor(props = {} as ShapeProps) {
super({
id: uniqueId(),
seed: Math.random(),
diff --git a/state/code/utils.ts b/state/code/utils.ts
index e62385788..8e550c9e2 100644
--- a/state/code/utils.ts
+++ b/state/code/utils.ts
@@ -496,12 +496,24 @@ export default class Utils {
return -c / 2 + -step
}
- static getPointsBetween(a: number[], b: number[], steps = 6): number[][] {
+ /**
+ * Get an array of points between two points.
+ * @param a
+ * @param b
+ * @param options
+ */
+ static getPointsBetween(
+ a: number[],
+ b: number[],
+ options = {} as {
+ steps?: number
+ ease?: (t: number) => number
+ }
+ ): number[][] {
+ const { steps = 6, ease = (t) => t * t * t } = options
+
return Array.from(Array(steps))
- .map((_, i) => {
- const t = i / steps
- return t * t * t
- })
+ .map((_, i) => ease(i / steps))
.map((t) => [...vec.lrp(a, b, t), (1 - t) / 2])
}
diff --git a/state/storage.ts b/state/storage.ts
index 87eeaa1e3..57d25a31f 100644
--- a/state/storage.ts
+++ b/state/storage.ts
@@ -335,15 +335,15 @@ class Storage {
}
)
+ const documentName = data.document.name
+
const fa = await import('browser-fs-access')
fa.fileSave(
blob,
{
fileName: `${
- saveAs
- ? data.document.name
- : this.previousSaveHandle?.name || 'My Document'
+ saveAs ? documentName : this.previousSaveHandle?.name || 'My Document'
}.tldr`,
description: 'tldraw file',
extensions: ['.tldr'],
diff --git a/types.ts b/types.ts
index 49ca10354..921cc1655 100644
--- a/types.ts
+++ b/types.ts
@@ -191,7 +191,7 @@ export interface GroupShape extends BaseShape {
size: number[]
}
-export type ShapeProps = Partial & {
+export type ShapeProps = Partial> & {
style?: Partial
}
diff --git a/yarn.lock b/yarn.lock
index cb713e58e..892cf70cf 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6592,6 +6592,13 @@ react-dom@^17.0.2:
object-assign "^4.1.1"
scheduler "^0.20.2"
+react-error-boundary@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.3.tgz#276bfa05de8ac17b863587c9e0647522c25e2a0b"
+ integrity sha512-A+F9HHy9fvt9t8SNDlonq01prnU8AmkjvGKV4kk8seB9kU3xMEO8J/PQlLVmoOIDODl5U2kufSBs4vrWIqhsAA==
+ dependencies:
+ "@babel/runtime" "^7.12.5"
+
react-feather@^2.0.9:
version "2.0.9"
resolved "https://registry.yarnpkg.com/react-feather/-/react-feather-2.0.9.tgz#6e42072130d2fa9a09d4476b0e61b0ed17814480"