Improves file saving / page saving and loading

This commit is contained in:
Steve Ruiz 2021-06-16 13:09:45 +01:00
parent 7e03adcd52
commit 4ce2b8cc6b
68 changed files with 1166 additions and 997 deletions

View file

@ -4,7 +4,7 @@ import { useRef } from 'react'
import { useSelector } from 'state' import { useSelector } from 'state'
import styled from 'styles' import styled from 'styles'
import { deepCompareArrays, getPage } from 'utils/utils' import { deepCompareArrays, getPage } from 'utils/utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
export default function Handles() { export default function Handles() {
const selectedIds = useSelector( const selectedIds = useSelector(

View file

@ -2,7 +2,6 @@ import styled from 'styles'
import { useSelector } from 'state' import { useSelector } from 'state'
import { import {
deepCompareArrays, deepCompareArrays,
getBoundsCenter,
getPage, getPage,
getSelectedIds, getSelectedIds,
setToArray, setToArray,
@ -11,7 +10,7 @@ import { getShapeUtils } from 'lib/shape-utils'
import useShapeEvents from 'hooks/useShapeEvents' import useShapeEvents from 'hooks/useShapeEvents'
import { memo, useRef } from 'react' import { memo, useRef } from 'react'
import { ShapeType } from 'types' import { ShapeType } from 'types'
import * as vec from 'utils/vec' import vec from 'utils/vec'
export default function Selected() { export default function Selected() {
const currentSelectedShapeIds = useSelector( const currentSelectedShapeIds = useSelector(
@ -69,6 +68,7 @@ const SelectIndicator = styled('path', {
strokeLinejoin: 'round', strokeLinejoin: 'round',
stroke: '$selected', stroke: '$selected',
pointerEvents: 'none', pointerEvents: 'none',
fill: 'none',
variants: { variants: {
isLocked: { isLocked: {

View file

@ -5,7 +5,7 @@ import { getShapeUtils } from 'lib/shape-utils'
import { getBoundsCenter, getPage } from 'utils/utils' import { getBoundsCenter, getPage } from 'utils/utils'
import { ShapeStyles, ShapeType } from 'types' import { ShapeStyles, ShapeType } from 'types'
import useShapeEvents from 'hooks/useShapeEvents' import useShapeEvents from 'hooks/useShapeEvents'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import { getShapeStyle } from 'lib/shape-styles' import { getShapeStyle } from 'lib/shape-styles'
import ContextMenu from 'components/context-menu' import ContextMenu from 'components/context-menu'
@ -30,7 +30,6 @@ function Shape({ id, isSelecting, parentPoint }: ShapeProps) {
// detects the change and pulls this component. // detects the change and pulls this component.
if (!shape) return null if (!shape) return null
const utils = getShapeUtils(shape)
const style = getShapeStyle(shape.style) const style = getShapeStyle(shape.style)
const shapeUtils = getShapeUtils(shape) const shapeUtils = getShapeUtils(shape)
const { isShy, isParent, isForeignObject } = shapeUtils const { isShy, isParent, isForeignObject } = shapeUtils

View file

@ -1,7 +1,7 @@
import * as _ContextMenu from '@radix-ui/react-context-menu' import * as _ContextMenu from '@radix-ui/react-context-menu'
import * as _Dropdown from '@radix-ui/react-dropdown-menu' import * as _Dropdown from '@radix-ui/react-dropdown-menu'
import styled from 'styles' import styled from 'styles'
import { RowButton } from './shared' import { IconWrapper, RowButton } from './shared'
import { import {
commandKey, commandKey,
deepCompareArrays, deepCompareArrays,
@ -11,6 +11,7 @@ import {
import state, { useSelector } from 'state' import state, { useSelector } from 'state'
import { MoveType, ShapeType } from 'types' import { MoveType, ShapeType } from 'types'
import React, { useRef } from 'react' import React, { useRef } from 'react'
import { ChevronRightIcon } from '@radix-ui/react-icons'
export default function ContextMenu({ export default function ContextMenu({
children, children,
@ -56,65 +57,9 @@ export default function ContextMenu({
</kbd> </kbd>
</Button> </Button>
<StyledDivider /> <StyledDivider />
<Button
onSelect={() =>
state.send('MOVED', {
type: MoveType.ToFront,
})
}
>
<span>Move To Front</span>
<kbd>
<span>{commandKey()}</span>
<span></span>
<span>]</span>
</kbd>
</Button>
<Button
onSelect={() =>
state.send('MOVED', {
type: MoveType.Forward,
})
}
>
<span>Move Forward</span>
<kbd>
<span>{commandKey()}</span>
<span>]</span>
</kbd>
</Button>
<Button
onSelect={() =>
state.send('MOVED', {
type: MoveType.Backward,
})
}
>
<span>Move Backward</span>
<kbd>
<span>{commandKey()}</span>
<span>[</span>
</kbd>
</Button>
<Button
onSelect={() =>
state.send('MOVED', {
type: MoveType.ToBack,
})
}
>
<span>Move to Back</span>
<kbd>
<span>{commandKey()}</span>
<span></span>
<span>[</span>
</kbd>
</Button>
{hasGroupSelectd || {hasGroupSelectd ||
(hasMultipleSelected && ( (hasMultipleSelected && (
<> <>
<StyledDivider />
{hasGroupSelectd && ( {hasGroupSelectd && (
<Button onSelect={() => state.send('UNGROUPED')}> <Button onSelect={() => state.send('UNGROUPED')}>
<span>Ungroup</span> <span>Ungroup</span>
@ -136,11 +81,65 @@ export default function ContextMenu({
)} )}
</> </>
))} ))}
<StyledDivider /> <SubMenu label="Move">
<Button
onSelect={() =>
state.send('MOVED', {
type: MoveType.ToFront,
})
}
>
<span>To Front</span>
<kbd>
<span>{commandKey()}</span>
<span></span>
<span>]</span>
</kbd>
</Button>
{/* <Button onSelect={() => state.send('MOVED_TO_PAGE')}> */} <Button
<MoveToPageDropDown>Move to Page</MoveToPageDropDown> onSelect={() =>
{/* </Button> */} state.send('MOVED', {
type: MoveType.Forward,
})
}
>
<span>Forward</span>
<kbd>
<span>{commandKey()}</span>
<span>]</span>
</kbd>
</Button>
<Button
onSelect={() =>
state.send('MOVED', {
type: MoveType.Backward,
})
}
>
<span>Backward</span>
<kbd>
<span>{commandKey()}</span>
<span>[</span>
</kbd>
</Button>
<Button
onSelect={() =>
state.send('MOVED', {
type: MoveType.ToBack,
})
}
>
<span>To Back</span>
<kbd>
<span>{commandKey()}</span>
<span></span>
<span>[</span>
</kbd>
</Button>
</SubMenu>
<MoveToPageMenu />
<StyledDivider />
<Button onSelect={() => state.send('DELETED')}> <Button onSelect={() => state.send('DELETED')}>
<span>Delete</span> <span>Delete</span>
<kbd> <kbd>
@ -180,8 +179,7 @@ const StyledContent = styled(_ContextMenu.Content, {
pointerEvents: 'all', pointerEvents: 'all',
userSelect: 'none', userSelect: 'none',
zIndex: 200, zIndex: 200,
padding: 2, padding: 3,
border: '1px solid $panel',
boxShadow: '0px 2px 4px rgba(0,0,0,.2)', boxShadow: '0px 2px 4px rgba(0,0,0,.2)',
minWidth: 128, minWidth: 128,
@ -210,7 +208,7 @@ const StyledContent = styled(_ContextMenu.Content, {
const StyledDivider = styled(_ContextMenu.Separator, { const StyledDivider = styled(_ContextMenu.Separator, {
backgroundColor: '$hover', backgroundColor: '$hover',
height: 1, height: 1,
margin: '2px -2px', margin: '3px -3px',
}) })
function Button({ function Button({
@ -234,7 +232,33 @@ function Button({
) )
} }
function MoveToPageDropDown({ children }: { children: React.ReactNode }) { function SubMenu({
children,
label,
}: {
label: string
children: React.ReactNode
}) {
return (
<_ContextMenu.Root>
<_ContextMenu.TriggerItem
as={RowButton}
bp={{ '@initial': 'mobile', '@sm': 'small' }}
>
<span>{label}</span>
<IconWrapper size="small">
<ChevronRightIcon />
</IconWrapper>
</_ContextMenu.TriggerItem>
<StyledContent sideOffset={2} alignOffset={-2} isMobile={isMobile()}>
{children}
<StyledArrow offset={13} />
</StyledContent>
</_ContextMenu.Root>
)
}
function MoveToPageMenu() {
const documentPages = useSelector((s) => s.data.document.pages) const documentPages = useSelector((s) => s.data.document.pages)
const currentPageId = useSelector((s) => s.data.currentPageId) const currentPageId = useSelector((s) => s.data.currentPageId)
@ -245,27 +269,29 @@ function MoveToPageDropDown({ children }: { children: React.ReactNode }) {
.filter((a) => a.id !== currentPageId) .filter((a) => a.id !== currentPageId)
return ( return (
<_Dropdown.Root> <_ContextMenu.Root>
<_Dropdown.Trigger <_ContextMenu.TriggerItem
as={RowButton} as={RowButton}
bp={{ '@initial': 'mobile', '@sm': 'small' }} bp={{ '@initial': 'mobile', '@sm': 'small' }}
> >
{children} <span>Move To Page</span>
</_Dropdown.Trigger> <IconWrapper size="small">
<StyledDialogContent side="right" sideOffset={8}> <ChevronRightIcon />
</IconWrapper>
</_ContextMenu.TriggerItem>
<StyledContent sideOffset={2} alignOffset={-2} isMobile={isMobile()}>
{sorted.map(({ id, name }) => ( {sorted.map(({ id, name }) => (
<_Dropdown.Item <Button
as={RowButton}
key={id} key={id}
bp={{ '@initial': 'mobile', '@sm': 'small' }}
disabled={id === currentPageId} disabled={id === currentPageId}
onSelect={() => state.send('MOVED_TO_PAGE', { id })} onSelect={() => state.send('MOVED_TO_PAGE', { id })}
> >
<span>{name}</span> <span>{name}</span>
</_Dropdown.Item> </Button>
))} ))}
</StyledDialogContent> <StyledArrow offset={13} />
</_Dropdown.Root> </StyledContent>
</_ContextMenu.Root>
) )
} }
@ -293,3 +319,7 @@ const StyledDialogContent = styled(_Dropdown.Content, {
outline: 'none', outline: 'none',
}, },
}) })
const StyledArrow = styled(_ContextMenu.Arrow, {
fill: 'white',
})

View file

@ -3,7 +3,6 @@ import { strokes } from 'lib/shape-styles'
import { ColorStyle } from 'types' import { ColorStyle } from 'types'
import * as DropdownMenu from '@radix-ui/react-dropdown-menu' import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
import { Square } from 'react-feather' import { Square } from 'react-feather'
import styled from 'styles'
import { DropdownContent } from '../shared' import { DropdownContent } from '../shared'
export default function ColorContent({ export default function ColorContent({

View file

@ -8,12 +8,9 @@ import {
import * as RadioGroup from '@radix-ui/react-radio-group' import * as RadioGroup from '@radix-ui/react-radio-group'
import { DashStyle } from 'types' import { DashStyle } from 'types'
import state from 'state' import state from 'state'
import { ChangeEvent } from 'react'
function handleChange(e: ChangeEvent<HTMLInputElement>) { function handleChange(dash: string) {
state.send('CHANGED_STYLE', { state.send('CHANGED_STYLE', { dash })
dash: e.currentTarget.value,
})
} }
interface Props { interface Props {

View file

@ -6,7 +6,7 @@ import { IconWrapper, RowButton } from '../shared'
interface Props { interface Props {
isFilled: boolean isFilled: boolean
onChange: (isFilled: boolean) => void onChange: (isFilled: boolean | string) => void
} }
export default function IsFilledPicker({ isFilled, onChange }: Props) { export default function IsFilledPicker({ isFilled, onChange }: Props) {
@ -15,9 +15,7 @@ export default function IsFilledPicker({ isFilled, onChange }: Props) {
as={RowButton} as={RowButton}
bp={{ '@initial': 'mobile', '@sm': 'small' }} bp={{ '@initial': 'mobile', '@sm': 'small' }}
checked={isFilled} checked={isFilled}
onCheckedChange={(e: React.ChangeEvent<HTMLInputElement>) => onCheckedChange={onChange}
onChange(e.currentTarget.checked)
}
> >
<label htmlFor="fill">Fill</label> <label htmlFor="fill">Fill</label>
<IconWrapper> <IconWrapper>

View file

@ -1,14 +1,11 @@
import { Group, Item } from '../shared' import { Group, Item } from '../shared'
import * as RadioGroup from '@radix-ui/react-radio-group' import * as RadioGroup from '@radix-ui/react-radio-group'
import { ChangeEvent } from 'react'
import { Circle } from 'react-feather' import { Circle } from 'react-feather'
import state from 'state' import state from 'state'
import { SizeStyle } from 'types' import { SizeStyle } from 'types'
function handleChange(e: ChangeEvent<HTMLInputElement>) { function handleChange(size: string) {
state.send('CHANGED_STYLE', { state.send('CHANGED_STYLE', { size })
size: e.currentTarget.value as SizeStyle,
})
} }
export default function SizePicker({ size }: { size: SizeStyle }) { export default function SizePicker({ size }: { size: SizeStyle }) {

View file

@ -1,4 +1,5 @@
import { useCallback } from 'react' import { useCallback } from 'react'
import { fastTransform } from 'state/hacks'
import inputs from 'state/inputs' import inputs from 'state/inputs'
import { Edge, Corner } from 'types' import { Edge, Corner } from 'types'
@ -29,7 +30,15 @@ export default function useBoundsEvents(handle: Edge | Corner | 'rotate') {
if (e.buttons !== 1) return if (e.buttons !== 1) return
if (!inputs.canAccept(e.pointerId)) return if (!inputs.canAccept(e.pointerId)) return
e.stopPropagation() e.stopPropagation()
state.send('MOVED_POINTER', inputs.pointerMove(e))
const info = inputs.pointerMove(e)
if (state.isIn('transformingSelection')) {
fastTransform(info)
return
}
state.send('MOVED_POINTER', info)
}, },
[handle] [handle]
) )

View file

@ -1,5 +1,6 @@
import React, { useEffect } from 'react' import React, { useEffect } from 'react'
import state from 'state' import state from 'state'
import storage from 'state/storage'
import { getCurrentCamera } from 'utils/utils' import { getCurrentCamera } from 'utils/utils'
/** /**
@ -23,10 +24,7 @@ export default function useCamera(ref: React.MutableRefObject<SVGGElement>) {
`scale(${zoom}) translate(${point[0]} ${point[1]})` `scale(${zoom}) translate(${point[0]} ${point[1]})`
) )
localStorage.setItem( storage.savePageState(state.data)
'code_slate_camera',
JSON.stringify({ point, zoom })
)
prev = getCurrentCamera(state.data) prev = getCurrentCamera(state.data)
} }

View file

@ -1,6 +1,11 @@
import { MutableRefObject, useCallback } from 'react' import { MutableRefObject, useCallback } from 'react'
import state from 'state' import state from 'state'
import { fastBrushSelect, fastDrawUpdate, fastTranslate } from 'state/hacks' import {
fastBrushSelect,
fastDrawUpdate,
fastTransform,
fastTranslate,
} from 'state/hacks'
import inputs from 'state/inputs' import inputs from 'state/inputs'
import { isMobile } from 'utils/utils' import { isMobile } from 'utils/utils'
@ -47,6 +52,11 @@ export default function useCanvasEvents(
return return
} }
if (state.isIn('transformingSelection')) {
fastTransform(info)
return
}
state.send('MOVED_POINTER', info) state.send('MOVED_POINTER', info)
}, []) }, [])

View file

@ -1,7 +1,7 @@
import React, { useEffect, useRef } from 'react' import React, { useEffect, useRef } from 'react'
import state from 'state' import state from 'state'
import inputs from 'state/inputs' import inputs from 'state/inputs'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import { useGesture } from 'react-use-gesture' import { useGesture } from 'react-use-gesture'
import { import {
fastBrushSelect, fastBrushSelect,

View file

@ -1,5 +1,5 @@
import CodeShape from './index' import CodeShape from './index'
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import { CircleShape, ShapeType } from 'types' import { CircleShape, ShapeType } from 'types'
import { vectorToPoint } from 'utils/utils' import { vectorToPoint } from 'utils/utils'
import { defaultStyle } from 'lib/shape-styles' import { defaultStyle } from 'lib/shape-styles'
@ -9,7 +9,7 @@ export default class Circle extends CodeShape<CircleShape> {
props.point = vectorToPoint(props.point) props.point = vectorToPoint(props.point)
super({ super({
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
parentId: (window as any).currentPageId, parentId: (window as any).currentPageId,
type: ShapeType.Circle, type: ShapeType.Circle,

View file

@ -3,8 +3,8 @@ import {
ControlType, ControlType,
NumberCodeControl, NumberCodeControl,
VectorCodeControl, VectorCodeControl,
} from "types" } from 'types'
import { v4 as uuid } from "uuid" import { v4 as uuid } from 'uuid'
export const controls: Record<string, any> = {} export const controls: Record<string, any> = {}
@ -13,8 +13,8 @@ export const codeControls = new Set<CodeControl>([])
export class Control<T extends CodeControl> { export class Control<T extends CodeControl> {
control: T control: T
constructor(control: Omit<T, "id">) { constructor(control: Omit<T, 'id'>) {
this.control = { ...control, id: uuid() } as T this.control = { ...control, id: uniqueId() } as T
codeControls.add(this.control) codeControls.add(this.control)
// Could there be a better way to prevent this? // Could there be a better way to prevent this?
@ -32,7 +32,7 @@ export class Control<T extends CodeControl> {
} }
export class NumberControl extends Control<NumberCodeControl> { export class NumberControl extends Control<NumberCodeControl> {
constructor(options: Omit<NumberCodeControl, "id" | "type">) { constructor(options: Omit<NumberCodeControl, 'id' | 'type'>) {
const { value = 0, step = 1 } = options const { value = 0, step = 1 } = options
super({ super({
type: ControlType.Number, type: ControlType.Number,
@ -44,7 +44,7 @@ export class NumberControl extends Control<NumberCodeControl> {
} }
export class VectorControl extends Control<VectorCodeControl> { export class VectorControl extends Control<VectorCodeControl> {
constructor(options: Omit<VectorCodeControl, "id" | "type">) { constructor(options: Omit<VectorCodeControl, 'id' | 'type'>) {
const { value = [0, 0], isNormalized = false } = options const { value = [0, 0], isNormalized = false } = options
super({ super({
type: ControlType.Vector, type: ControlType.Vector,

View file

@ -1,5 +1,5 @@
import CodeShape from './index' import CodeShape from './index'
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import { DotShape, ShapeType } from 'types' import { DotShape, ShapeType } from 'types'
import { vectorToPoint } from 'utils/utils' import { vectorToPoint } from 'utils/utils'
import { defaultStyle } from 'lib/shape-styles' import { defaultStyle } from 'lib/shape-styles'
@ -9,7 +9,7 @@ export default class Dot extends CodeShape<DotShape> {
props.point = vectorToPoint(props.point) props.point = vectorToPoint(props.point)
super({ super({
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
parentId: (window as any).currentPageId, parentId: (window as any).currentPageId,
type: ShapeType.Dot, type: ShapeType.Dot,

View file

@ -1,5 +1,5 @@
import CodeShape from './index' import CodeShape from './index'
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import { EllipseShape, ShapeType } from 'types' import { EllipseShape, ShapeType } from 'types'
import { vectorToPoint } from 'utils/utils' import { vectorToPoint } from 'utils/utils'
import { defaultStyle } from 'lib/shape-styles' import { defaultStyle } from 'lib/shape-styles'
@ -9,7 +9,7 @@ export default class Ellipse extends CodeShape<EllipseShape> {
props.point = vectorToPoint(props.point) props.point = vectorToPoint(props.point)
super({ super({
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
parentId: (window as any).currentPageId, parentId: (window as any).currentPageId,
type: ShapeType.Ellipse, type: ShapeType.Ellipse,

View file

@ -4,7 +4,7 @@ import shapeUtilityMap, {
getShapeUtils, getShapeUtils,
ShapeUtility, ShapeUtility,
} from 'lib/shape-utils' } from 'lib/shape-utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import Vector from './vector' import Vector from './vector'
import { vectorToPoint } from 'utils/utils' import { vectorToPoint } from 'utils/utils'

View file

@ -1,5 +1,5 @@
import CodeShape from './index' import CodeShape from './index'
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import { LineShape, ShapeType } from 'types' import { LineShape, ShapeType } from 'types'
import { vectorToPoint } from 'utils/utils' import { vectorToPoint } from 'utils/utils'
import { defaultStyle } from 'lib/shape-styles' import { defaultStyle } from 'lib/shape-styles'
@ -10,7 +10,7 @@ export default class Line extends CodeShape<LineShape> {
props.direction = vectorToPoint(props.direction) props.direction = vectorToPoint(props.direction)
super({ super({
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
parentId: (window as any).currentPageId, parentId: (window as any).currentPageId,
type: ShapeType.Line, type: ShapeType.Line,

View file

@ -1,5 +1,5 @@
import CodeShape from './index' import CodeShape from './index'
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import { PolylineShape, ShapeType } from 'types' import { PolylineShape, ShapeType } from 'types'
import { vectorToPoint } from 'utils/utils' import { vectorToPoint } from 'utils/utils'
import { defaultStyle } from 'lib/shape-styles' import { defaultStyle } from 'lib/shape-styles'
@ -10,7 +10,7 @@ export default class Polyline extends CodeShape<PolylineShape> {
props.points = props.points.map(vectorToPoint) props.points = props.points.map(vectorToPoint)
super({ super({
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
parentId: (window as any).currentPageId, parentId: (window as any).currentPageId,
type: ShapeType.Polyline, type: ShapeType.Polyline,

View file

@ -1,5 +1,5 @@
import CodeShape from './index' import CodeShape from './index'
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import { RayShape, ShapeType } from 'types' import { RayShape, ShapeType } from 'types'
import { vectorToPoint } from 'utils/utils' import { vectorToPoint } from 'utils/utils'
import { defaultStyle } from 'lib/shape-styles' import { defaultStyle } from 'lib/shape-styles'
@ -10,7 +10,7 @@ export default class Ray extends CodeShape<RayShape> {
props.direction = vectorToPoint(props.direction) props.direction = vectorToPoint(props.direction)
super({ super({
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
type: ShapeType.Ray, type: ShapeType.Ray,
isGenerated: true, isGenerated: true,

View file

@ -1,5 +1,5 @@
import CodeShape from './index' import CodeShape from './index'
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import { RectangleShape, ShapeType } from 'types' import { RectangleShape, ShapeType } from 'types'
import { vectorToPoint } from 'utils/utils' import { vectorToPoint } from 'utils/utils'
import { defaultStyle } from 'lib/shape-styles' import { defaultStyle } from 'lib/shape-styles'
@ -10,7 +10,7 @@ export default class Rectangle extends CodeShape<RectangleShape> {
props.size = vectorToPoint(props.size) props.size = vectorToPoint(props.size)
super({ super({
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
parentId: (window as any).currentPageId, parentId: (window as any).currentPageId,
type: ShapeType.Rectangle, type: ShapeType.Rectangle,

View file

@ -1,5 +1,5 @@
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import { import {
ease, ease,
getSvgPathFromStroke, getSvgPathFromStroke,
@ -65,7 +65,7 @@ const arrow = registerShapeUtils<ArrowShape>({
} = props } = props
return { return {
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
type: ShapeType.Arrow, type: ShapeType.Arrow,
isGenerated: false, isGenerated: false,

View file

@ -1,5 +1,5 @@
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import { CircleShape, ColorStyle, DashStyle, ShapeType, SizeStyle } from 'types' import { CircleShape, ColorStyle, DashStyle, ShapeType, SizeStyle } from 'types'
import { registerShapeUtils } from './index' import { registerShapeUtils } from './index'
import { boundsContained } from 'utils/bounds' import { boundsContained } from 'utils/bounds'
@ -13,7 +13,7 @@ const circle = registerShapeUtils<CircleShape>({
create(props) { create(props) {
return { return {
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
type: ShapeType.Circle, type: ShapeType.Circle,
isGenerated: false, isGenerated: false,

View file

@ -1,5 +1,5 @@
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import { DotShape, ShapeType } from 'types' import { DotShape, ShapeType } from 'types'
import { registerShapeUtils } from './index' import { registerShapeUtils } from './index'
import { boundsContained } from 'utils/bounds' import { boundsContained } from 'utils/bounds'
@ -12,7 +12,7 @@ const dot = registerShapeUtils<DotShape>({
create(props) { create(props) {
return { return {
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
type: ShapeType.Dot, type: ShapeType.Dot,
isGenerated: false, isGenerated: false,

View file

@ -1,5 +1,5 @@
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import { DashStyle, DrawShape, ShapeStyles, ShapeType } from 'types' import { DashStyle, DrawShape, ShapeStyles, ShapeType } from 'types'
import { registerShapeUtils } from './index' import { registerShapeUtils } from './index'
import { intersectPolylineBounds } from 'utils/intersections' import { intersectPolylineBounds } from 'utils/intersections'
@ -23,7 +23,7 @@ const draw = registerShapeUtils<DrawShape>({
create(props) { create(props) {
return { return {
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
type: ShapeType.Draw, type: ShapeType.Draw,
isGenerated: false, isGenerated: false,

View file

@ -1,5 +1,5 @@
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import { EllipseShape, ShapeType } from 'types' import { EllipseShape, ShapeType } from 'types'
import { getShapeUtils, registerShapeUtils } from './index' import { getShapeUtils, registerShapeUtils } from './index'
import { boundsContained, getRotatedEllipseBounds } from 'utils/bounds' import { boundsContained, getRotatedEllipseBounds } from 'utils/bounds'
@ -16,7 +16,7 @@ const ellipse = registerShapeUtils<EllipseShape>({
create(props) { create(props) {
return { return {
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
type: ShapeType.Ellipse, type: ShapeType.Ellipse,
isGenerated: false, isGenerated: false,

View file

@ -1,5 +1,5 @@
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import { import {
GroupShape, GroupShape,
RectangleShape, RectangleShape,
@ -27,7 +27,7 @@ const group = registerShapeUtils<GroupShape>({
create(props) { create(props) {
return { return {
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
type: ShapeType.Group, type: ShapeType.Group,
isGenerated: false, isGenerated: false,

View file

@ -9,7 +9,7 @@ import {
Mutable, Mutable,
ShapeByType, ShapeByType,
} from 'types' } from 'types'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import { import {
getBoundsCenter, getBoundsCenter,
getBoundsFromPoints, getBoundsFromPoints,
@ -20,7 +20,7 @@ import {
boundsContainPolygon, boundsContainPolygon,
pointInBounds, pointInBounds,
} from 'utils/bounds' } from 'utils/bounds'
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import circle from './circle' import circle from './circle'
import dot from './dot' import dot from './dot'
import polyline from './polyline' import polyline from './polyline'
@ -231,7 +231,7 @@ function getDefaultShapeUtil<T extends Shape>(): ShapeUtility<T> {
create(props) { create(props) {
return { return {
id: uuid(), id: uniqueId(),
isGenerated: false, isGenerated: false,
point: [0, 0], point: [0, 0],
name: 'Shape', name: 'Shape',

View file

@ -1,5 +1,5 @@
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import { LineShape, ShapeType } from 'types' import { LineShape, ShapeType } from 'types'
import { registerShapeUtils } from './index' import { registerShapeUtils } from './index'
import { boundsContained } from 'utils/bounds' import { boundsContained } from 'utils/bounds'
@ -14,7 +14,7 @@ const line = registerShapeUtils<LineShape>({
create(props) { create(props) {
return { return {
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
type: ShapeType.Line, type: ShapeType.Line,
isGenerated: false, isGenerated: false,

View file

@ -1,5 +1,5 @@
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import { PolylineShape, ShapeType } from 'types' import { PolylineShape, ShapeType } from 'types'
import { registerShapeUtils } from './index' import { registerShapeUtils } from './index'
import { intersectPolylineBounds } from 'utils/intersections' import { intersectPolylineBounds } from 'utils/intersections'
@ -12,7 +12,7 @@ const polyline = registerShapeUtils<PolylineShape>({
create(props) { create(props) {
return { return {
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
type: ShapeType.Polyline, type: ShapeType.Polyline,
isGenerated: false, isGenerated: false,

View file

@ -1,5 +1,5 @@
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import { RayShape, ShapeType } from 'types' import { RayShape, ShapeType } from 'types'
import { registerShapeUtils } from './index' import { registerShapeUtils } from './index'
import { boundsContained } from 'utils/bounds' import { boundsContained } from 'utils/bounds'
@ -13,7 +13,7 @@ const ray = registerShapeUtils<RayShape>({
create(props) { create(props) {
return { return {
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
type: ShapeType.Ray, type: ShapeType.Ray,
isGenerated: false, isGenerated: false,

View file

@ -1,5 +1,5 @@
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import { RectangleShape, ShapeType } from 'types' import { RectangleShape, ShapeType } from 'types'
import { registerShapeUtils } from './index' import { registerShapeUtils } from './index'
import { import {
@ -19,7 +19,7 @@ const rectangle = registerShapeUtils<RectangleShape>({
create(props) { create(props) {
return { return {
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
type: ShapeType.Rectangle, type: ShapeType.Rectangle,
isGenerated: false, isGenerated: false,

View file

@ -1,5 +1,5 @@
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import { TextShape, ShapeType, FontSize } from 'types' import { TextShape, ShapeType, FontSize } from 'types'
import { registerShapeUtils } from './index' import { registerShapeUtils } from './index'
import { defaultStyle, getFontStyle, getShapeStyle } from 'lib/shape-styles' import { defaultStyle, getFontStyle, getShapeStyle } from 'lib/shape-styles'
@ -36,7 +36,7 @@ const text = registerShapeUtils<TextShape>({
create(props) { create(props) {
return { return {
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
type: ShapeType.Text, type: ShapeType.Text,
isGenerated: false, isGenerated: false,

View file

@ -9,22 +9,22 @@
}, },
"dependencies": { "dependencies": {
"@monaco-editor/react": "^4.1.3", "@monaco-editor/react": "^4.1.3",
"@radix-ui/react-checkbox": "^0.0.15", "@radix-ui/react-checkbox": "^0.0.16",
"@radix-ui/react-context-menu": "^0.0.19", "@radix-ui/react-context-menu": "^0.0.21",
"@radix-ui/react-dialog": "^0.0.17", "@radix-ui/react-dialog": "^0.0.18",
"@radix-ui/react-dropdown-menu": "^0.0.19", "@radix-ui/react-dropdown-menu": "^0.0.20",
"@radix-ui/react-icons": "^1.0.3", "@radix-ui/react-icons": "^1.0.3",
"@radix-ui/react-radio-group": "^0.0.16", "@radix-ui/react-radio-group": "^0.0.17",
"@radix-ui/react-tooltip": "^0.0.18", "@radix-ui/react-tooltip": "^0.0.19",
"@state-designer/react": "^1.7.3", "@state-designer/react": "^1.7.3",
"@stitches/react": "^0.1.9", "@stitches/react": "^0.2.1",
"browser-fs-access": "^0.17.3", "browser-fs-access": "^0.17.3",
"framer-motion": "^4.1.16", "framer-motion": "^4.1.16",
"idb-keyval": "^5.0.6", "idb-keyval": "^5.0.6",
"ismobilejs": "^1.1.1", "ismobilejs": "^1.1.1",
"next": "10.2.0", "next": "10.2.0",
"next-pwa": "^5.2.21", "next-pwa": "^5.2.21",
"perfect-freehand": "^0.4.8", "perfect-freehand": "^0.4.9",
"prettier": "^2.3.0", "prettier": "^2.3.0",
"react": "17.0.2", "react": "17.0.2",
"react-dom": "17.0.2", "react-dom": "17.0.2",

View file

@ -3,8 +3,8 @@ import history from '../history'
import { Data } from 'types' import { Data } from 'types'
import storage from 'state/storage' import storage from 'state/storage'
export default function changePage(data: Data, pageId: string) { export default function changePage(data: Data, toPageId: string) {
const { currentPageId: prevPageId } = data const { currentPageId: fromPageId } = data
history.execute( history.execute(
data, data,
@ -13,13 +13,13 @@ export default function changePage(data: Data, pageId: string) {
category: 'canvas', category: 'canvas',
manualSelection: true, manualSelection: true,
do(data) { do(data) {
storage.savePage(data, data.document.id, prevPageId) storage.savePage(data, data.document.id, fromPageId)
data.currentPageId = pageId storage.loadPage(data, data.document.id, toPageId)
storage.loadPage(data) data.currentPageId = toPageId
}, },
undo(data) { undo(data) {
data.currentPageId = prevPageId storage.loadPage(data, data.document.id, fromPageId)
storage.loadPage(data, prevPageId) data.currentPageId = fromPageId
}, },
}) })
) )

View file

@ -1,9 +1,8 @@
import Command from './command' import Command from './command'
import history from '../history' import history from '../history'
import { Data, Page, PageState } from 'types' import { Data, Page, PageState } from 'types'
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import { current } from 'immer' import { current } from 'immer'
import { getSelectedIds } from 'utils/utils'
import storage from 'state/storage' import storage from 'state/storage'
export default function createPage(data: Data) { export default function createPage(data: Data) {
@ -20,12 +19,14 @@ export default function createPage(data: Data) {
data.pageStates[page.id] = pageState data.pageStates[page.id] = pageState
data.currentPageId = page.id data.currentPageId = page.id
storage.savePage(data, data.document.id, page.id) storage.savePage(data, data.document.id, page.id)
storage.saveDocumentToLocalStorage(data)
}, },
undo(data) { undo(data) {
const { page, currentPageId } = snapshot const { page, currentPageId } = snapshot
delete data.document.pages[page.id] delete data.document.pages[page.id]
delete data.pageStates[page.id] delete data.pageStates[page.id]
data.currentPageId = currentPageId data.currentPageId = currentPageId
storage.saveDocumentToLocalStorage(data)
}, },
}) })
) )
@ -36,7 +37,7 @@ function getSnapshot(data: Data) {
const pages = Object.values(data.document.pages) const pages = Object.values(data.document.pages)
const unchanged = pages.filter((page) => page.name.startsWith('Page ')) const unchanged = pages.filter((page) => page.name.startsWith('Page '))
const id = uuid() const id = uniqueId()
const page: Page = { const page: Page = {
type: 'page', type: 'page',
@ -46,7 +47,8 @@ function getSnapshot(data: Data) {
shapes: {}, shapes: {},
} }
const pageState: PageState = { const pageState: PageState = {
selectedIds: new Set<string>(), id,
selectedIds: new Set([]),
camera: { camera: {
point: [0, 0], point: [0, 0],
zoom: 1, zoom: 1,

View file

@ -4,7 +4,7 @@ import { Data } from 'types'
import { current } from 'immer' import { current } from 'immer'
import { getPage, getSelectedShapes } from 'utils/utils' import { getPage, getSelectedShapes } from 'utils/utils'
import { getShapeUtils } from 'lib/shape-utils' import { getShapeUtils } from 'lib/shape-utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import storage from 'state/storage' import storage from 'state/storage'
export default function changePage(data: Data, pageId: string) { export default function changePage(data: Data, pageId: string) {

View file

@ -8,16 +8,16 @@ import {
getSelectedShapes, getSelectedShapes,
setSelectedIds, setSelectedIds,
} from 'utils/utils' } from 'utils/utils'
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import { current } from 'immer' import { current } from 'immer'
import * as vec from 'utils/vec' import vec from 'utils/vec'
export default function duplicateCommand(data: Data) { export default function duplicateCommand(data: Data) {
const { currentPageId } = data const { currentPageId } = data
const selectedShapes = getSelectedShapes(current(data)) const selectedShapes = getSelectedShapes(current(data))
const duplicates = selectedShapes.map((shape) => ({ const duplicates = selectedShapes.map((shape) => ({
...shape, ...shape,
id: uuid(), id: uniqueId(),
point: vec.add(shape.point, vec.div([16, 16], getCurrentCamera(data).zoom)), point: vec.add(shape.point, vec.div([16, 16], getCurrentCamera(data).zoom)),
})) }))

View file

@ -12,7 +12,7 @@ import {
import { current } from 'immer' import { current } from 'immer'
import { createShape, getShapeUtils } from 'lib/shape-utils' import { createShape, getShapeUtils } from 'lib/shape-utils'
import { PropsOfType } from 'types' import { PropsOfType } from 'types'
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import commands from '.' import commands from '.'
export default function groupCommand(data: Data) { export default function groupCommand(data: Data) {

View file

@ -4,7 +4,7 @@ import { Data } from 'types'
import { getPage } from 'utils/utils' import { getPage } from 'utils/utils'
import { HandleSnapshot } from 'state/sessions/handle-session' import { HandleSnapshot } from 'state/sessions/handle-session'
import { getShapeUtils } from 'lib/shape-utils' import { getShapeUtils } from 'lib/shape-utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
export default function handleCommand( export default function handleCommand(
data: Data, data: Data,

View file

@ -12,7 +12,7 @@ import {
uniqueArray, uniqueArray,
} from 'utils/utils' } from 'utils/utils'
import { getShapeUtils } from 'lib/shape-utils' import { getShapeUtils } from 'lib/shape-utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import storage from 'state/storage' import storage from 'state/storage'
export default function nudgeCommand(data: Data, newPageId: string) { export default function nudgeCommand(data: Data, newPageId: string) {

View file

@ -3,7 +3,7 @@ import history from '../history'
import { Data } from 'types' import { Data } from 'types'
import { getPage, getSelectedShapes } from 'utils/utils' import { getPage, getSelectedShapes } from 'utils/utils'
import { getShapeUtils } from 'lib/shape-utils' import { getShapeUtils } from 'lib/shape-utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
export default function nudgeCommand(data: Data, delta: number[]) { export default function nudgeCommand(data: Data, delta: number[]) {
const { currentPageId } = data const { currentPageId } = data

View file

@ -7,7 +7,7 @@ import {
getPage, getPage,
getSelectedShapes, getSelectedShapes,
} from 'utils/utils' } from 'utils/utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import { getShapeUtils } from 'lib/shape-utils' import { getShapeUtils } from 'lib/shape-utils'
const PI2 = Math.PI * 2 const PI2 = Math.PI * 2

View file

@ -9,7 +9,7 @@ import {
updateParents, updateParents,
} from 'utils/utils' } from 'utils/utils'
import { getShapeUtils } from 'lib/shape-utils' import { getShapeUtils } from 'lib/shape-utils'
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
export default function translateCommand( export default function translateCommand(
data: Data, data: Data,

View file

@ -11,7 +11,7 @@ import {
import { current } from 'immer' import { current } from 'immer'
import { createShape, getShapeUtils } from 'lib/shape-utils' import { createShape, getShapeUtils } from 'lib/shape-utils'
import { PropsOfType } from 'types' import { PropsOfType } from 'types'
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
export default function ungroupCommand(data: Data) { export default function ungroupCommand(data: Data) {
const cData = current(data) const cData = current(data)

View file

@ -7,9 +7,10 @@ import {
setToArray, setToArray,
setZoomCSS, setZoomCSS,
} from 'utils/utils' } from 'utils/utils'
import { freeze } from 'immer'
import session from './session' import session from './session'
import state from './state' import state from './state'
import * as vec from 'utils/vec' import vec from 'utils/vec'
/** /**
* While a user is drawing with the draw tool, we want to update the shape without * While a user is drawing with the draw tool, we want to update the shape without
@ -34,7 +35,7 @@ export function fastDrawUpdate(info: PointerInfo) {
data.document.pages[data.currentPageId].shapes[selectedId] = { ...shape } data.document.pages[data.currentPageId].shapes[selectedId] = { ...shape }
state.forceData(Object.freeze(data)) state.forceData(freeze(data))
} }
export function fastPanUpdate(delta: number[]) { export function fastPanUpdate(delta: number[]) {
@ -44,7 +45,7 @@ export function fastPanUpdate(delta: number[]) {
data.pageStates[data.currentPageId].camera = { ...camera } data.pageStates[data.currentPageId].camera = { ...camera }
state.forceData(Object.freeze(data)) state.forceData(freeze(data))
} }
export function fastZoomUpdate(point: number[], delta: number) { export function fastZoomUpdate(point: number[], delta: number) {
@ -60,7 +61,7 @@ export function fastZoomUpdate(point: number[], delta: number) {
data.pageStates[data.currentPageId].camera = { ...camera } data.pageStates[data.currentPageId].camera = { ...camera }
state.forceData(Object.freeze(data)) state.forceData(freeze(data))
} }
export function fastPinchCamera( export function fastPinchCamera(
@ -86,7 +87,7 @@ export function fastPinchCamera(
data.pageStates[data.currentPageId] = { ...pageState } data.pageStates[data.currentPageId] = { ...pageState }
state.forceData(Object.freeze(data)) state.forceData(freeze(data))
} }
export function fastBrushSelect(point: number[]) { export function fastBrushSelect(point: number[]) {
@ -94,7 +95,7 @@ export function fastBrushSelect(point: number[]) {
session.current.update(data, screenToWorld(point, data)) session.current.update(data, screenToWorld(point, data))
state.forceData(Object.freeze(data)) state.forceData(freeze(data))
} }
export function fastTranslate(info: PointerInfo) { export function fastTranslate(info: PointerInfo) {
@ -107,5 +108,18 @@ export function fastTranslate(info: PointerInfo) {
info.altKey info.altKey
) )
state.forceData(Object.freeze(data)) state.forceData(freeze(data))
}
export function fastTransform(info: PointerInfo) {
const data = { ...state.data }
session.current.update(
data,
screenToWorld(info.point, data),
info.shiftKey,
info.altKey
)
state.forceData(freeze(data))
} }

View file

@ -1,4 +1,4 @@
import { Data, Page, PageState } from 'types' import { Data } from 'types'
import { BaseCommand } from './commands/command' import { BaseCommand } from './commands/command'
import storage from './storage' import storage from './storage'
@ -24,7 +24,6 @@ class History<T extends Data> {
} }
storage.savePage(data) storage.savePage(data)
// storage.saveToLocalStorage(data)
} }
undo = (data: T) => { undo = (data: T) => {
@ -34,7 +33,6 @@ class History<T extends Data> {
if (this.disabled) return if (this.disabled) return
this.pointer-- this.pointer--
storage.savePage(data) storage.savePage(data)
// storage.saveToLocalStorage(data)
} }
redo = (data: T) => { redo = (data: T) => {
@ -44,7 +42,6 @@ class History<T extends Data> {
if (this.disabled) return if (this.disabled) return
this.pointer++ this.pointer++
storage.savePage(data) storage.savePage(data)
// storage.saveToLocalStorage(data)
} }
disable = () => { disable = () => {
@ -62,6 +59,13 @@ class History<T extends Data> {
} }
} }
reset() {
this.stack = []
this.pointer = -1
this.maxLength = 100
this._enabled = true
}
get disabled() { get disabled() {
return !this._enabled return !this._enabled
} }

View file

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import { PointerInfo } from 'types' import { PointerInfo } from 'types'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import { isDarwin, getPoint } from 'utils/utils' import { isDarwin, getPoint } from 'utils/utils'
const DOUBLE_CLICK_DURATION = 300 const DOUBLE_CLICK_DURATION = 300

View file

@ -1,5 +1,5 @@
import { ArrowShape, Data, LineShape, RayShape } from 'types' import { ArrowShape, Data, LineShape, RayShape } from 'types'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import BaseSession from './base-session' import BaseSession from './base-session'
import commands from 'state/commands' import commands from 'state/commands'
import { current } from 'immer' import { current } from 'immer'

View file

@ -11,7 +11,7 @@ import {
setSelectedIds, setSelectedIds,
setToArray, setToArray,
} from 'utils/utils' } from 'utils/utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import state from 'state/state' import state from 'state/state'
export default class BrushSession extends BaseSession { export default class BrushSession extends BaseSession {

View file

@ -1,5 +1,5 @@
import { Data, LineShape, RayShape } from 'types' import { Data, LineShape, RayShape } from 'types'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import BaseSession from './base-session' import BaseSession from './base-session'
import commands from 'state/commands' import commands from 'state/commands'
import { current } from 'immer' import { current } from 'immer'

View file

@ -3,7 +3,7 @@ import { Data, DrawShape } from 'types'
import BaseSession from './base-session' import BaseSession from './base-session'
import { getShapeUtils } from 'lib/shape-utils' import { getShapeUtils } from 'lib/shape-utils'
import { getPage, getShape, isMobile, updateParents } from 'utils/utils' import { getPage, getShape, isMobile, updateParents } from 'utils/utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import commands from 'state/commands' import commands from 'state/commands'
export default class BrushSession extends BaseSession { export default class BrushSession extends BaseSession {
origin: number[] origin: number[]

View file

@ -1,5 +1,5 @@
import { Data, LineShape, RayShape, Shape } from 'types' import { Data, LineShape, RayShape, Shape } from 'types'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import BaseSession from './base-session' import BaseSession from './base-session'
import commands from 'state/commands' import commands from 'state/commands'
import { current } from 'immer' import { current } from 'immer'

View file

@ -1,5 +1,5 @@
import { Data } from 'types' import { Data } from 'types'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import BaseSession from './base-session' import BaseSession from './base-session'
import commands from 'state/commands' import commands from 'state/commands'
import { current } from 'immer' import { current } from 'immer'

View file

@ -1,5 +1,5 @@
import { Data, ShapeType } from 'types' import { Data, ShapeType } from 'types'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import BaseSession from './base-session' import BaseSession from './base-session'
import commands from 'state/commands' import commands from 'state/commands'
import { current } from 'immer' import { current } from 'immer'

View file

@ -1,8 +1,8 @@
import { Data, Edge, Corner, Bounds } from 'types' import { Data, Edge, Corner, Bounds } from 'types'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import BaseSession from './base-session' import BaseSession from './base-session'
import commands from 'state/commands' import commands from 'state/commands'
import { current } from 'immer' import { current, freeze } from 'immer'
import { getShapeUtils } from 'lib/shape-utils' import { getShapeUtils } from 'lib/shape-utils'
import { import {
getBoundsCenter, getBoundsCenter,
@ -72,6 +72,8 @@ export default class TransformSession extends BaseSession {
scaleY: this.scaleY, scaleY: this.scaleY,
transformOrigin, transformOrigin,
}) })
shapes[id] = { ...shape }
} }
updateParents(data, Object.keys(shapeBounds)) updateParents(data, Object.keys(shapeBounds))

View file

@ -1,14 +1,11 @@
import { Data, Edge, Corner } from 'types' import { Data, Edge, Corner } from 'types'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import BaseSession from './base-session' import BaseSession from './base-session'
import commands from 'state/commands' import commands from 'state/commands'
import { current } from 'immer' import { current } from 'immer'
import { getShapeUtils } from 'lib/shape-utils' import { getShapeUtils } from 'lib/shape-utils'
import { import {
getTransformedBoundingBox, getTransformedBoundingBox,
getCommonBounds,
getRotatedCorners,
getTransformAnchor,
getPage, getPage,
getShape, getShape,
getSelectedShapes, getSelectedShapes,
@ -63,6 +60,8 @@ export default class TransformSingleSession extends BaseSession {
transformOrigin: [0.5, 0.5], transformOrigin: [0.5, 0.5],
}) })
data.document.pages[data.currentPageId].shapes[shape.id] = { ...shape }
updateParents(data, [id]) updateParents(data, [id])
} }

View file

@ -1,9 +1,9 @@
import { Data, GroupShape, Shape, ShapeType } from 'types' import { Data, GroupShape, Shape, ShapeType } from 'types'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import BaseSession from './base-session' import BaseSession from './base-session'
import commands from 'state/commands' import commands from 'state/commands'
import { current } from 'immer' import { current } from 'immer'
import { v4 as uuid } from 'uuid' import { uniqueId } from 'utils/utils'
import { import {
getChildIndexAbove, getChildIndexAbove,
getDocumentBranch, getDocumentBranch,
@ -215,7 +215,7 @@ export function getTranslateSnapshot(data: Data) {
.flatMap((shape) => { .flatMap((shape) => {
const clone = { const clone = {
...shape, ...shape,
id: uuid(), id: uniqueId(),
seed: Math.random(), seed: Math.random(),
parentId: shape.parentId, parentId: shape.parentId,
childIndex: getChildIndexAbove(cData, shape.id), childIndex: getChildIndexAbove(cData, shape.id),
@ -235,7 +235,7 @@ function cloneGroup(data: Data, clone: Shape): Shape[] {
const page = getPage(data) const page = getPage(data)
const childClones = clone.children.flatMap((id) => { const childClones = clone.children.flatMap((id) => {
const newId = uuid() const newId = uniqueId()
const source = page.shapes[id] const source = page.shapes[id]
const next = { ...source, id: newId, parentId: clone.id } const next = { ...source, id: newId, parentId: clone.id }

View file

@ -1,9 +1,8 @@
import { createSelectorHook, createState } from '@state-designer/react' import { createSelectorHook, createState } from '@state-designer/react'
import { updateFromCode } from 'lib/code/generate' import { updateFromCode } from 'lib/code/generate'
import { createShape, getShapeUtils } from 'lib/shape-utils' import { createShape, getShapeUtils } from 'lib/shape-utils'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import inputs from './inputs' import inputs from './inputs'
import { defaultDocument } from './data'
import history from './history' import history from './history'
import storage from './storage' import storage from './storage'
import * as Sessions from './sessions' import * as Sessions from './sessions'
@ -77,16 +76,29 @@ const initialData: Data = {
currentParentId: 'page1', currentParentId: 'page1',
currentCodeFileId: 'file0', currentCodeFileId: 'file0',
codeControls: {}, codeControls: {},
document: defaultDocument, document: {
pageStates: { id: '0001',
page1: { name: 'My Document',
selectedIds: new Set([]), pages: {
camera: { page1: {
point: [0, 0], id: 'page1',
zoom: 1, type: 'page',
name: 'Page 1',
childIndex: 0,
shapes: {},
}, },
}, },
page2: { code: {
file0: {
id: 'file0',
name: 'index.ts',
code: ``,
},
},
},
pageStates: {
page1: {
id: 'page1',
selectedIds: new Set([]), selectedIds: new Set([]),
camera: { camera: {
point: [0, 0], point: [0, 0],
@ -141,7 +153,7 @@ const state = createState({
CHANGED_PAGE: 'changePage', CHANGED_PAGE: 'changePage',
CREATED_PAGE: ['clearSelectedIds', 'createPage'], CREATED_PAGE: ['clearSelectedIds', 'createPage'],
DELETED_PAGE: { unless: 'hasOnlyOnePage', do: 'deletePage' }, DELETED_PAGE: { unless: 'hasOnlyOnePage', do: 'deletePage' },
LOADED_FROM_FILE: 'loadDocumentFromJson', LOADED_FROM_FILE: ['loadDocumentFromJson', 'resetHistory'],
PANNED_CAMERA: { PANNED_CAMERA: {
do: 'panCamera', do: 'panCamera',
}, },
@ -362,7 +374,7 @@ const state = createState({
transformingSelection: { transformingSelection: {
onEnter: 'startTransformSession', onEnter: 'startTransformSession',
on: { on: {
MOVED_POINTER: 'updateTransformSession', // MOVED_POINTER: 'updateTransformSession', using hacks.fastTransform
PANNED_CAMERA: 'updateTransformSession', PANNED_CAMERA: 'updateTransformSession',
PRESSED_SHIFT_KEY: 'keyUpdateTransformSession', PRESSED_SHIFT_KEY: 'keyUpdateTransformSession',
RELEASED_SHIFT_KEY: 'keyUpdateTransformSession', RELEASED_SHIFT_KEY: 'keyUpdateTransformSession',
@ -405,8 +417,7 @@ const state = createState({
], ],
on: { on: {
STARTED_PINCHING: { do: 'completeSession', to: 'pinching' }, STARTED_PINCHING: { do: 'completeSession', to: 'pinching' },
// Currently using hacks.fastBrushSelect // MOVED_POINTER: 'updateBrushSession', using hacks.fastBrushSelect
// MOVED_POINTER: 'updateBrushSession',
PANNED_CAMERA: 'updateBrushSession', PANNED_CAMERA: 'updateBrushSession',
STOPPED_POINTING: { do: 'completeSession', to: 'selecting' }, STOPPED_POINTING: { do: 'completeSession', to: 'selecting' },
CANCELLED: { do: 'cancelSession', to: 'selecting' }, CANCELLED: { do: 'cancelSession', to: 'selecting' },
@ -425,8 +436,7 @@ const state = createState({
}, },
pinching: { pinching: {
on: { on: {
// Pinching uses hacks.fastPinchCamera // PINCHED: { do: 'pinchCamera' }, using hacks.fastPinchCamera
// PINCHED: { do: 'pinchCamera' },
}, },
initial: 'selectPinching', initial: 'selectPinching',
onExit: { secretlyDo: 'updateZoomCSS' }, onExit: { secretlyDo: 'updateZoomCSS' },
@ -1498,6 +1508,9 @@ const state = createState({
redo(data) { redo(data) {
history.redo(data) history.redo(data)
}, },
resetHistory(data) {
history.reset()
},
/* --------------------- Styles --------------------- */ /* --------------------- Styles --------------------- */
@ -1595,8 +1608,8 @@ const state = createState({
storage.loadDocumentFromFilesystem() storage.loadDocumentFromFilesystem()
}, },
loadDocumentFromJson(data, payload: { restoredData: any }) { loadDocumentFromJson(data, payload: { json: any }) {
storage.loadDocumentFromJson(data, payload.restoredData) storage.loadDocumentFromJson(data, payload.json)
}, },
forceSave(data) { forceSave(data) {
@ -1613,7 +1626,7 @@ const state = createState({
saveCode(data, payload: { code: string }) { saveCode(data, payload: { code: string }) {
data.document.code[data.currentCodeFileId].code = payload.code data.document.code[data.currentCodeFileId].code = payload.code
storage.saveToLocalStorage(data) storage.saveDocumentToLocalStorage(data)
}, },
clearBoundsRotation(data) { clearBoundsRotation(data) {

View file

@ -1,13 +1,11 @@
import * as fa from 'browser-fs-access' import * as fa from 'browser-fs-access'
import { Data, Page, PageState, TLDocument } from 'types' import { Data, PageState, TLDocument } from 'types'
import { decompress, compress, setToArray } from 'utils/utils' import { decompress, compress, setToArray } from 'utils/utils'
import state from './state' import state from './state'
import { current } from 'immer' import { uniqueId } from 'utils/utils'
import { v4 as uuid } from 'uuid'
import * as idb from 'idb-keyval' import * as idb from 'idb-keyval'
const CURRENT_VERSION = 'code_slate_0.0.7' const CURRENT_VERSION = 'code_slate_0.0.7'
const DOCUMENT_ID = '0001'
function storageId(fileId: string, label: string, id?: string) { function storageId(fileId: string, label: string, id?: string) {
return [CURRENT_VERSION, fileId, label, id].filter(Boolean).join('_') return [CURRENT_VERSION, fileId, label, id].filter(Boolean).join('_')
@ -21,99 +19,186 @@ class Storage {
} }
firstLoad(data: Data) { firstLoad(data: Data) {
const lastOpened = localStorage.getItem(`${CURRENT_VERSION}_lastOpened`) const lastOpenedFileId = localStorage.getItem(
`${CURRENT_VERSION}_lastOpened`
this.loadDocumentFromLocalStorage(data, lastOpened || DOCUMENT_ID)
this.loadPage(data, data.currentPageId)
this.saveToLocalStorage(data, data.document.id)
localStorage.setItem(`${CURRENT_VERSION}_lastOpened`, data.document.id)
}
load(data: Data, restoredData: any) {
// Before loading the state, save the pages / page states
// Empty current state.
data.document = {} as TLDocument
data.pageStates = {}
// Merge restored data into state.
Object.assign(data, restoredData)
// Add id and name to document, just in case.
data.document = {
id: 'document0',
name: 'My Document',
...restoredData.document,
}
}
loadDocumentFromLocalStorage(data: Data, fileId = DOCUMENT_ID) {
if (typeof window === 'undefined') return
if (typeof localStorage === 'undefined') return
// Load data from local storage
const savedData = localStorage.getItem(
storageId(fileId, 'document', fileId)
) )
if (savedData === null) { // 1. Load Document from Local Storage
// If we're going to use the default data, assign the // Using the "last opened file id" in local storage.
// current document a fresh random id. if (lastOpenedFileId !== null) {
data.document.id = uuid() // Load document from local storage
return false const savedDocument = localStorage.getItem(
storageId(lastOpenedFileId, 'document', lastOpenedFileId)
)
if (savedDocument === null) {
// If no document found, create a fresh random id.
data.document.id = uniqueId()
} else {
// If we did find a document, load it into state.
const restoredDocument: TLDocument = JSON.parse(
decompress(savedDocument)
)
// Merge restored data into state.
data.document = restoredDocument
}
} }
const restoredData: any = JSON.parse(decompress(savedData)) this.load(data)
this.load(data, restoredData)
} }
getDataToSave = (data: Data) => { saveDocumentToLocalStorage(data: Data) {
const dataToSave = current(data) as any const document = this.getCompleteDocument(data)
for (let pageId in data.document.pages) { localStorage.setItem(
storageId(data.document.id, 'document', data.document.id),
compress(JSON.stringify(document))
)
}
getCompleteDocument = (data: Data) => {
// Create a safely mutable copy of the data
const document: TLDocument = { ...data.document }
// Try to find the document's pages and page states in local storage.
Object.keys(document.pages).forEach((pageId) => {
const savedPage = localStorage.getItem(
storageId(document.id, 'page', pageId)
)
if (savedPage !== null) {
document.pages[pageId] = JSON.parse(decompress(savedPage))
}
})
return document
}
savePageState = (data: Data) => {
localStorage.setItem(
storageId(data.document.id, 'lastPageState', data.document.id),
JSON.stringify(data.pageStates[data.currentPageId])
)
}
loadDocumentFromJson(data: Data, json: string) {
const restoredDocument: { document: TLDocument; pageState: PageState } =
JSON.parse(json)
data.document = restoredDocument.document
// Save pages to local storage, possibly overwriting unsaved local copies
Object.values(data.document.pages).forEach((page) => {
localStorage.setItem(
storageId(data.document.id, 'page', page.id),
compress(JSON.stringify(page))
)
})
localStorage.setItem(
storageId(data.document.id, 'lastPageState', data.document.id),
JSON.stringify(restoredDocument.pageState)
)
// Save the new file as the last opened document id
localStorage.setItem(`${CURRENT_VERSION}_lastOpened`, data.document.id)
this.load(data)
}
load(data: Data) {
// Once we've loaded data either from local storage or json, run through these steps.
data.pageStates = {}
// 2. Load Pages from Local Storage
// Try to find the document's pages and page states in local storage.
Object.keys(data.document.pages).forEach((pageId) => {
const savedPage = localStorage.getItem( const savedPage = localStorage.getItem(
storageId(data.document.id, 'page', pageId) storageId(data.document.id, 'page', pageId)
) )
if (savedPage !== null) { if (savedPage !== null) {
const restored: Page = JSON.parse(decompress(savedPage)) // If we've found a page in local storage, set it into state.
dataToSave.document.pages[pageId] = restored data.document.pages[pageId] = JSON.parse(decompress(savedPage))
} }
const pageState = { ...dataToSave.pageStates[pageId] } const savedPageState = localStorage.getItem(
pageState.selectedIds = setToArray(pageState.selectedIds) storageId(data.document.id, 'pageState', pageId)
} )
return JSON.stringify(dataToSave, null, 2) if (savedPageState !== null) {
} // If we've found a page state in local storage, set it into state.
data.pageStates[pageId] = JSON.parse(decompress(savedPageState))
data.pageStates[pageId].selectedIds = new Set([])
} else {
// Or else create a new one.
data.pageStates[pageId] = {
id: pageId,
selectedIds: new Set([]),
camera: {
point: [0, 0],
zoom: 1,
},
}
}
})
saveToLocalStorage = (data: Data, fileId = data.document.id) => { // 3. Restore the last page state
if (typeof window === 'undefined') return // Using the "last page state" in local storage.
if (typeof localStorage === 'undefined') return const savedPageState = localStorage.getItem(
storageId(data.document.id, 'lastPageState', data.document.id)
const dataToSave = this.getDataToSave(data)
// Save current data to local storage
localStorage.setItem(
storageId(fileId, 'document', fileId),
compress(dataToSave)
) )
}
loadDocumentFromJson(data: Data, restoredData: any) { if (savedPageState !== null) {
this.load(data, restoredData) const pageState = JSON.parse(decompress(savedPageState))
pageState.selectedIds = new Set([])
for (let key in restoredData.document.pages) { data.pageStates[pageState.id] = pageState
this.savePage(restoredData, restoredData.document.id, key) data.currentPageId = pageState.id
} }
this.loadPage(data, data.currentPageId) // 4. Save the current document
this.saveToLocalStorage(data, data.document.id) // The document is now "full" and ready. Whether we've restored a
// document or created a new one, save the entire current document.
localStorage.setItem(
storageId(data.document.id, 'document', data.document.id),
compress(JSON.stringify(data.document))
)
// 4.1
// Also save out copies of each page separately.
Object.values(data.document.pages).forEach((page) => {
// Save page
localStorage.setItem(
storageId(data.document.id, 'page', page.id),
compress(JSON.stringify(page))
)
})
// Save the last page state
const currentPageState = data.pageStates[data.currentPageId]
localStorage.setItem(
storageId(data.document.id, 'lastPageState', data.document.id),
JSON.stringify(currentPageState)
)
// Finally, save the current document id as the "last opened" document id.
localStorage.setItem(`${CURRENT_VERSION}_lastOpened`, data.document.id) localStorage.setItem(`${CURRENT_VERSION}_lastOpened`, data.document.id)
// 5. Prepare the new state.
// Clear out the other pages from state.
Object.values(data.document.pages).forEach((page) => {
if (page.id !== data.currentPageId) {
page.shapes = {}
}
})
// Update camera for the new page state
document.documentElement.style.setProperty(
'--camera-zoom',
data.pageStates[data.currentPageId].camera.zoom.toString()
)
} }
/* ---------------------- Pages --------------------- */ /* ---------------------- Pages --------------------- */
@ -127,25 +212,17 @@ class Storage {
} }
savePage(data: Data, fileId = data.document.id, pageId = data.currentPageId) { savePage(data: Data, fileId = data.document.id, pageId = data.currentPageId) {
if (typeof window === 'undefined') return const page = data.document.pages[pageId]
if (typeof localStorage === 'undefined') return
// Save page // Save page
const page = data.document.pages[pageId]
const json = JSON.stringify(page)
localStorage.setItem(storageId(fileId, 'page', pageId), compress(json)) localStorage.setItem(
storageId(fileId, 'page', pageId),
compress(JSON.stringify(page))
)
// Save page state // Save page state
let currentPageState = data.pageStates[pageId]
let currentPageState = {
camera: {
point: [0, 0],
zoom: 1,
},
selectedIds: new Set([]),
...data.pageStates[pageId],
}
localStorage.setItem( localStorage.setItem(
storageId(fileId, 'pageState', pageId), storageId(fileId, 'pageState', pageId),
@ -156,37 +233,42 @@ class Storage {
) )
} }
loadPage(data: Data, pageId = data.currentPageId) { loadPage(data: Data, fileId = data.document.id, pageId = data.currentPageId) {
if (typeof window === 'undefined') return if (typeof window === 'undefined') return
if (typeof localStorage === 'undefined') return if (typeof localStorage === 'undefined') return
const fileId = data.document.id data.currentPageId = pageId
// Page // Get saved page from local storage
const savedPage = localStorage.getItem(storageId(fileId, 'page', pageId)) const savedPage = localStorage.getItem(storageId(fileId, 'page', pageId))
if (savedPage !== null) { if (savedPage !== null) {
// If we have a page, move it into state
data.document.pages[pageId] = JSON.parse(decompress(savedPage)) data.document.pages[pageId] = JSON.parse(decompress(savedPage))
} else { } else {
// If we don't have a page, create a new page
data.document.pages[pageId] = { data.document.pages[pageId] = {
id: pageId, id: pageId,
type: 'page', type: 'page',
childIndex: 0, childIndex: Object.keys(data.document.pages).length,
name: 'Page', name: 'New Page',
shapes: {}, shapes: {},
} }
} }
// Page state // Get saved page state from local storage
const savedPageState = localStorage.getItem( const savedPageState = localStorage.getItem(
storageId(fileId, 'pageState', pageId) storageId(fileId, 'pageState', pageId)
) )
if (savedPageState !== null) { if (savedPageState !== null) {
// If we have a page, move it into state
const restored: PageState = JSON.parse(savedPageState) const restored: PageState = JSON.parse(savedPageState)
data.pageStates[pageId] = restored data.pageStates[pageId] = restored
data.pageStates[pageId].selectedIds = new Set(restored.selectedIds)
} else { } else {
data.pageStates[pageId] = { data.pageStates[pageId] = {
id: pageId,
camera: { camera: {
point: [0, 0], point: [0, 0],
zoom: 1, zoom: 1,
@ -195,17 +277,20 @@ class Storage {
} }
} }
// Empty shapes in state for other pages // Save the last page state
localStorage.setItem(
storageId(fileId, 'lastPageState'),
JSON.stringify(data.pageStates[pageId])
)
for (let key in data.document.pages) { // Prepare new state
if (key === pageId) continue
data.document.pages[key].shapes = {}
}
// Force selected Ids into sets // Now clear out the other pages from state.
for (let key in data.pageStates) { Object.values(data.document.pages).forEach((page) => {
data.pageStates[key].selectedIds = new Set([]) if (page.id !== data.currentPageId) {
} page.shapes = {}
}
})
// Update camera for the new page state // Update camera for the new page state
document.documentElement.style.setProperty( document.documentElement.style.setProperty(
@ -217,21 +302,32 @@ class Storage {
/* ------------------- File System ------------------ */ /* ------------------- File System ------------------ */
saveToFileSystem = (data: Data) => { saveToFileSystem = (data: Data) => {
this.saveDocumentToLocalStorage(data)
this.saveDataToFileSystem(data, data.document.id, false) this.saveDataToFileSystem(data, data.document.id, false)
} }
saveAsToFileSystem = (data: Data) => { saveAsToFileSystem = (data: Data) => {
this.saveDataToFileSystem(data, uuid(), true) this.saveDocumentToLocalStorage(data)
this.saveDataToFileSystem(data, uniqueId(), true)
} }
saveDataToFileSystem = (data: Data, fileId: string, saveAs: boolean) => { saveDataToFileSystem = (data: Data, fileId: string, saveAs: boolean) => {
const json = this.getDataToSave(data) const document = this.getCompleteDocument(data)
this.saveToLocalStorage(data, fileId) // Then save to file system
const blob = new Blob(
const blob = new Blob([json], { [
type: 'application/vnd.tldraw+json', compress(
}) JSON.stringify({
document,
pageState: data.pageStates[data.currentPageId],
})
),
],
{
type: 'application/vnd.tldraw+json',
}
)
fa.fileSave( fa.fileSave(
blob, blob,
@ -258,23 +354,15 @@ class Storage {
} }
loadDocumentFromFilesystem() { loadDocumentFromFilesystem() {
console.warn('Loading file from file system.')
fa.fileOpen({ fa.fileOpen({
description: 'tldraw files', description: 'tldraw files',
}) })
.then((blob) => .then((blob) =>
getTextFromBlob(blob).then((text) => { getTextFromBlob(blob).then((json) => {
const restoredData = JSON.parse(text)
if (restoredData === null) {
console.warn('Could not load that data.')
return
}
// Save blob for future saves // Save blob for future saves
this.previousSaveHandle = blob.handle this.previousSaveHandle = blob.handle
state.send('LOADED_FROM_FILE', { restoredData: { ...restoredData } }) state.send('LOADED_FROM_FILE', { json: decompress(json) })
}) })
) )
.catch((e) => { .catch((e) => {

View file

@ -30,6 +30,7 @@
- [x] Create context Menu - [x] Create context Menu
- [x] Wire up events - [x] Wire up events
- [ ] Use nested context menu
## Move to Page ## Move to Page

View file

@ -1,7 +1,5 @@
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api' import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'
import React from 'react'
/* -------------------------------------------------- */ /* -------------------------------------------------- */
/* Client State */ /* Client State */
/* -------------------------------------------------- */ /* -------------------------------------------------- */
@ -53,6 +51,7 @@ export interface Page {
} }
export interface PageState { export interface PageState {
id: string
selectedIds: Set<string> selectedIds: Set<string>
camera: { camera: {
point: number[] point: number[]

View file

@ -1,5 +1,5 @@
import { Bounds } from "types" import { Bounds } from 'types'
import * as vec from "./vec" import vec from './vec'
/** /**
* Get whether a point is inside of a bounds. * Get whether a point is inside of a bounds.

View file

@ -1,5 +1,5 @@
import { Bounds } from 'types' import { Bounds } from 'types'
import * as vec from 'utils/vec' import vec from 'utils/vec'
import { isAngleBetween } from './utils' import { isAngleBetween } from './utils'
interface Intersection { interface Intersection {

View file

@ -1,6 +1,6 @@
// Some helpers for drawing SVGs. // Some helpers for drawing SVGs.
import * as vec from './vec' import vec from './vec'
import { getSweep } from 'utils/utils' import { getSweep } from 'utils/utils'
// General // General

View file

@ -1,19 +1,15 @@
import Vector from 'lib/code/vector' import Vector from 'lib/code/vector'
import React from 'react' import React from 'react'
import { import { Data, Bounds, Edge, Corner, Shape, GroupShape, ShapeType } from 'types'
Data, import { v4 as uuid } from 'uuid'
Bounds, import vec from './vec'
Edge,
Corner,
Shape,
ShapeStyles,
GroupShape,
ShapeType,
} from 'types'
import * as vec from './vec'
import _isMobile from 'ismobilejs' import _isMobile from 'ismobilejs'
import { getShapeUtils } from 'lib/shape-utils' import { getShapeUtils } from 'lib/shape-utils'
export function uniqueId() {
return uuid()
}
export function screenToWorld(point: number[], data: Data) { export function screenToWorld(point: number[], data: Data) {
const camera = getCurrentCamera(data) const camera = getCurrentCamera(data)
return vec.sub(vec.div(point, camera.zoom), camera.point) return vec.sub(vec.div(point, camera.zoom), camera.point)

View file

@ -1,483 +1,487 @@
/** // A big collection of vector utilities. Collected into a class to improve logging / packaging.
* Clamp a value into a range.
* @param n
* @param min
*/
export function clamp(n: number, min: number): number
export function clamp(n: number, min: number, max: number): number
export function clamp(n: number, min: number, max?: number): number {
return Math.max(min, typeof max !== 'undefined' ? Math.min(n, max) : n)
}
/** export default class Vec {
* Negate a vector. /**
* @param A * Clamp a value into a range.
*/ * @param n
export function neg(A: number[]) { * @param min
return [-A[0], -A[1]] */
} static clamp(n: number, min: number): number
static clamp(n: number, min: number, max: number): number
/** static clamp(n: number, min: number, max?: number): number {
* Add vectors. return Math.max(min, typeof max !== 'undefined' ? Math.min(n, max) : n)
* @param A
* @param B
*/
export function add(A: number[], B: number[]) {
return [A[0] + B[0], A[1] + B[1]]
}
/**
* Add scalar to vector.
* @param A
* @param B
*/
export function addScalar(A: number[], n: number) {
return [A[0] + n, A[1] + n]
}
/**
* Subtract vectors.
* @param A
* @param B
*/
export function sub(A: number[], B: number[]) {
return [A[0] - B[0], A[1] - B[1]]
}
/**
* Subtract scalar from vector.
* @param A
* @param B
*/
export function subScalar(A: number[], n: number) {
return [A[0] - n, A[1] - n]
}
/**
* Get the vector from vectors A to B.
* @param A
* @param B
*/
export function vec(A: number[], B: number[]) {
// A, B as vectors get the vector from A to B
return [B[0] - A[0], B[1] - A[1]]
}
/**
* Vector multiplication by scalar
* @param A
* @param n
*/
export function mul(A: number[], n: number) {
return [A[0] * n, A[1] * n]
}
export function mulV(A: number[], B: number[]) {
return [A[0] * B[0], A[1] * B[1]]
}
/**
* Vector division by scalar.
* @param A
* @param n
*/
export function div(A: number[], n: number) {
return [A[0] / n, A[1] / n]
}
/**
* Vector division by vector.
* @param A
* @param n
*/
export function divV(A: number[], B: number[]) {
return [A[0] / B[0], A[1] / B[1]]
}
/**
* Perpendicular rotation of a vector A
* @param A
*/
export function per(A: number[]) {
return [A[1], -A[0]]
}
/**
* Dot product
* @param A
* @param B
*/
export function dpr(A: number[], B: number[]) {
return A[0] * B[0] + A[1] * B[1]
}
/**
* Cross product (outer product) | A X B |
* @param A
* @param B
*/
export function cpr(A: number[], B: number[]) {
return A[0] * B[1] - B[0] * A[1]
}
/**
* Length of the vector squared
* @param A
*/
export function len2(A: number[]) {
return A[0] * A[0] + A[1] * A[1]
}
/**
* Length of the vector
* @param A
*/
export function len(A: number[]) {
return Math.hypot(A[0], A[1])
}
/**
* Project A over B
* @param A
* @param B
*/
export function pry(A: number[], B: number[]) {
return dpr(A, B) / len(B)
}
/**
* Get normalized / unit vector.
* @param A
*/
export function uni(A: number[]) {
return div(A, len(A))
}
/**
* Get normalized / unit vector.
* @param A
*/
export function normalize(A: number[]) {
return uni(A)
}
/**
* Get the tangent between two vectors.
* @param A
* @param B
* @returns
*/
export function tangent(A: number[], B: number[]) {
return normalize(sub(A, B))
}
/**
* Dist length from A to B squared.
* @param A
* @param B
*/
export function dist2(A: number[], B: number[]) {
return len2(sub(A, B))
}
/**
* Dist length from A to B
* @param A
* @param B
*/
export function dist(A: number[], B: number[]) {
return Math.hypot(A[1] - B[1], A[0] - B[0])
}
/**
* A faster, though less accurate method for testing distances. Maybe faster?
* @param A
* @param B
* @returns
*/
export function fastDist(A: number[], B: number[]) {
const V = [B[0] - A[0], B[1] - A[1]]
const aV = [Math.abs(V[0]), Math.abs(V[1])]
let r = 1 / Math.max(aV[0], aV[1])
r = r * (1.29289 - (aV[0] + aV[1]) * r * 0.29289)
return [V[0] * r, V[1] * r]
}
/**
* Angle between vector A and vector B in radians
* @param A
* @param B
*/
export function ang(A: number[], B: number[]) {
return Math.atan2(cpr(A, B), dpr(A, B))
}
/**
* Angle between vector A and vector B in radians
* @param A
* @param B
*/
export function angle(A: number[], B: number[]) {
return Math.atan2(B[1] - A[1], B[0] - A[0])
}
/**
* Mean between two vectors or mid vector between two vectors
* @param A
* @param B
*/
export function med(A: number[], B: number[]) {
return mul(add(A, B), 0.5)
}
/**
* Vector rotation by r (radians)
* @param A
* @param r rotation in radians
*/
export function rot(A: number[], r: number) {
return [
A[0] * Math.cos(r) - A[1] * Math.sin(r),
A[0] * Math.sin(r) + A[1] * Math.cos(r),
]
}
/**
* Rotate a vector around another vector by r (radians)
* @param A vector
* @param C center
* @param r rotation in radians
*/
export function rotWith(A: number[], C: number[], r: number) {
if (r === 0) return A
const s = Math.sin(r)
const c = Math.cos(r)
const px = A[0] - C[0]
const py = A[1] - C[1]
const nx = px * c - py * s
const ny = px * s + py * c
return [nx + C[0], ny + C[1]]
}
/**
* Check of two vectors are identical.
* @param A
* @param B
*/
export function isEqual(A: number[], B: number[]) {
return A[0] === B[0] && A[1] === B[1]
}
/**
* Interpolate vector A to B with a scalar t
* @param A
* @param B
* @param t scalar
*/
export function lrp(A: number[], B: number[], t: number) {
return add(A, mul(vec(A, B), t))
}
/**
* Interpolate from A to B when curVAL goes fromVAL => to
* @param A
* @param B
* @param from Starting value
* @param to Ending value
* @param s Strength
*/
export function int(A: number[], B: number[], from: number, to: number, s = 1) {
const t = (clamp(from, to) - from) / (to - from)
return add(mul(A, 1 - t), mul(B, s))
}
/**
* Get the angle between the three vectors A, B, and C.
* @param p1
* @param pc
* @param p2
*/
export function ang3(p1: number[], pc: number[], p2: number[]) {
// this,
const v1 = vec(pc, p1)
const v2 = vec(pc, p2)
return ang(v1, v2)
}
/**
* Absolute value of a vector.
* @param A
* @returns
*/
export function abs(A: number[]) {
return [Math.abs(A[0]), Math.abs(A[1])]
}
export function rescale(a: number[], n: number) {
const l = len(a)
return [(n * a[0]) / l, (n * a[1]) / l]
}
/**
* Get whether p1 is left of p2, relative to pc.
* @param p1
* @param pc
* @param p2
*/
export function isLeft(p1: number[], pc: number[], p2: number[]) {
// isLeft: >0 for counterclockwise
// =0 for none (degenerate)
// <0 for clockwise
return (pc[0] - p1[0]) * (p2[1] - p1[1]) - (p2[0] - p1[0]) * (pc[1] - p1[1])
}
export function clockwise(p1: number[], pc: number[], p2: number[]) {
return isLeft(p1, pc, p2) > 0
}
export function round(a: number[], d = 5) {
return a.map((v) => Number(v.toPrecision(d)))
}
/**
* Get the minimum distance from a point P to a line with a segment AB.
* @param A The start of the line.
* @param B The end of the line.
* @param P A point.
* @returns
*/
// export function distanceToLine(A: number[], B: number[], P: number[]) {
// const delta = sub(B, A)
// const angle = Math.atan2(delta[1], delta[0])
// const dir = rot(sub(P, A), -angle)
// return dir[1]
// }
/**
* Get the nearest point on a line segment AB.
* @param A The start of the line.
* @param B The end of the line.
* @param P A point.
* @param clamp Whether to clamp the resulting point to the segment.
* @returns
*/
// export function nearestPointOnLine(
// A: number[],
// B: number[],
// P: number[],
// clamp = true
// ) {
// const delta = sub(B, A)
// const length = len(delta)
// const angle = Math.atan2(delta[1], delta[0])
// const dir = rot(sub(P, A), -angle)
// if (clamp) {
// if (dir[0] < 0) return A
// if (dir[0] > length) return B
// }
// return add(A, div(mul(delta, dir[0]), length))
// }
/**
* Get the nearest point on a line with a known unit vector that passes through point A
* @param A Any point on the line
* @param u The unit vector for the line.
* @param P A point not on the line to test.
* @returns
*/
export function nearestPointOnLineThroughPoint(
A: number[],
u: number[],
P: number[]
) {
return add(A, mul(u, pry(sub(P, A), u)))
}
/**
* Distance between a point and a line with a known unit vector that passes through a point.
* @param A Any point on the line
* @param u The unit vector for the line.
* @param P A point not on the line to test.
* @returns
*/
export function distanceToLineThroughPoint(
A: number[],
u: number[],
P: number[]
) {
return dist(P, nearestPointOnLineThroughPoint(A, u, P))
}
/**
* Get the nearest point on a line segment between A and B
* @param A The start of the line segment
* @param B The end of the line segment
* @param P The off-line point
* @param clamp Whether to clamp the point between A and B.
* @returns
*/
export function nearestPointOnLineSegment(
A: number[],
B: number[],
P: number[],
clamp = true
) {
const delta = sub(B, A)
const length = len(delta)
const u = div(delta, length)
const pt = add(A, mul(u, pry(sub(P, A), u)))
if (clamp) {
const da = dist(A, pt)
const db = dist(B, pt)
if (db < da && da > length) return B
if (da < db && db > length) return A
} }
return pt /**
} * Negate a vector.
* @param A
*/
static neg = (A: number[]) => {
return [-A[0], -A[1]]
}
/** /**
* Distance between a point and the nearest point on a line segment between A and B * Add vectors.
* @param A The start of the line segment * @param A
* @param B The end of the line segment * @param B
* @param P The off-line point */
* @param clamp Whether to clamp the point between A and B. static add = (A: number[], B: number[]) => {
* @returns return [A[0] + B[0], A[1] + B[1]]
*/ }
export function distanceToLineSegment(
A: number[],
B: number[],
P: number[],
clamp = true
) {
return dist(P, nearestPointOnLineSegment(A, B, P, clamp))
}
/** /**
* Get a vector d distance from A towards B. * Add scalar to vector.
* @param A * @param A
* @param B * @param B
* @param d */
* @returns static addScalar = (A: number[], n: number) => {
*/ return [A[0] + n, A[1] + n]
export function nudge(A: number[], B: number[], d: number) { }
return add(A, mul(uni(vec(A, B)), d))
}
/** /**
* Round a vector to a precision length. * Subtract vectors.
* @param a * @param A
* @param n * @param B
*/ */
export function toPrecision(a: number[], n = 4) { static sub = (A: number[], B: number[]) => {
return [+a[0].toPrecision(n), +a[1].toPrecision(n)] return [A[0] - B[0], A[1] - B[1]]
}
/**
* Subtract scalar from vector.
* @param A
* @param B
*/
static subScalar = (A: number[], n: number) => {
return [A[0] - n, A[1] - n]
}
/**
* Get the vector from vectors A to B.
* @param A
* @param B
*/
static vec = (A: number[], B: number[]) => {
// A, B as vectors get the vector from A to B
return [B[0] - A[0], B[1] - A[1]]
}
/**
* Vector multiplication by scalar
* @param A
* @param n
*/
static mul = (A: number[], n: number) => {
return [A[0] * n, A[1] * n]
}
static mulV = (A: number[], B: number[]) => {
return [A[0] * B[0], A[1] * B[1]]
}
/**
* Vector division by scalar.
* @param A
* @param n
*/
static div = (A: number[], n: number) => {
return [A[0] / n, A[1] / n]
}
/**
* Vector division by vector.
* @param A
* @param n
*/
static divV = (A: number[], B: number[]) => {
return [A[0] / B[0], A[1] / B[1]]
}
/**
* Perpendicular rotation of a vector A
* @param A
*/
static per(A: number[]) {
return [A[1], -A[0]]
}
/**
* Dot product
* @param A
* @param B
*/
static dpr = (A: number[], B: number[]) => {
return A[0] * B[0] + A[1] * B[1]
}
/**
* Cross product (outer product) | A X B |
* @param A
* @param B
*/
static cpr = (A: number[], B: number[]) => {
return A[0] * B[1] - B[0] * A[1]
}
/**
* Length of the vector squared
* @param A
*/
static len2 = (A: number[]) => {
return A[0] * A[0] + A[1] * A[1]
}
/**
* Length of the vector
* @param A
*/
static len = (A: number[]) => {
return Math.hypot(A[0], A[1])
}
/**
* Project A over B
* @param A
* @param B
*/
static pry = (A: number[], B: number[]) => {
return Vec.dpr(A, B) / Vec.len(B)
}
/**
* Get normalized / unit vector.
* @param A
*/
static uni = (A: number[]) => {
return Vec.div(A, Vec.len(A))
}
/**
* Get normalized / unit vector.
* @param A
*/
static normalize = (A: number[]) => {
return Vec.uni(A)
}
/**
* Get the tangent between two vectors.
* @param A
* @param B
* @returns
*/
static tangent = (A: number[], B: number[]) => {
return Vec.normalize(Vec.sub(A, B))
}
/**
* Dist length from A to B squared.
* @param A
* @param B
*/
static dist2 = (A: number[], B: number[]) => {
return Vec.len2(Vec.sub(A, B))
}
/**
* Dist length from A to B
* @param A
* @param B
*/
static dist = (A: number[], B: number[]) => {
return Math.hypot(A[1] - B[1], A[0] - B[0])
}
/**
* A faster, though less accurate method for testing distances. Maybe faster?
* @param A
* @param B
* @returns
*/
static fastDist = (A: number[], B: number[]) => {
const V = [B[0] - A[0], B[1] - A[1]]
const aV = [Math.abs(V[0]), Math.abs(V[1])]
let r = 1 / Math.max(aV[0], aV[1])
r = r * (1.29289 - (aV[0] + aV[1]) * r * 0.29289)
return [V[0] * r, V[1] * r]
}
/**
* Angle between vector A and vector B in radians
* @param A
* @param B
*/
static ang = (A: number[], B: number[]) => {
return Math.atan2(Vec.cpr(A, B), Vec.dpr(A, B))
}
/**
* Angle between vector A and vector B in radians
* @param A
* @param B
*/
static angle = (A: number[], B: number[]) => {
return Math.atan2(B[1] - A[1], B[0] - A[0])
}
/**
* Mean between two vectors or mid vector between two vectors
* @param A
* @param B
*/
static med = (A: number[], B: number[]) => {
return Vec.mul(Vec.add(A, B), 0.5)
}
/**
* Vector rotation by r (radians)
* @param A
* @param r rotation in radians
*/
static rot = (A: number[], r: number) => {
return [
A[0] * Math.cos(r) - A[1] * Math.sin(r),
A[0] * Math.sin(r) + A[1] * Math.cos(r),
]
}
/**
* Rotate a vector around another vector by r (radians)
* @param A vector
* @param C center
* @param r rotation in radians
*/
static rotWith = (A: number[], C: number[], r: number) => {
if (r === 0) return A
const s = Math.sin(r)
const c = Math.cos(r)
const px = A[0] - C[0]
const py = A[1] - C[1]
const nx = px * c - py * s
const ny = px * s + py * c
return [nx + C[0], ny + C[1]]
}
/**
* Check of two vectors are identical.
* @param A
* @param B
*/
static isEqual = (A: number[], B: number[]) => {
return A[0] === B[0] && A[1] === B[1]
}
/**
* Interpolate vector A to B with a scalar t
* @param A
* @param B
* @param t scalar
*/
static lrp = (A: number[], B: number[], t: number) => {
return Vec.add(A, Vec.mul(Vec.vec(A, B), t))
}
/**
* Interpolate from A to B when curVAL goes fromVAL => to
* @param A
* @param B
* @param from Starting value
* @param to Ending value
* @param s Strength
*/
static int = (A: number[], B: number[], from: number, to: number, s = 1) => {
const t = (Vec.clamp(from, to) - from) / (to - from)
return Vec.add(Vec.mul(A, 1 - t), Vec.mul(B, s))
}
/**
* Get the angle between the three vectors A, B, and C.
* @param p1
* @param pc
* @param p2
*/
static ang3 = (p1: number[], pc: number[], p2: number[]) => {
// this,
const v1 = Vec.vec(pc, p1)
const v2 = Vec.vec(pc, p2)
return Vec.ang(v1, v2)
}
/**
* Absolute value of a vector.
* @param A
* @returns
*/
static abs = (A: number[]) => {
return [Math.abs(A[0]), Math.abs(A[1])]
}
static rescale = (a: number[], n: number) => {
const l = Vec.len(a)
return [(n * a[0]) / l, (n * a[1]) / l]
}
/**
* Get whether p1 is left of p2, relative to pc.
* @param p1
* @param pc
* @param p2
*/
static isLeft = (p1: number[], pc: number[], p2: number[]) => {
// isLeft: >0 for counterclockwise
// =0 for none (degenerate)
// <0 for clockwise
return (pc[0] - p1[0]) * (p2[1] - p1[1]) - (p2[0] - p1[0]) * (pc[1] - p1[1])
}
static clockwise = (p1: number[], pc: number[], p2: number[]) => {
return Vec.isLeft(p1, pc, p2) > 0
}
static round = (a: number[], d = 5) => {
return a.map((v) => Number(v.toPrecision(d)))
}
/**
* Get the minimum distance from a point P to a line with a segment AB.
* @param A The start of the line.
* @param B The end of the line.
* @param P A point.
* @returns
*/
// static distanceToLine(A: number[], B: number[], P: number[]) {
// const delta = sub(B, A)
// const angle = Math.atan2(delta[1], delta[0])
// const dir = rot(sub(P, A), -angle)
// return dir[1]
// }
/**
* Get the nearest point on a line segment AB.
* @param A The start of the line.
* @param B The end of the line.
* @param P A point.
* @param clamp Whether to clamp the resulting point to the segment.
* @returns
*/
// static nearestPointOnLine(
// A: number[],
// B: number[],
// P: number[],
// clamp = true
// ) {
// const delta = sub(B, A)
// const length = len(delta)
// const angle = Math.atan2(delta[1], delta[0])
// const dir = rot(sub(P, A), -angle)
// if (clamp) {
// if (dir[0] < 0) return A
// if (dir[0] > length) return B
// }
// return add(A, div(mul(delta, dir[0]), length))
// }
/**
* Get the nearest point on a line with a known unit vector that passes through point A
* @param A Any point on the line
* @param u The unit vector for the line.
* @param P A point not on the line to test.
* @returns
*/
static nearestPointOnLineThroughPoint = (
A: number[],
u: number[],
P: number[]
) => {
return Vec.add(A, Vec.mul(u, Vec.pry(Vec.sub(P, A), u)))
}
/**
* Distance between a point and a line with a known unit vector that passes through a point.
* @param A Any point on the line
* @param u The unit vector for the line.
* @param P A point not on the line to test.
* @returns
*/
static distanceToLineThroughPoint = (
A: number[],
u: number[],
P: number[]
) => {
return Vec.dist(P, Vec.nearestPointOnLineThroughPoint(A, u, P))
}
/**
* Get the nearest point on a line segment between A and B
* @param A The start of the line segment
* @param B The end of the line segment
* @param P The off-line point
* @param clamp Whether to clamp the point between A and B.
* @returns
*/
static nearestPointOnLineSegment = (
A: number[],
B: number[],
P: number[],
clamp = true
) => {
const delta = Vec.sub(B, A)
const length = Vec.len(delta)
const u = Vec.div(delta, length)
const pt = Vec.add(A, Vec.mul(u, Vec.pry(Vec.sub(P, A), u)))
if (clamp) {
const da = Vec.dist(A, pt)
const db = Vec.dist(B, pt)
if (db < da && da > length) return B
if (da < db && db > length) return A
}
return pt
}
/**
* Distance between a point and the nearest point on a line segment between A and B
* @param A The start of the line segment
* @param B The end of the line segment
* @param P The off-line point
* @param clamp Whether to clamp the point between A and B.
* @returns
*/
static distanceToLineSegment = (
A: number[],
B: number[],
P: number[],
clamp = true
) => {
return Vec.dist(P, Vec.nearestPointOnLineSegment(A, B, P, clamp))
}
/**
* Get a vector d distance from A towards B.
* @param A
* @param B
* @param d
* @returns
*/
static nudge = (A: number[], B: number[], d: number) => {
return Vec.add(A, Vec.mul(Vec.uni(Vec.vec(A, B)), d))
}
/**
* Round a vector to a precision length.
* @param a
* @param n
*/
static toPrecision = (a: number[], n = 4) => {
return [+a[0].toPrecision(n), +a[1].toPrecision(n)]
}
} }

298
yarn.lock
View file

@ -1573,38 +1573,40 @@
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-arrow@0.0.13": "@radix-ui/react-arrow@0.0.14":
version "0.0.13" version "0.0.14"
resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-0.0.13.tgz#06b5a17f33bbc1af1c88d2827f25f37be95daa38" resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-0.0.14.tgz#70b2c66efbf3cde0c9dd0895417e39f6cdf31805"
integrity sha512-BHBAULgQYmj36BrJ+1AGhC5p4QjJaE+szJgJ1a1EYOM3G6QOeIQKYvIm8TPEdKAiJhAivK+jZFccsE4Blzqc9g== integrity sha512-ZWfQM3hTCXN6ub2dMUmMv2ttEB/1zuBlOZdeWFeCCJHwZ+yy7iPzWF6ixaNygBa6t451PImq/dC7sqOnDJCfqQ==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-polymorphic" "0.0.11" "@radix-ui/react-polymorphic" "0.0.12"
"@radix-ui/react-primitive" "0.0.13" "@radix-ui/react-primitive" "0.0.14"
"@radix-ui/react-checkbox@^0.0.15": "@radix-ui/react-checkbox@^0.0.16":
version "0.0.15" version "0.0.16"
resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-0.0.15.tgz#d53b56854fbba65e74ed4486116107638951b9d1" resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-0.0.16.tgz#503a934f73745e9c3831a5fd39d3b06062813f0e"
integrity sha512-R8ErERPlu2kvmqNjxRyyLcS1y3D7J2bQUUEPsvP0BL2AfisUjbT7c9t19k2K/Un3Iieqe93gTPG4LRdbDQQjBw== integrity sha512-2RJep0OB4FZvGk/PTou8BEt/cISA/i88iiloZuAxlOjYgF90W4xfrQqgczLCC64t0ee3DtUkSOJyjO45M95hVg==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.0.5" "@radix-ui/primitive" "0.0.5"
"@radix-ui/react-compose-refs" "0.0.5" "@radix-ui/react-compose-refs" "0.0.5"
"@radix-ui/react-context" "0.0.5" "@radix-ui/react-context" "0.0.5"
"@radix-ui/react-label" "0.0.13" "@radix-ui/react-label" "0.0.14"
"@radix-ui/react-polymorphic" "0.0.11" "@radix-ui/react-polymorphic" "0.0.12"
"@radix-ui/react-presence" "0.0.14" "@radix-ui/react-presence" "0.0.14"
"@radix-ui/react-primitive" "0.0.13" "@radix-ui/react-primitive" "0.0.14"
"@radix-ui/react-use-controllable-state" "0.0.6" "@radix-ui/react-use-controllable-state" "0.0.6"
"@radix-ui/react-use-previous" "0.0.5"
"@radix-ui/react-use-size" "0.0.6"
"@radix-ui/react-collection@0.0.12": "@radix-ui/react-collection@0.0.13":
version "0.0.12" version "0.0.13"
resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-0.0.12.tgz#5cd09312cdec34fdbbe1d31affaba69eb768e342" resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-0.0.13.tgz#050fc9b1d84b58ab04a5852812b181767dde2e85"
integrity sha512-jUP99/wCHpXm7ytbmpcjhhxFHBsr2lptEzKO8DUkD2CrJBS4Q3XHMKBDOvNQQzIqJhsz0A5JP6Fo0Vgd8Ld3FQ== integrity sha512-TKUUFLeRlxp+1S1FQz9pQkP1VbRwZGOEH8oUdgWjUsNc+GVURlS3H5twsDtSAAfvebOc6YmDTE23mvvhfsPhnw==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "0.0.5" "@radix-ui/react-compose-refs" "0.0.5"
"@radix-ui/react-slot" "0.0.10" "@radix-ui/react-slot" "0.0.11"
"@radix-ui/react-compose-refs@0.0.5": "@radix-ui/react-compose-refs@0.0.5":
version "0.0.5" version "0.0.5"
@ -1613,17 +1615,17 @@
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-context-menu@^0.0.19": "@radix-ui/react-context-menu@^0.0.21":
version "0.0.19" version "0.0.21"
resolved "https://registry.yarnpkg.com/@radix-ui/react-context-menu/-/react-context-menu-0.0.19.tgz#47ef194d7bc925ff7f998889be9fd373ce4bd9a1" resolved "https://registry.yarnpkg.com/@radix-ui/react-context-menu/-/react-context-menu-0.0.21.tgz#b080c8e515b9a0d18fcda9b02f34b7a94347ef08"
integrity sha512-FR2jXeFqxD9n+1AC81+7jfMbnz80kdQNMfgIcPQL1S0M5SODADdDiTKKfok4Blc1nYWe7nEwBTYauMirF7avSQ== integrity sha512-snRhgVDtdxBNwds2ULd7RQAB97PfSeKlrL5rRudlj4YTlLq7y/Jr0oJ7FTnu9EylfH2ii0uKcW5dEVhrbzr5YQ==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.0.5" "@radix-ui/primitive" "0.0.5"
"@radix-ui/react-context" "0.0.5" "@radix-ui/react-context" "0.0.5"
"@radix-ui/react-menu" "0.0.18" "@radix-ui/react-menu" "0.0.19"
"@radix-ui/react-polymorphic" "0.0.11" "@radix-ui/react-polymorphic" "0.0.12"
"@radix-ui/react-primitive" "0.0.13" "@radix-ui/react-primitive" "0.0.14"
"@radix-ui/react-use-callback-ref" "0.0.5" "@radix-ui/react-use-callback-ref" "0.0.5"
"@radix-ui/react-context@0.0.5": "@radix-ui/react-context@0.0.5":
@ -1633,50 +1635,54 @@
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-dialog@^0.0.17": "@radix-ui/react-dialog@^0.0.18":
version "0.0.17" version "0.0.18"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-0.0.17.tgz#736e37626c4e9e20d46546ddbbb8869a352578f0" resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-0.0.18.tgz#8d2d9f8816bb8447056031583a3a3cb2b6305281"
integrity sha512-DOSW8SdniyVLro+MvF0owaEEa8MUYGMuvuuSQpFnD/hA+K0pKzyTSjEuC7OeflPCImBFEbmKhgRoeWypfMZZOA== integrity sha512-EH8yxFh3hQQ/hIPQsBzdJgx3oWTEmLu2a2x2PfRjxbDhcDIjcYJWdeEMjkTUjkBwpz3h6L/JWqnYJ2dqA65Deg==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.0.5" "@radix-ui/primitive" "0.0.5"
"@radix-ui/react-compose-refs" "0.0.5" "@radix-ui/react-compose-refs" "0.0.5"
"@radix-ui/react-context" "0.0.5" "@radix-ui/react-context" "0.0.5"
"@radix-ui/react-dismissable-layer" "0.0.13" "@radix-ui/react-dismissable-layer" "0.0.14"
"@radix-ui/react-focus-guards" "0.0.7" "@radix-ui/react-focus-guards" "0.0.7"
"@radix-ui/react-focus-scope" "0.0.13" "@radix-ui/react-focus-scope" "0.0.14"
"@radix-ui/react-id" "0.0.6" "@radix-ui/react-id" "0.0.6"
"@radix-ui/react-polymorphic" "0.0.11" "@radix-ui/react-polymorphic" "0.0.12"
"@radix-ui/react-portal" "0.0.13" "@radix-ui/react-portal" "0.0.14"
"@radix-ui/react-presence" "0.0.14" "@radix-ui/react-presence" "0.0.14"
"@radix-ui/react-primitive" "0.0.13" "@radix-ui/react-primitive" "0.0.14"
"@radix-ui/react-slot" "0.0.11"
"@radix-ui/react-use-controllable-state" "0.0.6" "@radix-ui/react-use-controllable-state" "0.0.6"
aria-hidden "^1.1.1" aria-hidden "^1.1.1"
react-remove-scroll "^2.4.0" react-remove-scroll "^2.4.0"
"@radix-ui/react-dismissable-layer@0.0.13": "@radix-ui/react-dismissable-layer@0.0.14":
version "0.0.13" version "0.0.14"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-0.0.13.tgz#7c4be6170a14d8a66c48680a8a8c987bc29bcf05" resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-0.0.14.tgz#9d8a3415a2830688070c6596dec18b43c33df7b2"
integrity sha512-g0zhxdZzCJhVeRumaU8ODptRFhYorSRPsLwD30sz9slYaW0yg6lHvMQicRNtgFX0WnsbmrUVY6NMrwWpSHJXbg== integrity sha512-0pmRuGYYvWlEaED1igGFLjic0+hD0OqvsnrZaN3n1nDOkoCd7H5CA2geaShSrlBF5riI2Dr9jIZPGLbDRhs4DA==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.0.5"
"@radix-ui/react-polymorphic" "0.0.12"
"@radix-ui/react-primitive" "0.0.14"
"@radix-ui/react-use-body-pointer-events" "0.0.6" "@radix-ui/react-use-body-pointer-events" "0.0.6"
"@radix-ui/react-use-callback-ref" "0.0.5" "@radix-ui/react-use-callback-ref" "0.0.5"
"@radix-ui/react-use-escape-keydown" "0.0.6" "@radix-ui/react-use-escape-keydown" "0.0.6"
"@radix-ui/react-dropdown-menu@^0.0.19": "@radix-ui/react-dropdown-menu@^0.0.20":
version "0.0.19" version "0.0.20"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-0.0.19.tgz#fbc3106bcda65d3060b280f4af3a9c45324ab474" resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-0.0.20.tgz#ca4667deb38ff7b74e6741aac1cebd1b50e3ec07"
integrity sha512-7UhT6PIXl9fr8Ai9DkXMtxPNQFh8emrklgY5jcad9NHKWAztgfL6Fm+Ns0GEYUkd5OpJLEaIj/EUkwFM73SrGw== integrity sha512-3MXtFeNkRZxAA32gvAGBdzlmJRMCNgBKWy0rDSd5NNzRjtqNFKZRR5dd1gWQTiS24aLxSiztVc9exnvMx4Lu8g==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.0.5" "@radix-ui/primitive" "0.0.5"
"@radix-ui/react-compose-refs" "0.0.5" "@radix-ui/react-compose-refs" "0.0.5"
"@radix-ui/react-context" "0.0.5" "@radix-ui/react-context" "0.0.5"
"@radix-ui/react-id" "0.0.6" "@radix-ui/react-id" "0.0.6"
"@radix-ui/react-menu" "0.0.18" "@radix-ui/react-menu" "0.0.19"
"@radix-ui/react-polymorphic" "0.0.11" "@radix-ui/react-polymorphic" "0.0.12"
"@radix-ui/react-primitive" "0.0.13" "@radix-ui/react-primitive" "0.0.14"
"@radix-ui/react-use-controllable-state" "0.0.6" "@radix-ui/react-use-controllable-state" "0.0.6"
"@radix-ui/react-focus-guards@0.0.7": "@radix-ui/react-focus-guards@0.0.7":
@ -1686,12 +1692,15 @@
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-focus-scope@0.0.13": "@radix-ui/react-focus-scope@0.0.14":
version "0.0.13" version "0.0.14"
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-0.0.13.tgz#06dd6781d457b272601d4c087ac1240907824443" resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-0.0.14.tgz#778e2a3ea607621d82e0139616d7ea6d517d9533"
integrity sha512-PelAuc+7HSGruBraSzuHogwaKqCvmO288ecIm3cCAkrJqPQ7hoKSd/LfLfoa/EvjqK9azmm7NQ6LSPoteQvOGQ== integrity sha512-D3v6Tw8vzpIBNd2I32Q2G4LCiXMIlmc6Pl2VV9CZjSatDOjkV/ckGbhkQyQ7QxnD/0CmiSxNo5hTeGRmZDjwmA==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "0.0.5"
"@radix-ui/react-polymorphic" "0.0.12"
"@radix-ui/react-primitive" "0.0.14"
"@radix-ui/react-use-callback-ref" "0.0.5" "@radix-ui/react-use-callback-ref" "0.0.5"
"@radix-ui/react-icons@^1.0.3": "@radix-ui/react-icons@^1.0.3":
@ -1706,72 +1715,72 @@
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-label@0.0.13": "@radix-ui/react-label@0.0.14":
version "0.0.13" version "0.0.14"
resolved "https://registry.yarnpkg.com/@radix-ui/react-label/-/react-label-0.0.13.tgz#b71930fa16a2cf859296317436cb88e31efb8ecf" resolved "https://registry.yarnpkg.com/@radix-ui/react-label/-/react-label-0.0.14.tgz#32928a0f5d72e3a24b99021f5c6805e96f3d9395"
integrity sha512-csNElm8qA38pOHr772CXIvBXd/eCGaoAMImuLdawUxQNzwxQ4npd8lr/f9fi/4OLkgeNOVOqjsaVamiNmF/lIw== integrity sha512-gtrEpON6Ye8OBYtOEJBEuB/n/h6x07VPs7JR/1t34LaB74GJ+gACcafLcg5gMGWku3Gbc3eaDwfhOkxhTuJdYQ==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "0.0.5" "@radix-ui/react-compose-refs" "0.0.5"
"@radix-ui/react-id" "0.0.6" "@radix-ui/react-id" "0.0.6"
"@radix-ui/react-polymorphic" "0.0.11" "@radix-ui/react-polymorphic" "0.0.12"
"@radix-ui/react-primitive" "0.0.13" "@radix-ui/react-primitive" "0.0.14"
"@radix-ui/react-menu@0.0.18": "@radix-ui/react-menu@0.0.19":
version "0.0.18" version "0.0.19"
resolved "https://registry.yarnpkg.com/@radix-ui/react-menu/-/react-menu-0.0.18.tgz#b36f7657eb6757c623ffc688c48a4781ffd82351" resolved "https://registry.yarnpkg.com/@radix-ui/react-menu/-/react-menu-0.0.19.tgz#12036862ad6c813e442a14316b5ed380f54962e9"
integrity sha512-js5hFzoxNOnHV8g7RPoPl/GncUCW2aCOuNt9Qh6WznRmxmsETPUWZZe4kADJnZmYbIxG07EEl0iv3E1rmsqNMw== integrity sha512-ACGk5i83KahzHGyZVpWGyZBi68VCplQgMmS8ZvuuwDGf1vQl5dlpxvWM8iKh6EUL8zCTXNzX2jRtg0NZYoRrPg==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.0.5" "@radix-ui/primitive" "0.0.5"
"@radix-ui/react-collection" "0.0.12" "@radix-ui/react-collection" "0.0.13"
"@radix-ui/react-compose-refs" "0.0.5" "@radix-ui/react-compose-refs" "0.0.5"
"@radix-ui/react-context" "0.0.5" "@radix-ui/react-context" "0.0.5"
"@radix-ui/react-dismissable-layer" "0.0.13" "@radix-ui/react-dismissable-layer" "0.0.14"
"@radix-ui/react-focus-guards" "0.0.7" "@radix-ui/react-focus-guards" "0.0.7"
"@radix-ui/react-focus-scope" "0.0.13" "@radix-ui/react-focus-scope" "0.0.14"
"@radix-ui/react-polymorphic" "0.0.11" "@radix-ui/react-id" "0.0.6"
"@radix-ui/react-popper" "0.0.16" "@radix-ui/react-polymorphic" "0.0.12"
"@radix-ui/react-portal" "0.0.13" "@radix-ui/react-popper" "0.0.17"
"@radix-ui/react-portal" "0.0.14"
"@radix-ui/react-presence" "0.0.14" "@radix-ui/react-presence" "0.0.14"
"@radix-ui/react-primitive" "0.0.13" "@radix-ui/react-primitive" "0.0.14"
"@radix-ui/react-roving-focus" "0.0.13" "@radix-ui/react-roving-focus" "0.0.14"
"@radix-ui/react-slot" "0.0.10" "@radix-ui/react-slot" "0.0.11"
"@radix-ui/react-use-callback-ref" "0.0.5" "@radix-ui/react-use-callback-ref" "0.0.5"
"@radix-ui/react-use-direction" "0.0.1"
aria-hidden "^1.1.1" aria-hidden "^1.1.1"
react-remove-scroll "^2.4.0" react-remove-scroll "^2.4.0"
"@radix-ui/react-polymorphic@0.0.11": "@radix-ui/react-polymorphic@0.0.12":
version "0.0.11" version "0.0.12"
resolved "https://registry.yarnpkg.com/@radix-ui/react-polymorphic/-/react-polymorphic-0.0.11.tgz#23f26b14e67a0e57cd981c37618d603b952af80d" resolved "https://registry.yarnpkg.com/@radix-ui/react-polymorphic/-/react-polymorphic-0.0.12.tgz#bf4ae516669b68e059549538104d97322f7c876b"
integrity sha512-CztM4962esOx3i1ls6GuY9RBYIY2Df1Bmp5emHRTxZi8owyCZwZYPctYaDuMO0qIGikPiKD8HBion/m7VWUyEA== integrity sha512-/GYNMicBnGzjD1d2fCAuzql1VeFrp8mqM3xfzT1kxhnV85TKdURO45jBfMgqo17XNXoNhWIAProUsCO4qFAAIg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-popper@0.0.16": "@radix-ui/react-popper@0.0.17":
version "0.0.16" version "0.0.17"
resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-0.0.16.tgz#62cccb7d920dc89e076bbdc3421db8c84078f428" resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-0.0.17.tgz#a73486e19a628cb3fecaf3fb6eecf6e2cab9d0be"
integrity sha512-njciQ/eIKaDF9h+27Pwi1H6NliQbzTgaHOsT0w/Lxx4vfMe8zcHtiEigYVGErNR4zAYlbW72KzLjtngtNnaorg== integrity sha512-2Lk5AjlCcN9B6fQwQ+y1Zb93f1LfyCK6u0oOAUsPtwbnYEHCdC6wuCFBOZ7AonpjHbrHbY6QDrLGcC43TY6ihw==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/popper" "0.0.10" "@radix-ui/popper" "0.0.10"
"@radix-ui/react-arrow" "0.0.13" "@radix-ui/react-arrow" "0.0.14"
"@radix-ui/react-compose-refs" "0.0.5" "@radix-ui/react-compose-refs" "0.0.5"
"@radix-ui/react-context" "0.0.5" "@radix-ui/react-context" "0.0.5"
"@radix-ui/react-polymorphic" "0.0.11" "@radix-ui/react-polymorphic" "0.0.12"
"@radix-ui/react-primitive" "0.0.13" "@radix-ui/react-primitive" "0.0.14"
"@radix-ui/react-use-rect" "0.0.7" "@radix-ui/react-use-rect" "0.0.7"
"@radix-ui/react-use-size" "0.0.6" "@radix-ui/react-use-size" "0.0.6"
"@radix-ui/rect" "0.0.5" "@radix-ui/rect" "0.0.5"
"@radix-ui/react-portal@0.0.13": "@radix-ui/react-portal@0.0.14":
version "0.0.13" version "0.0.14"
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-0.0.13.tgz#8e60e9ee9b1594f98ee4a8f24a9851ef7ef2ad31" resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-0.0.14.tgz#31513d8777cf5e50a3a30ebc9deb34821e890e9e"
integrity sha512-Mw5hrxds2T9HTGwdDbvlKGvTUfcuKhPtqxLnizOrM685e80j+pfjAAfMSXSyfmra9KFQvk3XxmUP0d4U6+kzMg== integrity sha512-Wi9arVwVenonjZIX6znCBYaasua03Tb+UtrBZShepJkLGtbGxDlzExijiGIaIRNetl46Oc2pw0F6Y6HffDnUww==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-polymorphic" "0.0.11" "@radix-ui/react-polymorphic" "0.0.12"
"@radix-ui/react-primitive" "0.0.13" "@radix-ui/react-primitive" "0.0.14"
"@radix-ui/react-use-layout-effect" "0.0.5" "@radix-ui/react-use-layout-effect" "0.0.5"
"@radix-ui/react-presence@0.0.14": "@radix-ui/react-presence@0.0.14":
@ -1782,79 +1791,80 @@
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "0.0.5" "@radix-ui/react-compose-refs" "0.0.5"
"@radix-ui/react-primitive@0.0.13": "@radix-ui/react-primitive@0.0.14":
version "0.0.13" version "0.0.14"
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-0.0.13.tgz#79c606c3e7da9c377b77a7b3f4ec879b45de47a2" resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-0.0.14.tgz#752a967cb05d4c5643634fe20274e7dc905d1cce"
integrity sha512-6xxWzw67t7ZuN9Ikn9NNQdb/HSF7dNHPN3kPcgjiVgTEZa3tKk1xjSxGjjQztE61g9GrnTLpu7mBjmEuZDI/lA== integrity sha512-FYOWGCrxFpLdB534aWTwMK4Pjg8cxFb+745qWhPfI+cYi+aYUddJQD3ilRHHXxCBD72ve7/PufqeB7Y/QlKqgg==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-polymorphic" "0.0.11" "@radix-ui/react-polymorphic" "0.0.12"
"@radix-ui/react-radio-group@^0.0.16": "@radix-ui/react-radio-group@^0.0.17":
version "0.0.16" version "0.0.17"
resolved "https://registry.yarnpkg.com/@radix-ui/react-radio-group/-/react-radio-group-0.0.16.tgz#10fc6e5c3102599cf422e9f6f8d2766088e602a1" resolved "https://registry.yarnpkg.com/@radix-ui/react-radio-group/-/react-radio-group-0.0.17.tgz#29ae3d361fc6823cd082d29b7d96efd370dfdf2a"
integrity sha512-vOtgflNWcauSul+EvnPCxATdmPw7fb1cuqBJX07yJdjbrw1Iv5v/+d79fNyIwPR+KrkhP+uCMIBfF0gvo6K7ZQ== integrity sha512-Xvibkj4jGxV9AipZyhFU8xXI5pFPSx/7OGxkRMwHs6U1a28vWppVq++hBhybxCNPO1ZragC2hzl7xdtkz+sugg==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.0.5" "@radix-ui/primitive" "0.0.5"
"@radix-ui/react-compose-refs" "0.0.5" "@radix-ui/react-compose-refs" "0.0.5"
"@radix-ui/react-context" "0.0.5" "@radix-ui/react-context" "0.0.5"
"@radix-ui/react-label" "0.0.13" "@radix-ui/react-label" "0.0.14"
"@radix-ui/react-polymorphic" "0.0.11" "@radix-ui/react-polymorphic" "0.0.12"
"@radix-ui/react-presence" "0.0.14" "@radix-ui/react-presence" "0.0.14"
"@radix-ui/react-primitive" "0.0.13" "@radix-ui/react-primitive" "0.0.14"
"@radix-ui/react-roving-focus" "0.0.13" "@radix-ui/react-roving-focus" "0.0.14"
"@radix-ui/react-slot" "0.0.10" "@radix-ui/react-slot" "0.0.11"
"@radix-ui/react-use-callback-ref" "0.0.5" "@radix-ui/react-use-callback-ref" "0.0.5"
"@radix-ui/react-use-controllable-state" "0.0.6" "@radix-ui/react-use-controllable-state" "0.0.6"
"@radix-ui/react-use-previous" "0.0.5"
"@radix-ui/react-use-size" "0.0.6"
"@radix-ui/react-roving-focus@0.0.13": "@radix-ui/react-roving-focus@0.0.14":
version "0.0.13" version "0.0.14"
resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-0.0.13.tgz#c72f503832577979c4caa9efcfd59140730c2f80" resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-0.0.14.tgz#cedc20ee78227199e4379434489cdd7c00e3edbd"
integrity sha512-jTx3TBMYEdj9SFzRDryO62M9ZK28pYvKKxj/mxWMpMbIjF4q6L+yJmDxtv6glHgCRkwBEyulFVGCjjDg+qxYRA== integrity sha512-MHYdC5kyx/3V8/9DbvMDOCPHP/4blRkaD3EUM4w7VTHUHiZgw6+Dnk1Gc2LoTkcx8Oz413aU+S11vyRdw/1pcA==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.0.5" "@radix-ui/primitive" "0.0.5"
"@radix-ui/react-collection" "0.0.12" "@radix-ui/react-collection" "0.0.13"
"@radix-ui/react-compose-refs" "0.0.5" "@radix-ui/react-compose-refs" "0.0.5"
"@radix-ui/react-context" "0.0.5" "@radix-ui/react-context" "0.0.5"
"@radix-ui/react-id" "0.0.6" "@radix-ui/react-id" "0.0.6"
"@radix-ui/react-polymorphic" "0.0.11" "@radix-ui/react-polymorphic" "0.0.12"
"@radix-ui/react-primitive" "0.0.13" "@radix-ui/react-primitive" "0.0.14"
"@radix-ui/react-use-callback-ref" "0.0.5" "@radix-ui/react-use-callback-ref" "0.0.5"
"@radix-ui/react-use-controllable-state" "0.0.6" "@radix-ui/react-use-controllable-state" "0.0.6"
"@radix-ui/react-slot@0.0.10": "@radix-ui/react-slot@0.0.11":
version "0.0.10" version "0.0.11"
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-0.0.10.tgz#27a17cad7064872117aeb68113fa934bc5d34c37" resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-0.0.11.tgz#d0121018ae20d86683d527b7011b7f21465dffbd"
integrity sha512-p0jJj6lTz1RV2imavnclk8Gda002ZSDR4/zPJ4EQBhspGnx7Y8l6G59c8lxJrT7c7F46F2eRNjpTTjFqqir6EQ== integrity sha512-H/l58Aqoyi9Jq5Jcny5v6HeMNelOivm2NUdaIvMBJzysbbwWhc78VEmpOpfQ6H+y/Fsx6aLprHbfmxPd/66PxQ==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.0.5"
"@radix-ui/react-compose-refs" "0.0.5" "@radix-ui/react-compose-refs" "0.0.5"
"@radix-ui/react-tooltip@^0.0.18": "@radix-ui/react-tooltip@^0.0.19":
version "0.0.18" version "0.0.19"
resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-0.0.18.tgz#7594297dbc2acf101ac45fdb414c7bc0ac9426bc" resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-0.0.19.tgz#2ee843c7c5612708b31ad52adbefc580b213a421"
integrity sha512-oYRAbbTZJ8zEokrrk5pe7QbzF+ZnzMby8mBplrkColi0ntToJ7RKzPgUs1OOFvmg/Nld0Iy2FufrTNlyyEI3kQ== integrity sha512-zDFrGH0nQfRZyk7qD73o22nCbA1QNQoo8cl6RhBLBAFG/b+h1Q5Y4VHu2ZKEz3zZEgrZ8lOKnX8A6dOa9619PQ==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.0.5" "@radix-ui/primitive" "0.0.5"
"@radix-ui/react-compose-refs" "0.0.5" "@radix-ui/react-compose-refs" "0.0.5"
"@radix-ui/react-context" "0.0.5" "@radix-ui/react-context" "0.0.5"
"@radix-ui/react-id" "0.0.6" "@radix-ui/react-id" "0.0.6"
"@radix-ui/react-polymorphic" "0.0.11" "@radix-ui/react-polymorphic" "0.0.12"
"@radix-ui/react-popper" "0.0.16" "@radix-ui/react-popper" "0.0.17"
"@radix-ui/react-portal" "0.0.13" "@radix-ui/react-portal" "0.0.14"
"@radix-ui/react-presence" "0.0.14" "@radix-ui/react-presence" "0.0.14"
"@radix-ui/react-primitive" "0.0.13" "@radix-ui/react-primitive" "0.0.14"
"@radix-ui/react-slot" "0.0.10" "@radix-ui/react-slot" "0.0.11"
"@radix-ui/react-use-controllable-state" "0.0.6" "@radix-ui/react-use-controllable-state" "0.0.6"
"@radix-ui/react-use-escape-keydown" "0.0.6" "@radix-ui/react-use-escape-keydown" "0.0.6"
"@radix-ui/react-use-layout-effect" "0.0.5" "@radix-ui/react-use-layout-effect" "0.0.5"
"@radix-ui/react-use-previous" "0.0.5" "@radix-ui/react-use-previous" "0.0.5"
"@radix-ui/react-use-rect" "0.0.7" "@radix-ui/react-use-rect" "0.0.7"
"@radix-ui/react-visually-hidden" "0.0.13" "@radix-ui/react-visually-hidden" "0.0.14"
"@radix-ui/react-use-body-pointer-events@0.0.6": "@radix-ui/react-use-body-pointer-events@0.0.6":
version "0.0.6" version "0.0.6"
@ -1879,6 +1889,13 @@
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-use-callback-ref" "0.0.5" "@radix-ui/react-use-callback-ref" "0.0.5"
"@radix-ui/react-use-direction@0.0.1":
version "0.0.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-direction/-/react-use-direction-0.0.1.tgz#9ac72eb6d9902ed505c8a34048981d94f9433e14"
integrity sha512-sU+tkP09uEI1m+YJAR1ZAZLVFY1h/JD+jLSSQt5Wo3b9SYrJA889i2hH1P3DNRyWbbbisweiEQdK3MWILhFCig==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-escape-keydown@0.0.6": "@radix-ui/react-use-escape-keydown@0.0.6":
version "0.0.6" version "0.0.6"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-0.0.6.tgz#1ad1c81b99961b7dbe376ef54151ebc8bef627a0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-0.0.6.tgz#1ad1c81b99961b7dbe376ef54151ebc8bef627a0"
@ -1916,14 +1933,14 @@
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-visually-hidden@0.0.13": "@radix-ui/react-visually-hidden@0.0.14":
version "0.0.13" version "0.0.14"
resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-0.0.13.tgz#c7f69097eb7d796dcd9117cdd228d87991c08baf" resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-0.0.14.tgz#47b764d295275572aa290dabc81c39be3091663b"
integrity sha512-8VNuE4/3PnyrLv1je56fxaa5qka0Nb6/FlyQEDF2HCPpxVOWR4sxRfSBe8cjy+Me+pJN9ZoKBIuoFCVRk54xJA== integrity sha512-fCz6Q24EWfU5uX4JGXFoO2I5H5otGzyyrxyKeOKYpkWiTGY93CO9C4oN7rXiRNCxkm68bgynAgIG40TjOqy0Lw==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-polymorphic" "0.0.11" "@radix-ui/react-polymorphic" "0.0.12"
"@radix-ui/react-primitive" "0.0.13" "@radix-ui/react-primitive" "0.0.14"
"@radix-ui/rect@0.0.5": "@radix-ui/rect@0.0.5":
version "0.0.5" version "0.0.5"
@ -2034,17 +2051,10 @@
dependencies: dependencies:
"@state-designer/core" latest "@state-designer/core" latest
"@stitches/core@^0.1.9": "@stitches/react@^0.2.1":
version "0.1.9" version "0.2.1"
resolved "https://registry.yarnpkg.com/@stitches/core/-/core-0.1.9.tgz#6cc42ae6052f6900f9add164232449ff8d62c4ba" resolved "https://registry.yarnpkg.com/@stitches/react/-/react-0.2.1.tgz#c63a0a337b849b302da2342cb5d7aeb6ea257913"
integrity sha512-70gVb0uhgV6BOJmloirWEGZmofk/Hzr5yWlub1rtm4CvimD3Xl5xr9fVkjcTu+REC6ILCJCnlEP4WmilTfXNOA== integrity sha512-LPCOUXsadQM4JrtHQEsOJ+msyss0KySO9jL8L2SUVi9Vi6BfbyEi1mVGaqgvIQRZQfWsM0Se/3AXcl9rUQi5Nw==
"@stitches/react@^0.1.9":
version "0.1.9"
resolved "https://registry.yarnpkg.com/@stitches/react/-/react-0.1.9.tgz#b16e05c9ee7e34369bb3b051b92a997ab379863f"
integrity sha512-yK4ZAdkuOiOum+bfPYlDNS9UI/Tm9JYsYNMUWLB3uAy+7tvSYfB4cVYkXwFuiKxis03woATTZTe6IlTb+LXm+A==
dependencies:
"@stitches/core" "^0.1.9"
"@surma/rollup-plugin-off-main-thread@^1.4.1": "@surma/rollup-plugin-off-main-thread@^1.4.1":
version "1.4.2" version "1.4.2"
@ -7134,10 +7144,10 @@ pend@~1.2.0:
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
perfect-freehand@^0.4.8: perfect-freehand@^0.4.9:
version "0.4.8" version "0.4.9"
resolved "https://registry.yarnpkg.com/perfect-freehand/-/perfect-freehand-0.4.8.tgz#0054995322fdd9939c0c38c260d96a9d0f22eb4f" resolved "https://registry.yarnpkg.com/perfect-freehand/-/perfect-freehand-0.4.9.tgz#09e9f5a1057da7eda335367d5f1f2c6fbd369e3e"
integrity sha512-zU0hvTh0ctjb/h5+nwFhb+/5ZnqS8Z16mn7lY2tYy3NwTkjrEKZ8CJvQ2phlOV5kk+BnCDFGVz7tkKrl9+Mr9w== integrity sha512-mNf9Yd2dxWV3kZs2PPNWjLOt8Yh31+PIu6/zst7I8YJOicYBnRofzXl9us5gxqK4ZfHKO5O7PjttHF0tuHfo0g==
performance-now@^2.1.0: performance-now@^2.1.0:
version "2.1.0" version "2.1.0"