Big restructure of utils
This commit is contained in:
parent
80d2ba5f00
commit
45223e2a8d
69 changed files with 1334 additions and 1031 deletions
|
@ -1,7 +1,7 @@
|
||||||
import state from 'state'
|
import state from 'state'
|
||||||
import { generateFromCode } from 'state/code/generate'
|
import { generateFromCode } from 'state/code/generate'
|
||||||
import { getShape, getShapes } from 'utils'
|
|
||||||
import * as json from './__mocks__/document.json'
|
import * as json from './__mocks__/document.json'
|
||||||
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
jest.useRealTimers()
|
jest.useRealTimers()
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ describe('selection', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('saves changes to code', () => {
|
it('saves changes to code', () => {
|
||||||
expect(getShapes(state.data).length).toBe(0)
|
expect(tld.getShapes(state.data).length).toBe(0)
|
||||||
|
|
||||||
const code = `// hello world!`
|
const code = `// hello world!`
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ describe('selection', () => {
|
||||||
|
|
||||||
state.send('GENERATED_FROM_CODE', { controls, shapes })
|
state.send('GENERATED_FROM_CODE', { controls, shapes })
|
||||||
|
|
||||||
expect(getShapes(state.data)).toMatchSnapshot(
|
expect(tld.getShapes(state.data)).toMatchSnapshot(
|
||||||
'generated rectangle from code'
|
'generated rectangle from code'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -106,7 +106,7 @@ describe('selection', () => {
|
||||||
'data in state after changing control'
|
'data in state after changing control'
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(getShape(state.data, 'test-rectangle')).toMatchSnapshot(
|
expect(tld.getShape(state.data, 'test-rectangle')).toMatchSnapshot(
|
||||||
'rectangle in state after changing code control'
|
'rectangle in state after changing code control'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -116,7 +116,7 @@ describe('selection', () => {
|
||||||
it('does not saves changes to code when readonly', () => {
|
it('does not saves changes to code when readonly', () => {
|
||||||
state.send('CLEARED_PAGE')
|
state.send('CLEARED_PAGE')
|
||||||
|
|
||||||
expect(getShapes(state.data).length).toBe(0)
|
expect(tld.getShapes(state.data).length).toBe(0)
|
||||||
|
|
||||||
const code = `// hello world!`
|
const code = `// hello world!`
|
||||||
|
|
||||||
|
@ -190,7 +190,7 @@ describe('selection', () => {
|
||||||
|
|
||||||
state.send('GENERATED_FROM_CODE', { controls, shapes })
|
state.send('GENERATED_FROM_CODE', { controls, shapes })
|
||||||
|
|
||||||
expect(getShapes(state.data)).toMatchSnapshot(
|
expect(tld.getShapes(state.data)).toMatchSnapshot(
|
||||||
'generated rectangle from code'
|
'generated rectangle from code'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -220,7 +220,9 @@ describe('selection', () => {
|
||||||
|
|
||||||
state.send('GENERATED_FROM_CODE', { controls, shapes })
|
state.send('GENERATED_FROM_CODE', { controls, shapes })
|
||||||
|
|
||||||
expect(getShapes(state.data)).toMatchSnapshot('generated ellipse from code')
|
expect(tld.getShapes(state.data)).toMatchSnapshot(
|
||||||
|
'generated ellipse from code'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates a draw shape', async () => {
|
it('generates a draw shape', async () => {
|
||||||
|
@ -242,7 +244,9 @@ describe('selection', () => {
|
||||||
|
|
||||||
state.send('GENERATED_FROM_CODE', { controls, shapes })
|
state.send('GENERATED_FROM_CODE', { controls, shapes })
|
||||||
|
|
||||||
expect(getShapes(state.data)).toMatchSnapshot('generated draw from code')
|
expect(tld.getShapes(state.data)).toMatchSnapshot(
|
||||||
|
'generated draw from code'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates an arrow shape', async () => {
|
it('generates an arrow shape', async () => {
|
||||||
|
@ -264,7 +268,9 @@ describe('selection', () => {
|
||||||
|
|
||||||
state.send('GENERATED_FROM_CODE', { controls, shapes })
|
state.send('GENERATED_FROM_CODE', { controls, shapes })
|
||||||
|
|
||||||
expect(getShapes(state.data)).toMatchSnapshot('generated draw from code')
|
expect(tld.getShapes(state.data)).toMatchSnapshot(
|
||||||
|
'generated draw from code'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates a text shape', async () => {
|
it('generates a text shape', async () => {
|
||||||
|
@ -287,6 +293,8 @@ describe('selection', () => {
|
||||||
|
|
||||||
state.send('GENERATED_FROM_CODE', { controls, shapes })
|
state.send('GENERATED_FROM_CODE', { controls, shapes })
|
||||||
|
|
||||||
expect(getShapes(state.data)).toMatchSnapshot('generated draw from code')
|
expect(tld.getShapes(state.data)).toMatchSnapshot(
|
||||||
|
'generated draw from code'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -69,7 +69,7 @@ describe('deletes and restores grouped shapes', () => {
|
||||||
// Should select the group
|
// Should select the group
|
||||||
expect(assertShapeProps(group, { type: ShapeType.Group }))
|
expect(assertShapeProps(group, { type: ShapeType.Group }))
|
||||||
|
|
||||||
const arrow = getShape(state.data, arrowId)
|
const arrow = tld.getShape(state.data, arrowId)
|
||||||
|
|
||||||
// The arrow should be have the group as its parent
|
// The arrow should be have the group as its parent
|
||||||
expect(assertShapeProps(arrow, { parentId: group.id }))
|
expect(assertShapeProps(arrow, { parentId: group.id }))
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Data, Shape, ShapeType } from 'types'
|
import { Data, Shape, ShapeType } from 'types'
|
||||||
import { getSelectedIds, getSelectedShapes, getShape } from 'utils'
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
export const rectangleId = '1f6c251c-e12e-40b4-8dd2-c1847d80b72f'
|
export const rectangleId = '1f6c251c-e12e-40b4-8dd2-c1847d80b72f'
|
||||||
export const arrowId = '5ca167d7-54de-47c9-aa8f-86affa25e44d'
|
export const arrowId = '5ca167d7-54de-47c9-aa8f-86affa25e44d'
|
||||||
|
@ -40,7 +40,7 @@ export function idsAreSelected(
|
||||||
ids: string[],
|
ids: string[],
|
||||||
strict = true
|
strict = true
|
||||||
): boolean {
|
): boolean {
|
||||||
const selectedIds = getSelectedIds(data)
|
const selectedIds = tld.getSelectedIds(data)
|
||||||
return (
|
return (
|
||||||
(strict ? selectedIds.size === ids.length : true) &&
|
(strict ? selectedIds.size === ids.length : true) &&
|
||||||
ids.every((id) => selectedIds.has(id))
|
ids.every((id) => selectedIds.has(id))
|
||||||
|
@ -52,11 +52,11 @@ export function hasParent(
|
||||||
childId: string,
|
childId: string,
|
||||||
parentId: string
|
parentId: string
|
||||||
): boolean {
|
): boolean {
|
||||||
return getShape(data, childId).parentId === parentId
|
return tld.getShape(data, childId).parentId === parentId
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getOnlySelectedShape(data: Data): Shape {
|
export function getOnlySelectedShape(data: Data): Shape {
|
||||||
const selectedShapes = getSelectedShapes(data)
|
const selectedShapes = tld.getSelectedShapes(data)
|
||||||
return selectedShapes.length === 1 ? selectedShapes[0] : undefined
|
return selectedShapes.length === 1 ? selectedShapes[0] : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ export function assertShapeType(
|
||||||
shapeId: string,
|
shapeId: string,
|
||||||
type: ShapeType
|
type: ShapeType
|
||||||
): boolean {
|
): boolean {
|
||||||
const shape = getShape(data, shapeId)
|
const shape = tld.getShape(data, shapeId)
|
||||||
if (shape.type !== type) {
|
if (shape.type !== type) {
|
||||||
throw new TypeError(
|
throw new TypeError(
|
||||||
`expected shape ${shapeId} to be of type ${type}, found ${shape?.type} instead`
|
`expected shape ${shapeId} to be of type ${type}, found ${shape?.type} instead`
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { Edge, Corner } from 'types'
|
import { Edge, Corner } from 'types'
|
||||||
import { useSelector } from 'state'
|
import { useSelector } from 'state'
|
||||||
import {
|
import { getBoundsCenter, isMobile } from 'utils'
|
||||||
getBoundsCenter,
|
import tld from 'utils/tld'
|
||||||
getCurrentCamera,
|
|
||||||
getPage,
|
|
||||||
getSelectedShapes,
|
|
||||||
isMobile,
|
|
||||||
} from 'utils'
|
|
||||||
import CenterHandle from './center-handle'
|
import CenterHandle from './center-handle'
|
||||||
import CornerHandle from './corner-handle'
|
import CornerHandle from './corner-handle'
|
||||||
import EdgeHandle from './edge-handle'
|
import EdgeHandle from './edge-handle'
|
||||||
|
@ -18,23 +13,23 @@ export default function Bounds(): JSX.Element {
|
||||||
|
|
||||||
const isSelecting = useSelector((s) => s.isIn('selecting'))
|
const isSelecting = useSelector((s) => s.isIn('selecting'))
|
||||||
|
|
||||||
const zoom = useSelector((s) => getCurrentCamera(s.data).zoom)
|
const zoom = useSelector((s) => tld.getCurrentCamera(s.data).zoom)
|
||||||
|
|
||||||
const bounds = useSelector((s) => s.values.selectedBounds)
|
const bounds = useSelector((s) => s.values.selectedBounds)
|
||||||
|
|
||||||
const rotation = useSelector((s) =>
|
const rotation = useSelector((s) =>
|
||||||
s.values.selectedIds.length === 1
|
s.values.selectedIds.length === 1
|
||||||
? getSelectedShapes(s.data)[0].rotation
|
? tld.getSelectedShapes(s.data)[0].rotation
|
||||||
: 0
|
: 0
|
||||||
)
|
)
|
||||||
|
|
||||||
const isAllLocked = useSelector((s) => {
|
const isAllLocked = useSelector((s) => {
|
||||||
const page = getPage(s.data)
|
const page = tld.getPage(s.data)
|
||||||
return s.values.selectedIds.every((id) => page.shapes[id]?.isLocked)
|
return s.values.selectedIds.every((id) => page.shapes[id]?.isLocked)
|
||||||
})
|
})
|
||||||
|
|
||||||
const isSingleHandles = useSelector((s) => {
|
const isSingleHandles = useSelector((s) => {
|
||||||
const page = getPage(s.data)
|
const page = tld.getPage(s.data)
|
||||||
return (
|
return (
|
||||||
s.values.selectedIds.length === 1 &&
|
s.values.selectedIds.length === 1 &&
|
||||||
page.shapes[s.values.selectedIds[0]]?.handles !== undefined
|
page.shapes[s.values.selectedIds[0]]?.handles !== undefined
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { useRef } from 'react'
|
||||||
import state, { useSelector } from 'state'
|
import state, { useSelector } from 'state'
|
||||||
import inputs from 'state/inputs'
|
import inputs from 'state/inputs'
|
||||||
import styled from 'styles'
|
import styled from 'styles'
|
||||||
import { getPage } from 'utils'
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
function handlePointerDown(e: React.PointerEvent<SVGRectElement>) {
|
function handlePointerDown(e: React.PointerEvent<SVGRectElement>) {
|
||||||
if (!inputs.canAccept(e.pointerId)) return
|
if (!inputs.canAccept(e.pointerId)) return
|
||||||
|
@ -36,7 +36,7 @@ export default function BoundsBg(): JSX.Element {
|
||||||
|
|
||||||
if (selectedIds.length === 1) {
|
if (selectedIds.length === 1) {
|
||||||
const selected = selectedIds[0]
|
const selected = selectedIds[0]
|
||||||
const page = getPage(s.data)
|
const page = tld.getPage(s.data)
|
||||||
|
|
||||||
return page.shapes[selected]?.rotation
|
return page.shapes[selected]?.rotation
|
||||||
} else {
|
} else {
|
||||||
|
@ -48,7 +48,7 @@ export default function BoundsBg(): JSX.Element {
|
||||||
const selectedIds = s.values.selectedIds
|
const selectedIds = s.values.selectedIds
|
||||||
|
|
||||||
if (selectedIds.length === 1) {
|
if (selectedIds.length === 1) {
|
||||||
const page = getPage(s.data)
|
const page = tld.getPage(s.data)
|
||||||
const selected = selectedIds[0]
|
const selected = selectedIds[0]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -3,14 +3,14 @@ import { getShapeUtils } from 'state/shape-utils'
|
||||||
import { useRef } from 'react'
|
import { useRef } from 'react'
|
||||||
import { useSelector } from 'state'
|
import { useSelector } from 'state'
|
||||||
import styled from 'styles'
|
import styled from 'styles'
|
||||||
import { getPage } from 'utils'
|
import tld from 'utils/tld'
|
||||||
import vec from 'utils/vec'
|
import vec from 'utils/vec'
|
||||||
|
|
||||||
export default function Handles(): JSX.Element {
|
export default function Handles(): JSX.Element {
|
||||||
const shape = useSelector(
|
const shape = useSelector(
|
||||||
(s) =>
|
(s) =>
|
||||||
s.values.selectedIds.length === 1 &&
|
s.values.selectedIds.length === 1 &&
|
||||||
getPage(s.data).shapes[s.values.selectedIds[0]]
|
tld.getPage(s.data).shapes[s.values.selectedIds[0]]
|
||||||
)
|
)
|
||||||
|
|
||||||
const isSelecting = useSelector((s) =>
|
const isSelecting = useSelector((s) =>
|
||||||
|
|
|
@ -4,8 +4,9 @@ import {
|
||||||
IconWrapper,
|
IconWrapper,
|
||||||
IconButton as _IconButton,
|
IconButton as _IconButton,
|
||||||
RowButton,
|
RowButton,
|
||||||
|
breakpoints,
|
||||||
} from 'components/shared'
|
} from 'components/shared'
|
||||||
import { commandKey, deepCompareArrays, getShape, isMobile } from 'utils'
|
import { commandKey, deepCompareArrays, isMobile } from 'utils'
|
||||||
import state, { useSelector } from 'state'
|
import state, { useSelector } from 'state'
|
||||||
import {
|
import {
|
||||||
AlignType,
|
AlignType,
|
||||||
|
@ -14,6 +15,7 @@ import {
|
||||||
ShapeType,
|
ShapeType,
|
||||||
StretchType,
|
StretchType,
|
||||||
} from 'types'
|
} from 'types'
|
||||||
|
import tld from 'utils/tld'
|
||||||
import React, { useRef } from 'react'
|
import React, { useRef } from 'react'
|
||||||
import {
|
import {
|
||||||
ChevronRightIcon,
|
ChevronRightIcon,
|
||||||
|
@ -82,7 +84,9 @@ export default function ContextMenu({
|
||||||
const rContent = useRef<HTMLDivElement>(null)
|
const rContent = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
const hasGroupSelected = useSelector((s) =>
|
const hasGroupSelected = useSelector((s) =>
|
||||||
selectedShapeIds.some((id) => getShape(s.data, id).type === ShapeType.Group)
|
selectedShapeIds.some(
|
||||||
|
(id) => tld.getShape(s.data, id).type === ShapeType.Group
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
const hasTwoOrMore = selectedShapeIds.length > 1
|
const hasTwoOrMore = selectedShapeIds.length > 1
|
||||||
|
@ -300,7 +304,7 @@ function Button({
|
||||||
<_ContextMenu.Item
|
<_ContextMenu.Item
|
||||||
as={RowButton}
|
as={RowButton}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
bp={{ '@initial': 'mobile', '@sm': 'small' }}
|
bp={breakpoints}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
@ -320,7 +324,7 @@ function IconButton({
|
||||||
return (
|
return (
|
||||||
<_ContextMenu.Item
|
<_ContextMenu.Item
|
||||||
as={_IconButton}
|
as={_IconButton}
|
||||||
bp={{ '@initial': 'mobile', '@sm': 'small' }}
|
bp={breakpoints}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
>
|
>
|
||||||
|
@ -338,10 +342,7 @@ function SubMenu({
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<_ContextMenu.Root dir="ltr">
|
<_ContextMenu.Root dir="ltr">
|
||||||
<_ContextMenu.TriggerItem
|
<_ContextMenu.TriggerItem as={RowButton} bp={breakpoints}>
|
||||||
as={RowButton}
|
|
||||||
bp={{ '@initial': 'mobile', '@sm': 'small' }}
|
|
||||||
>
|
|
||||||
<span>{label}</span>
|
<span>{label}</span>
|
||||||
<IconWrapper size="small">
|
<IconWrapper size="small">
|
||||||
<ChevronRightIcon />
|
<ChevronRightIcon />
|
||||||
|
@ -363,10 +364,7 @@ function AlignDistributeSubMenu({
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<_ContextMenu.Root dir="ltr">
|
<_ContextMenu.Root dir="ltr">
|
||||||
<_ContextMenu.TriggerItem
|
<_ContextMenu.TriggerItem as={RowButton} bp={breakpoints}>
|
||||||
as={RowButton}
|
|
||||||
bp={{ '@initial': 'mobile', '@sm': 'small' }}
|
|
||||||
>
|
|
||||||
<span>Align / Distribute</span>
|
<span>Align / Distribute</span>
|
||||||
<IconWrapper size="small">
|
<IconWrapper size="small">
|
||||||
<ChevronRightIcon />
|
<ChevronRightIcon />
|
||||||
|
@ -447,10 +445,7 @@ function MoveToPageMenu() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<_ContextMenu.Root dir="ltr">
|
<_ContextMenu.Root dir="ltr">
|
||||||
<_ContextMenu.TriggerItem
|
<_ContextMenu.TriggerItem as={RowButton} bp={breakpoints}>
|
||||||
as={RowButton}
|
|
||||||
bp={{ '@initial': 'mobile', '@sm': 'small' }}
|
|
||||||
>
|
|
||||||
<span>Move To Page</span>
|
<span>Move To Page</span>
|
||||||
<IconWrapper size="small">
|
<IconWrapper size="small">
|
||||||
<ChevronRightIcon />
|
<ChevronRightIcon />
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { getShapeStyle } from 'state/shape-styles'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useSelector } from 'state'
|
import { useSelector } from 'state'
|
||||||
import { getCurrentCamera } from 'utils'
|
import tld from 'utils/tld'
|
||||||
import { DotCircle, Handle } from './misc'
|
import { DotCircle, Handle } from './misc'
|
||||||
import useShapeDef from 'hooks/useShape'
|
import useShapeDef from 'hooks/useShape'
|
||||||
import useShapesToRender from 'hooks/useShapesToRender'
|
import useShapesToRender from 'hooks/useShapesToRender'
|
||||||
|
@ -40,7 +40,7 @@ function Def({ id }: { id: string }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function ExpandDef() {
|
function ExpandDef() {
|
||||||
const zoom = useSelector((s) => getCurrentCamera(s.data).zoom)
|
const zoom = useSelector((s) => tld.getCurrentCamera(s.data).zoom)
|
||||||
return (
|
return (
|
||||||
<filter id="expand">
|
<filter id="expand">
|
||||||
<feMorphology operator="dilate" radius={2 / zoom} />
|
<feMorphology operator="dilate" radius={2 / zoom} />
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
import { getShape } from 'utils'
|
import tld from 'utils/tld'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import vec from 'utils/vec'
|
import vec from 'utils/vec'
|
||||||
import styled from 'styles'
|
import styled from 'styles'
|
||||||
|
@ -8,10 +8,10 @@ import { getShapeStyle } from 'state/shape-styles'
|
||||||
|
|
||||||
function HoveredShape({ id }: { id: string }) {
|
function HoveredShape({ id }: { id: string }) {
|
||||||
const transform = useSelector((s) => {
|
const transform = useSelector((s) => {
|
||||||
const shape = getShape(s.data, id)
|
const shape = tld.getShape(s.data, id)
|
||||||
const center = getShapeUtils(shape).getCenter(shape)
|
const center = getShapeUtils(shape).getCenter(shape)
|
||||||
const rotation = shape.rotation * (180 / Math.PI)
|
const rotation = shape.rotation * (180 / Math.PI)
|
||||||
const parentPoint = getShape(s.data, shape.parentId)?.point || [0, 0]
|
const parentPoint = tld.getShape(s.data, shape.parentId)?.point || [0, 0]
|
||||||
|
|
||||||
return `
|
return `
|
||||||
translate(${vec.neg(parentPoint)})
|
translate(${vec.neg(parentPoint)})
|
||||||
|
@ -21,7 +21,7 @@ function HoveredShape({ id }: { id: string }) {
|
||||||
})
|
})
|
||||||
|
|
||||||
const strokeWidth = useSelector((s) => {
|
const strokeWidth = useSelector((s) => {
|
||||||
const shape = getShape(s.data, id)
|
const shape = tld.getShape(s.data, id)
|
||||||
const style = getShapeStyle(shape.style)
|
const style = getShapeStyle(shape.style)
|
||||||
return +style.strokeWidth
|
return +style.strokeWidth
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import styled from 'styles'
|
import styled from 'styles'
|
||||||
import { useSelector } from 'state'
|
import { useSelector } from 'state'
|
||||||
import { deepCompareArrays, getPage } from 'utils'
|
import tld from 'utils/tld'
|
||||||
|
import { deepCompareArrays } from 'utils'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ export default function Selected(): JSX.Element {
|
||||||
export const ShapeOutline = memo(function ShapeOutline({ id }: { id: string }) {
|
export const ShapeOutline = memo(function ShapeOutline({ id }: { id: string }) {
|
||||||
// const rIndicator = useRef<SVGUseElement>(null)
|
// const rIndicator = useRef<SVGUseElement>(null)
|
||||||
|
|
||||||
const shape = useSelector((s) => getPage(s.data).shapes[id])
|
const shape = useSelector((s) => tld.getShape(s.data, id))
|
||||||
|
|
||||||
// const events = useShapeEvents(id, shape?.type === ShapeType.Group, rIndicator)
|
// const events = useShapeEvents(id, shape?.type === ShapeType.Group, rIndicator)
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@ import React, { useRef, memo, useEffect, useState } from 'react'
|
||||||
import state, { useSelector } from 'state'
|
import state, { useSelector } from 'state'
|
||||||
import styled from 'styles'
|
import styled from 'styles'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import { deepCompareArrays, getPage, getShape } from 'utils'
|
import { deepCompareArrays } from 'utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
import useShapeEvents from 'hooks/useShapeEvents'
|
import useShapeEvents from 'hooks/useShapeEvents'
|
||||||
import vec from 'utils/vec'
|
import vec from 'utils/vec'
|
||||||
import { getShapeStyle } from 'state/shape-styles'
|
import { getShapeStyle } from 'state/shape-styles'
|
||||||
|
@ -17,31 +18,31 @@ function Shape({ id, isSelecting }: ShapeProps): JSX.Element {
|
||||||
const rGroup = useRef<SVGGElement>(null)
|
const rGroup = useRef<SVGGElement>(null)
|
||||||
|
|
||||||
const isHidden = useSelector((s) => {
|
const isHidden = useSelector((s) => {
|
||||||
const shape = getShape(s.data, id)
|
const shape = tld.getShape(s.data, id)
|
||||||
return shape?.isHidden || false
|
return shape?.isHidden || false
|
||||||
})
|
})
|
||||||
|
|
||||||
const children = useSelector((s) => {
|
const children = useSelector((s) => {
|
||||||
const shape = getShape(s.data, id)
|
const shape = tld.getShape(s.data, id)
|
||||||
return shape?.children || []
|
return shape?.children || []
|
||||||
}, deepCompareArrays)
|
}, deepCompareArrays)
|
||||||
|
|
||||||
const strokeWidth = useSelector((s) => {
|
const strokeWidth = useSelector((s) => {
|
||||||
const shape = getShape(s.data, id)
|
const shape = tld.getShape(s.data, id)
|
||||||
const style = getShapeStyle(shape?.style)
|
const style = getShapeStyle(shape?.style)
|
||||||
return +style.strokeWidth
|
return +style.strokeWidth
|
||||||
})
|
})
|
||||||
|
|
||||||
const shapeUtils = useSelector((s) => {
|
const shapeUtils = useSelector((s) => {
|
||||||
const shape = getShape(s.data, id)
|
const shape = tld.getShape(s.data, id)
|
||||||
return getShapeUtils(shape)
|
return getShapeUtils(shape)
|
||||||
})
|
})
|
||||||
|
|
||||||
const transform = useSelector((s) => {
|
const transform = useSelector((s) => {
|
||||||
const shape = getShape(s.data, id)
|
const shape = tld.getShape(s.data, id)
|
||||||
const center = getShapeUtils(shape).getCenter(shape)
|
const center = getShapeUtils(shape).getCenter(shape)
|
||||||
const rotation = shape.rotation * (180 / Math.PI)
|
const rotation = shape.rotation * (180 / Math.PI)
|
||||||
const parentPoint = getShape(s.data, shape.parentId)?.point || [0, 0]
|
const parentPoint = tld.getShape(s.data, shape.parentId)?.point || [0, 0]
|
||||||
|
|
||||||
return `
|
return `
|
||||||
translate(${vec.neg(parentPoint)})
|
translate(${vec.neg(parentPoint)})
|
||||||
|
@ -121,7 +122,7 @@ const ForeignObjectHover = memo(function ForeignObjectHover({
|
||||||
id: string
|
id: string
|
||||||
}) {
|
}) {
|
||||||
const size = useSelector((s) => {
|
const size = useSelector((s) => {
|
||||||
const shape = getPage(s.data).shapes[id]
|
const shape = tld.getPage(s.data).shapes[id]
|
||||||
const bounds = getShapeUtils(shape).getBounds(shape)
|
const bounds = getShapeUtils(shape).getBounds(shape)
|
||||||
|
|
||||||
return [bounds.width, bounds.height]
|
return [bounds.width, bounds.height]
|
||||||
|
@ -202,7 +203,7 @@ function useMissingShapeTest(id: string) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return state.onUpdate((s) => {
|
return state.onUpdate((s) => {
|
||||||
if (isShape && !getShape(s.data, id)) {
|
if (isShape && !tld.getShape(s.data, id)) {
|
||||||
setIsShape(false)
|
setIsShape(false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
|
import tld from 'utils/tld'
|
||||||
|
import state, { useSelector } from 'state'
|
||||||
import { IconButton, breakpoints } from 'components/shared'
|
import { IconButton, breakpoints } from 'components/shared'
|
||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
import styled from 'styles'
|
import styled from 'styles'
|
||||||
import { MoveType } from 'types'
|
import { MoveType } from 'types'
|
||||||
import { Trash2 } from 'react-feather'
|
import { Trash2 } from 'react-feather'
|
||||||
import state, { useSelector } from 'state'
|
|
||||||
import Tooltip from 'components/tooltip'
|
import Tooltip from 'components/tooltip'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ArrowDownIcon,
|
ArrowDownIcon,
|
||||||
ArrowUpIcon,
|
ArrowUpIcon,
|
||||||
|
@ -20,7 +20,6 @@ import {
|
||||||
PinTopIcon,
|
PinTopIcon,
|
||||||
RotateCounterClockwiseIcon,
|
RotateCounterClockwiseIcon,
|
||||||
} from '@radix-ui/react-icons'
|
} from '@radix-ui/react-icons'
|
||||||
import { getPage, getSelectedIds } from 'utils'
|
|
||||||
|
|
||||||
function handleRotateCcw() {
|
function handleRotateCcw() {
|
||||||
state.send('ROTATED_CCW')
|
state.send('ROTATED_CCW')
|
||||||
|
@ -64,24 +63,24 @@ function handleDelete() {
|
||||||
|
|
||||||
function ShapesFunctions() {
|
function ShapesFunctions() {
|
||||||
const isAllLocked = useSelector((s) => {
|
const isAllLocked = useSelector((s) => {
|
||||||
const page = getPage(s.data)
|
const page = tld.getPage(s.data)
|
||||||
return s.values.selectedIds.every((id) => page.shapes[id].isLocked)
|
return s.values.selectedIds.every((id) => page.shapes[id].isLocked)
|
||||||
})
|
})
|
||||||
|
|
||||||
const isAllAspectLocked = useSelector((s) => {
|
const isAllAspectLocked = useSelector((s) => {
|
||||||
const page = getPage(s.data)
|
const page = tld.getPage(s.data)
|
||||||
return s.values.selectedIds.every(
|
return s.values.selectedIds.every(
|
||||||
(id) => page.shapes[id].isAspectRatioLocked
|
(id) => page.shapes[id].isAspectRatioLocked
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const isAllHidden = useSelector((s) => {
|
const isAllHidden = useSelector((s) => {
|
||||||
const page = getPage(s.data)
|
const page = tld.getPage(s.data)
|
||||||
return s.values.selectedIds.every((id) => page.shapes[id].isHidden)
|
return s.values.selectedIds.every((id) => page.shapes[id].isHidden)
|
||||||
})
|
})
|
||||||
|
|
||||||
const hasSelection = useSelector((s) => {
|
const hasSelection = useSelector((s) => {
|
||||||
return getSelectedIds(s.data).size > 0
|
return tld.getSelectedIds(s.data).size > 0
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -2,8 +2,8 @@ import { ZoomInIcon, ZoomOutIcon } from '@radix-ui/react-icons'
|
||||||
import { IconButton } from 'components/shared'
|
import { IconButton } from 'components/shared'
|
||||||
import state, { useSelector } from 'state'
|
import state, { useSelector } from 'state'
|
||||||
import styled from 'styles'
|
import styled from 'styles'
|
||||||
import { getCurrentCamera } from 'utils'
|
|
||||||
import Tooltip from '../tooltip'
|
import Tooltip from '../tooltip'
|
||||||
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
const zoomIn = () => state.send('ZOOMED_IN')
|
const zoomIn = () => state.send('ZOOMED_IN')
|
||||||
const zoomOut = () => state.send('ZOOMED_OUT')
|
const zoomOut = () => state.send('ZOOMED_OUT')
|
||||||
|
@ -31,7 +31,7 @@ export default function Zoom(): JSX.Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
function ZoomCounter() {
|
function ZoomCounter() {
|
||||||
const zoom = useSelector((s) => getCurrentCamera(s.data).zoom)
|
const zoom = useSelector((s) => tld.getCurrentCamera(s.data).zoom)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ZoomButton
|
<ZoomButton
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import state from 'state'
|
import state from 'state'
|
||||||
import storage from 'state/storage'
|
import storage from 'state/storage'
|
||||||
import { getCurrentCamera } from 'utils'
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the state's camera changes, update the transform of
|
* When the state's camera changes, update the transform of
|
||||||
|
@ -11,13 +11,13 @@ import { getCurrentCamera } from 'utils'
|
||||||
*/
|
*/
|
||||||
export default function useCamera(ref: React.MutableRefObject<SVGGElement>) {
|
export default function useCamera(ref: React.MutableRefObject<SVGGElement>) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let prev = getCurrentCamera(state.data)
|
let prev = tld.getCurrentCamera(state.data)
|
||||||
|
|
||||||
return state.onUpdate(() => {
|
return state.onUpdate(() => {
|
||||||
const g = ref.current
|
const g = ref.current
|
||||||
if (!g) return
|
if (!g) return
|
||||||
|
|
||||||
const { point, zoom } = getCurrentCamera(state.data)
|
const { point, zoom } = tld.getCurrentCamera(state.data)
|
||||||
|
|
||||||
if (point !== prev.point || zoom !== prev.zoom) {
|
if (point !== prev.point || zoom !== prev.zoom) {
|
||||||
g.setAttribute(
|
g.setAttribute(
|
||||||
|
@ -27,7 +27,7 @@ export default function useCamera(ref: React.MutableRefObject<SVGGElement>) {
|
||||||
|
|
||||||
storage.savePageState(state.data)
|
storage.savePageState(state.data)
|
||||||
|
|
||||||
prev = getCurrentCamera(state.data)
|
prev = tld.getCurrentCamera(state.data)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, [state])
|
}, [state])
|
||||||
|
|
|
@ -7,9 +7,8 @@ import {
|
||||||
boundsContain,
|
boundsContain,
|
||||||
debounce,
|
debounce,
|
||||||
deepCompareArrays,
|
deepCompareArrays,
|
||||||
getPageState,
|
|
||||||
getViewport,
|
|
||||||
} from 'utils'
|
} from 'utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
const viewportCache = new WeakMap<PageState, Bounds>()
|
const viewportCache = new WeakMap<PageState, Bounds>()
|
||||||
|
|
||||||
|
@ -27,10 +26,10 @@ export default function usePageShapes(): string[] {
|
||||||
|
|
||||||
// Get the shapes that fit into the current window
|
// Get the shapes that fit into the current window
|
||||||
const visiblePageShapeIds = useSelector((s) => {
|
const visiblePageShapeIds = useSelector((s) => {
|
||||||
const pageState = getPageState(s.data)
|
const pageState = tld.getPageState(s.data)
|
||||||
|
|
||||||
if (!viewportCache.has(pageState)) {
|
if (!viewportCache.has(pageState)) {
|
||||||
const viewport = getViewport(s.data)
|
const viewport = tld.getViewport(s.data)
|
||||||
viewportCache.set(pageState, viewport)
|
viewportCache.set(pageState, viewport)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
import { useSelector } from 'state'
|
import { useSelector } from 'state'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import { getShape } from 'utils'
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
export default function useShapeDef(id: string) {
|
export default function useShapeDef(id: string) {
|
||||||
return useSelector(
|
return useSelector(
|
||||||
(s) => getShape(s.data, id),
|
(s) => tld.getShape(s.data, id),
|
||||||
(prev, next) => {
|
(prev, next) => {
|
||||||
const shouldSkip = !(
|
const shouldSkip = !(
|
||||||
prev &&
|
prev &&
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { useSelector } from 'state'
|
import { useSelector } from 'state'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import { deepCompareArrays, getPage } from 'utils'
|
import { deepCompareArrays } from 'utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
export default function useShapesToRender(): string[] {
|
export default function useShapesToRender(): string[] {
|
||||||
return useSelector(
|
return useSelector(
|
||||||
(s) =>
|
(s) =>
|
||||||
Object.values(getPage(s.data).shapes)
|
Object.values(tld.getPage(s.data).shapes)
|
||||||
.filter((shape) => shape && !getShapeUtils(shape).isForeignObject)
|
.filter((shape) => shape && !getShapeUtils(shape).isForeignObject)
|
||||||
.map((shape) => shape.id),
|
.map((shape) => shape.id),
|
||||||
deepCompareArrays
|
deepCompareArrays
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { useRef } from 'react'
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
import { useRef } from 'react'
|
||||||
import state from 'state'
|
import state from 'state'
|
||||||
import inputs from 'state/inputs'
|
import inputs from 'state/inputs'
|
||||||
import vec from 'utils/vec'
|
import vec from 'utils/vec'
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
"@sentry/webpack-plugin": "^1.15.1",
|
"@sentry/webpack-plugin": "^1.15.1",
|
||||||
"@state-designer/react": "^1.7.32",
|
"@state-designer/react": "^1.7.32",
|
||||||
"@stitches/react": "^0.2.2",
|
"@stitches/react": "^0.2.2",
|
||||||
|
"@supabase/realtime-js": "^1.0.9",
|
||||||
"@types/uuid": "^8.3.0",
|
"@types/uuid": "^8.3.0",
|
||||||
"browser-fs-access": "^0.17.3",
|
"browser-fs-access": "^0.17.3",
|
||||||
"framer-motion": "^4.1.17",
|
"framer-motion": "^4.1.17",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { getShapeUtils } from './shape-utils'
|
import { getShapeUtils } from './shape-utils'
|
||||||
import { Data, Shape } from 'types'
|
import { Data, Shape } from 'types'
|
||||||
import { getCommonBounds, getSelectedShapes } from 'utils'
|
import { getCommonBounds } from 'utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
import state from './state'
|
import state from './state'
|
||||||
|
|
||||||
class Clipboard {
|
class Clipboard {
|
||||||
|
@ -47,7 +48,7 @@ class Clipboard {
|
||||||
}
|
}
|
||||||
|
|
||||||
copySelectionToSvg(data: Data) {
|
copySelectionToSvg(data: Data) {
|
||||||
const shapes = getSelectedShapes(data)
|
const shapes = tld.getSelectedShapes(data)
|
||||||
|
|
||||||
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
|
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ import {
|
||||||
SizeStyle,
|
SizeStyle,
|
||||||
CodeError,
|
CodeError,
|
||||||
} from 'types'
|
} from 'types'
|
||||||
import { getPage, getShapes } from 'utils'
|
import tld from 'utils/tld'
|
||||||
import { transform } from 'sucrase'
|
import { transform } from 'sucrase'
|
||||||
import { getErrorWithLineAndColumn, getFormattedCode } from 'utils/code'
|
import { getErrorWithLineAndColumn, getFormattedCode } from 'utils/code'
|
||||||
|
|
||||||
|
@ -89,7 +89,8 @@ export async function generateFromCode(
|
||||||
)
|
)
|
||||||
|
|
||||||
const startingChildIndex =
|
const startingChildIndex =
|
||||||
getShapes(data)
|
tld
|
||||||
|
.getShapes(data)
|
||||||
.filter((shape) => shape.parentId === data.currentPageId)
|
.filter((shape) => shape.parentId === data.currentPageId)
|
||||||
.sort((a, b) => a.childIndex - b.childIndex)[0]?.childIndex || 1
|
.sort((a, b) => a.childIndex - b.childIndex)[0]?.childIndex || 1
|
||||||
|
|
||||||
|
@ -98,7 +99,7 @@ export async function generateFromCode(
|
||||||
.map((instance, i) => ({
|
.map((instance, i) => ({
|
||||||
...instance.shape,
|
...instance.shape,
|
||||||
isGenerated: true,
|
isGenerated: true,
|
||||||
parentId: getPage(data).id,
|
parentId: tld.getPage(data).id,
|
||||||
childIndex: startingChildIndex + i,
|
childIndex: startingChildIndex + i,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
@ -141,7 +142,8 @@ export async function updateFromCode(
|
||||||
}
|
}
|
||||||
|
|
||||||
const startingChildIndex =
|
const startingChildIndex =
|
||||||
getShapes(data)
|
tld
|
||||||
|
.getShapes(data)
|
||||||
.filter((shape) => shape.parentId === data.currentPageId)
|
.filter((shape) => shape.parentId === data.currentPageId)
|
||||||
.sort((a, b) => a.childIndex - b.childIndex)[0]?.childIndex || 1
|
.sort((a, b) => a.childIndex - b.childIndex)[0]?.childIndex || 1
|
||||||
|
|
||||||
|
@ -156,7 +158,7 @@ export async function updateFromCode(
|
||||||
.map((instance, i) => ({
|
.map((instance, i) => ({
|
||||||
...instance.shape,
|
...instance.shape,
|
||||||
isGenerated: true,
|
isGenerated: true,
|
||||||
parentId: getPage(data).id,
|
parentId: tld.getPage(data).id,
|
||||||
childIndex: startingChildIndex + i,
|
childIndex: startingChildIndex + i,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { AlignType, Data } from 'types'
|
import { AlignType, Data } from 'types'
|
||||||
import { getCommonBounds, getPage, getSelectedShapes } from 'utils'
|
import { getCommonBounds } from 'utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
|
||||||
export default function alignCommand(data: Data, type: AlignType): void {
|
export default function alignCommand(data: Data, type: AlignType): void {
|
||||||
const selectedShapes = getSelectedShapes(data)
|
const selectedShapes = tld.getSelectedShapes(data)
|
||||||
const entries = selectedShapes.map(
|
const entries = selectedShapes.map(
|
||||||
(shape) => [shape.id, getShapeUtils(shape).getBounds(shape)] as const
|
(shape) => [shape.id, getShapeUtils(shape).getBounds(shape)] as const
|
||||||
)
|
)
|
||||||
|
@ -20,7 +21,7 @@ export default function alignCommand(data: Data, type: AlignType): void {
|
||||||
name: 'aligned',
|
name: 'aligned',
|
||||||
category: 'canvas',
|
category: 'canvas',
|
||||||
do(data) {
|
do(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AlignType.Top: {
|
case AlignType.Top: {
|
||||||
|
@ -86,7 +87,7 @@ export default function alignCommand(data: Data, type: AlignType): void {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
for (const id in boundsForShapes) {
|
for (const id in boundsForShapes) {
|
||||||
const shape = shapes[id]
|
const shape = shapes[id]
|
||||||
const initialBounds = boundsForShapes[id]
|
const initialBounds = boundsForShapes[id]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data } from 'types'
|
import { Data } from 'types'
|
||||||
import { getPage, getSelectedIds } from 'utils'
|
import tld from 'utils/tld'
|
||||||
import { ArrowSnapshot } from 'state/sessions/arrow-session'
|
import { ArrowSnapshot } from 'state/sessions/arrow-session'
|
||||||
|
|
||||||
export default function arrowCommand(
|
export default function arrowCommand(
|
||||||
|
@ -20,11 +20,11 @@ export default function arrowCommand(
|
||||||
|
|
||||||
const { initialShape } = after
|
const { initialShape } = after
|
||||||
|
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
page.shapes[initialShape.id] = initialShape
|
page.shapes[initialShape.id] = initialShape
|
||||||
|
|
||||||
const selectedIds = getSelectedIds(data)
|
const selectedIds = tld.getSelectedIds(data)
|
||||||
selectedIds.clear()
|
selectedIds.clear()
|
||||||
selectedIds.add(initialShape.id)
|
selectedIds.add(initialShape.id)
|
||||||
data.hoveredId = undefined
|
data.hoveredId = undefined
|
||||||
|
@ -32,11 +32,11 @@ export default function arrowCommand(
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { initialShape } = before
|
const { initialShape } = before
|
||||||
const shapes = getPage(data).shapes
|
const shapes = tld.getPage(data).shapes
|
||||||
|
|
||||||
delete shapes[initialShape.id]
|
delete shapes[initialShape.id]
|
||||||
|
|
||||||
const selectedIds = getSelectedIds(data)
|
const selectedIds = tld.getSelectedIds(data)
|
||||||
selectedIds.clear()
|
selectedIds.clear()
|
||||||
data.hoveredId = undefined
|
data.hoveredId = undefined
|
||||||
data.pointedId = undefined
|
data.pointedId = undefined
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Data } from 'types'
|
import { Data } from 'types'
|
||||||
import { getSelectedIds, setSelectedIds, setToArray } from 'utils'
|
import { setToArray } from 'utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
/* ------------------ Command Class ----------------- */
|
/* ------------------ Command Class ----------------- */
|
||||||
|
|
||||||
|
@ -84,12 +85,12 @@ export class BaseCommand<T extends any> {
|
||||||
export default class Command extends BaseCommand<Data> {
|
export default class Command extends BaseCommand<Data> {
|
||||||
saveSelectionState = (data: Data): ((next: Data) => void) => {
|
saveSelectionState = (data: Data): ((next: Data) => void) => {
|
||||||
const { currentPageId } = data
|
const { currentPageId } = data
|
||||||
const selectedIds = setToArray(getSelectedIds(data))
|
const selectedIds = setToArray(tld.getSelectedIds(data))
|
||||||
return (next: Data) => {
|
return (next: Data) => {
|
||||||
next.currentPageId = currentPageId
|
next.currentPageId = currentPageId
|
||||||
next.hoveredId = undefined
|
next.hoveredId = undefined
|
||||||
next.pointedId = undefined
|
next.pointedId = undefined
|
||||||
setSelectedIds(next, selectedIds)
|
tld.setSelectedIds(next, selectedIds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,8 @@ import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data } from 'types'
|
import { Data } from 'types'
|
||||||
import storage from 'state/storage'
|
import storage from 'state/storage'
|
||||||
import { deepClone, getPage, getPageState } from 'utils'
|
import { deepClone } from 'utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
export default function deletePage(data: Data, pageId: string): void {
|
export default function deletePage(data: Data, pageId: string): void {
|
||||||
const snapshot = getSnapshot(data, pageId)
|
const snapshot = getSnapshot(data, pageId)
|
||||||
|
@ -31,9 +32,9 @@ export default function deletePage(data: Data, pageId: string): void {
|
||||||
function getSnapshot(data: Data, pageId: string) {
|
function getSnapshot(data: Data, pageId: string) {
|
||||||
const { currentPageId, document } = data
|
const { currentPageId, document } = data
|
||||||
|
|
||||||
const page = deepClone(getPage(data))
|
const page = deepClone(tld.getPage(data))
|
||||||
|
|
||||||
const pageState = deepClone(getPageState(data))
|
const pageState = deepClone(tld.getPageState(data))
|
||||||
|
|
||||||
const isCurrent = data.currentPageId === pageId
|
const isCurrent = data.currentPageId === pageId
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,19 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data, Shape } from 'types'
|
import { Data, Shape } from 'types'
|
||||||
import {
|
import { deepClone } from 'utils'
|
||||||
deepClone,
|
import tld from 'utils/tld'
|
||||||
getDocumentBranch,
|
|
||||||
getPage,
|
|
||||||
getSelectedShapes,
|
|
||||||
setSelectedIds,
|
|
||||||
} from 'utils'
|
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
|
||||||
export default function deleteSelected(data: Data): void {
|
export default function deleteSelected(data: Data): void {
|
||||||
const selectedShapes = getSelectedShapes(data)
|
const selectedShapes = tld.getSelectedShapes(data)
|
||||||
|
|
||||||
const selectedIdsArr = selectedShapes
|
const selectedIdsArr = selectedShapes
|
||||||
.filter((shape) => !shape.isLocked)
|
.filter((shape) => !shape.isLocked)
|
||||||
.map((shape) => shape.id)
|
.map((shape) => shape.id)
|
||||||
|
|
||||||
const shapeIdsToDelete = selectedIdsArr.flatMap((id) =>
|
const shapeIdsToDelete = selectedIdsArr.flatMap((id) =>
|
||||||
getDocumentBranch(data, id)
|
tld.getDocumentBranch(data, id)
|
||||||
)
|
)
|
||||||
|
|
||||||
const remainingIds = selectedShapes
|
const remainingIds = selectedShapes
|
||||||
|
@ -35,16 +30,16 @@ export default function deleteSelected(data: Data): void {
|
||||||
manualSelection: true,
|
manualSelection: true,
|
||||||
do(data) {
|
do(data) {
|
||||||
// Update selected ids
|
// Update selected ids
|
||||||
setSelectedIds(data, remainingIds)
|
tld.setSelectedIds(data, remainingIds)
|
||||||
|
|
||||||
// Recursively delete shapes (and maybe their parents too)
|
// Recursively delete shapes (and maybe their parents too)
|
||||||
deletedShapes = deleteShapes(data, shapeIdsToDelete)
|
deletedShapes = deleteShapes(data, shapeIdsToDelete)
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
// Update selected ids
|
// Update selected ids
|
||||||
setSelectedIds(data, selectedIdsArr)
|
tld.setSelectedIds(data, selectedIdsArr)
|
||||||
|
|
||||||
// Restore deleted shapes
|
// Restore deleted shapes
|
||||||
deletedShapes.forEach((shape) => (page.shapes[shape.id] = shape))
|
deletedShapes.forEach((shape) => (page.shapes[shape.id] = shape))
|
||||||
|
@ -76,7 +71,7 @@ function deleteShapes(
|
||||||
): Shape[] {
|
): Shape[] {
|
||||||
const parentsToDelete: string[] = []
|
const parentsToDelete: string[] = []
|
||||||
|
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
const parentIds = new Set(shapeIds.map((id) => page.shapes[id].parentId))
|
const parentIds = new Set(shapeIds.map((id) => page.shapes[id].parentId))
|
||||||
|
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
import Command from './command'
|
|
||||||
import history from '../history'
|
|
||||||
import { DirectionSnapshot } from 'state/sessions/direction-session'
|
|
||||||
import { Data, LineShape, RayShape } from 'types'
|
|
||||||
import { getPage } from 'utils'
|
|
||||||
|
|
||||||
export default function directCommand(
|
|
||||||
data: Data,
|
|
||||||
before: DirectionSnapshot,
|
|
||||||
after: DirectionSnapshot
|
|
||||||
): void {
|
|
||||||
history.execute(
|
|
||||||
data,
|
|
||||||
new Command({
|
|
||||||
name: 'set_direction',
|
|
||||||
category: 'canvas',
|
|
||||||
do(data) {
|
|
||||||
const { shapes } = getPage(data)
|
|
||||||
|
|
||||||
for (const { id, direction } of after.shapes) {
|
|
||||||
const shape = shapes[id] as RayShape | LineShape
|
|
||||||
|
|
||||||
shape.direction = direction
|
|
||||||
}
|
|
||||||
},
|
|
||||||
undo(data) {
|
|
||||||
const { shapes } = getPage(data)
|
|
||||||
|
|
||||||
for (const { id, direction } of after.shapes) {
|
|
||||||
const shape = shapes[id] as RayShape | LineShape
|
|
||||||
|
|
||||||
shape.direction = direction
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,21 +1,17 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data, DistributeType } from 'types'
|
import { Data, DistributeType } from 'types'
|
||||||
import {
|
import { getBoundsCenter, getCommonBounds } from 'utils'
|
||||||
getBoundsCenter,
|
import tld from 'utils/tld'
|
||||||
getCommonBounds,
|
|
||||||
getPage,
|
|
||||||
getSelectedShapes,
|
|
||||||
} from 'utils'
|
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
|
||||||
export default function distributeCommand(
|
export default function distributeCommand(
|
||||||
data: Data,
|
data: Data,
|
||||||
type: DistributeType
|
type: DistributeType
|
||||||
): void {
|
): void {
|
||||||
const selectedShapes = getSelectedShapes(data).filter(
|
const selectedShapes = tld
|
||||||
(shape) => !shape.isLocked
|
.getSelectedShapes(data)
|
||||||
)
|
.filter((shape) => !shape.isLocked)
|
||||||
|
|
||||||
const entries = selectedShapes.map(
|
const entries = selectedShapes.map(
|
||||||
(shape) => [shape.id, getShapeUtils(shape).getBounds(shape)] as const
|
(shape) => [shape.id, getShapeUtils(shape).getBounds(shape)] as const
|
||||||
|
@ -38,7 +34,7 @@ export default function distributeCommand(
|
||||||
name: 'distribute_shapes',
|
name: 'distribute_shapes',
|
||||||
category: 'canvas',
|
category: 'canvas',
|
||||||
do(data) {
|
do(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
const len = entries.length
|
const len = entries.length
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -130,7 +126,7 @@ export default function distributeCommand(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
for (const id in boundsForShapes) {
|
for (const id in boundsForShapes) {
|
||||||
const shape = shapes[id]
|
const shape = shapes[id]
|
||||||
const initialBounds = boundsForShapes[id]
|
const initialBounds = boundsForShapes[id]
|
||||||
|
|
|
@ -2,14 +2,15 @@ import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data, PointerInfo } from 'types'
|
import { Data, PointerInfo } from 'types'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import { deepClone, getPage, getShape, updateParents } from 'utils'
|
import { deepClone } from 'utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
export default function doublePointHandleCommand(
|
export default function doublePointHandleCommand(
|
||||||
data: Data,
|
data: Data,
|
||||||
id: string,
|
id: string,
|
||||||
payload: PointerInfo
|
payload: PointerInfo
|
||||||
): void {
|
): void {
|
||||||
const initialShape = deepClone(getShape(data, id))
|
const initialShape = deepClone(tld.getShape(data, id))
|
||||||
|
|
||||||
history.execute(
|
history.execute(
|
||||||
data,
|
data,
|
||||||
|
@ -17,16 +18,16 @@ export default function doublePointHandleCommand(
|
||||||
name: 'double_point_handle',
|
name: 'double_point_handle',
|
||||||
category: 'canvas',
|
category: 'canvas',
|
||||||
do(data) {
|
do(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
const shape = shapes[id]
|
const shape = shapes[id]
|
||||||
getShapeUtils(shape).onDoublePointHandle(shape, payload.target, payload)
|
getShapeUtils(shape).onDoublePointHandle(shape, payload.target, payload)
|
||||||
updateParents(data, [id])
|
tld.updateParents(data, [id])
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
shapes[id] = initialShape
|
shapes[id] = initialShape
|
||||||
updateParents(data, [id])
|
tld.updateParents(data, [id])
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data, DrawShape } from 'types'
|
import { Data, DrawShape } from 'types'
|
||||||
import { deepClone, getPage, getShape, setSelectedIds } from 'utils'
|
import tld from 'utils/tld'
|
||||||
|
import { deepClone } from 'utils'
|
||||||
|
|
||||||
export default function drawCommand(data: Data, id: string): void {
|
export default function drawCommand(data: Data, id: string): void {
|
||||||
const restoreShape = deepClone(getShape(data, id)) as DrawShape
|
const restoreShape = deepClone(tld.getShape(data, id)) as DrawShape
|
||||||
|
|
||||||
history.execute(
|
history.execute(
|
||||||
data,
|
data,
|
||||||
|
@ -14,14 +15,14 @@ export default function drawCommand(data: Data, id: string): void {
|
||||||
manualSelection: true,
|
manualSelection: true,
|
||||||
do(data, initial) {
|
do(data, initial) {
|
||||||
if (!initial) {
|
if (!initial) {
|
||||||
getPage(data).shapes[id] = restoreShape
|
tld.getPage(data).shapes[id] = restoreShape
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedIds(data, [])
|
tld.setSelectedIds(data, [])
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
setSelectedIds(data, [])
|
tld.setSelectedIds(data, [])
|
||||||
delete getPage(data).shapes[id]
|
delete tld.getPage(data).shapes[id]
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,23 +1,21 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data } from 'types'
|
import { Data } from 'types'
|
||||||
import {
|
import { deepClone } from 'utils'
|
||||||
deepClone,
|
import tld from 'utils/tld'
|
||||||
getCurrentCamera,
|
|
||||||
getPage,
|
|
||||||
getSelectedShapes,
|
|
||||||
setSelectedIds,
|
|
||||||
} from 'utils'
|
|
||||||
import { uniqueId } from 'utils'
|
import { uniqueId } from 'utils'
|
||||||
import vec from 'utils/vec'
|
import vec from 'utils/vec'
|
||||||
|
|
||||||
export default function duplicateCommand(data: Data): void {
|
export default function duplicateCommand(data: Data): void {
|
||||||
const selectedShapes = getSelectedShapes(data).map(deepClone)
|
const selectedShapes = tld.getSelectedShapes(data).map(deepClone)
|
||||||
|
|
||||||
const duplicates = selectedShapes.map((shape) => ({
|
const duplicates = selectedShapes.map((shape) => ({
|
||||||
...shape,
|
...shape,
|
||||||
id: uniqueId(),
|
id: uniqueId(),
|
||||||
point: vec.add(shape.point, vec.div([16, 16], getCurrentCamera(data).zoom)),
|
point: vec.add(
|
||||||
|
shape.point,
|
||||||
|
vec.div([16, 16], tld.getCurrentCamera(data).zoom)
|
||||||
|
),
|
||||||
isGenerated: false,
|
isGenerated: false,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
@ -28,25 +26,25 @@ export default function duplicateCommand(data: Data): void {
|
||||||
category: 'canvas',
|
category: 'canvas',
|
||||||
manualSelection: true,
|
manualSelection: true,
|
||||||
do(data) {
|
do(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
for (const duplicate of duplicates) {
|
for (const duplicate of duplicates) {
|
||||||
shapes[duplicate.id] = duplicate
|
shapes[duplicate.id] = duplicate
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedIds(
|
tld.setSelectedIds(
|
||||||
data,
|
data,
|
||||||
duplicates.map((d) => d.id)
|
duplicates.map((d) => d.id)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
for (const duplicate of duplicates) {
|
for (const duplicate of duplicates) {
|
||||||
delete shapes[duplicate.id]
|
delete shapes[duplicate.id]
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedIds(
|
tld.setSelectedIds(
|
||||||
data,
|
data,
|
||||||
selectedShapes.map((d) => d.id)
|
selectedShapes.map((d) => d.id)
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data } from 'types'
|
import { Data } from 'types'
|
||||||
import { getPage } from 'utils'
|
import tld from 'utils/tld'
|
||||||
import { EditSnapshot } from 'state/sessions/edit-session'
|
import { EditSnapshot } from 'state/sessions/edit-session'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ export default function editCommand(
|
||||||
do(data) {
|
do(data) {
|
||||||
const { initialShape } = after
|
const { initialShape } = after
|
||||||
|
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
page.shapes[initialShape.id] = initialShape
|
page.shapes[initialShape.id] = initialShape
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ export default function editCommand(
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { initialShape } = before
|
const { initialShape } = before
|
||||||
|
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
page.shapes[initialShape.id] = initialShape
|
page.shapes[initialShape.id] = initialShape
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data, Shape } from 'types'
|
import { Data, Shape } from 'types'
|
||||||
import { deepClone, getPage, getShapes, setSelectedIds } from 'utils'
|
import { deepClone } from 'utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
export default function generateCommand(
|
export default function generateCommand(
|
||||||
data: Data,
|
data: Data,
|
||||||
generatedShapes: Shape[]
|
generatedShapes: Shape[]
|
||||||
): void {
|
): void {
|
||||||
const initialShapes = getShapes(data)
|
const initialShapes = tld
|
||||||
|
.getShapes(data)
|
||||||
.filter((shape) => shape.isGenerated)
|
.filter((shape) => shape.isGenerated)
|
||||||
.map(deepClone)
|
.map(deepClone)
|
||||||
|
|
||||||
|
@ -17,16 +19,16 @@ export default function generateCommand(
|
||||||
name: 'generate_shapes',
|
name: 'generate_shapes',
|
||||||
category: 'canvas',
|
category: 'canvas',
|
||||||
do(data) {
|
do(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
initialShapes.forEach((shape) => delete shapes[shape.id])
|
initialShapes.forEach((shape) => delete shapes[shape.id])
|
||||||
generatedShapes.forEach((shape) => (shapes[shape.id] = shape))
|
generatedShapes.forEach((shape) => (shapes[shape.id] = shape))
|
||||||
setSelectedIds(data, [])
|
tld.setSelectedIds(data, [])
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
generatedShapes.forEach((shape) => delete shapes[shape.id])
|
generatedShapes.forEach((shape) => delete shapes[shape.id])
|
||||||
initialShapes.forEach((shape) => (shapes[shape.id] = shape))
|
initialShapes.forEach((shape) => (shapes[shape.id] = shape))
|
||||||
setSelectedIds(data, [])
|
tld.setSelectedIds(data, [])
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data, GroupShape, ShapeType } from 'types'
|
import { Data, GroupShape, ShapeType } from 'types'
|
||||||
import {
|
import { getCommonBounds } from 'utils'
|
||||||
getCommonBounds,
|
|
||||||
getPage,
|
|
||||||
getSelectedIds,
|
|
||||||
getSelectedShapes,
|
|
||||||
getShape,
|
|
||||||
setSelectedIds,
|
|
||||||
} from 'utils'
|
|
||||||
import { current } from 'immer'
|
import { current } from 'immer'
|
||||||
|
import tld from 'utils/tld'
|
||||||
import { createShape, getShapeUtils } from 'state/shape-utils'
|
import { createShape, getShapeUtils } from 'state/shape-utils'
|
||||||
import commands from '.'
|
import commands from '.'
|
||||||
|
|
||||||
|
@ -17,11 +11,11 @@ export default function groupCommand(data: Data): void {
|
||||||
const cData = current(data)
|
const cData = current(data)
|
||||||
const { currentPageId } = cData
|
const { currentPageId } = cData
|
||||||
|
|
||||||
const oldSelectedIds = getSelectedIds(cData)
|
const oldSelectedIds = tld.getSelectedIds(cData)
|
||||||
|
|
||||||
const initialShapes = getSelectedShapes(cData).sort(
|
const initialShapes = tld
|
||||||
(a, b) => a.childIndex - b.childIndex
|
.getSelectedShapes(cData)
|
||||||
)
|
.sort((a, b) => a.childIndex - b.childIndex)
|
||||||
|
|
||||||
const isAllSameParent = initialShapes.every(
|
const isAllSameParent = initialShapes.every(
|
||||||
(shape, i) => i === 0 || shape.parentId === initialShapes[i - 1].parentId
|
(shape, i) => i === 0 || shape.parentId === initialShapes[i - 1].parentId
|
||||||
|
@ -43,7 +37,7 @@ export default function groupCommand(data: Data): void {
|
||||||
newGroupParentId = currentPageId
|
newGroupParentId = currentPageId
|
||||||
} else {
|
} else {
|
||||||
// Are all of the parent's children selected?
|
// Are all of the parent's children selected?
|
||||||
const parent = getShape(data, parentId) as GroupShape
|
const parent = tld.getShape(data, parentId) as GroupShape
|
||||||
|
|
||||||
if (parent.children.length === initialShapes.length) {
|
if (parent.children.length === initialShapes.length) {
|
||||||
// !!! Hey! We're not going any further. We need to ungroup those shapes.
|
// !!! Hey! We're not going any further. We need to ungroup those shapes.
|
||||||
|
@ -82,7 +76,7 @@ export default function groupCommand(data: Data): void {
|
||||||
category: 'canvas',
|
category: 'canvas',
|
||||||
manualSelection: true,
|
manualSelection: true,
|
||||||
do(data) {
|
do(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
// Create the new group
|
// Create the new group
|
||||||
shapes[newGroupShape.id] = newGroupShape
|
shapes[newGroupShape.id] = newGroupShape
|
||||||
|
@ -115,10 +109,10 @@ export default function groupCommand(data: Data): void {
|
||||||
.setProperty(shape, 'parentId', newGroupShape.id)
|
.setProperty(shape, 'parentId', newGroupShape.id)
|
||||||
})
|
})
|
||||||
|
|
||||||
setSelectedIds(data, [newGroupShape.id])
|
tld.setSelectedIds(data, [newGroupShape.id])
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
const group = shapes[newGroupShape.id]
|
const group = shapes[newGroupShape.id]
|
||||||
|
|
||||||
|
@ -152,7 +146,7 @@ export default function groupCommand(data: Data): void {
|
||||||
delete shapes[newGroupShape.id]
|
delete shapes[newGroupShape.id]
|
||||||
|
|
||||||
// Reselect the children of the group
|
// Reselect the children of the group
|
||||||
setSelectedIds(data, initialShapeIds)
|
tld.setSelectedIds(data, initialShapeIds)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -163,5 +157,5 @@ function getShapeDepth(data: Data, id: string, depth = 0) {
|
||||||
return depth
|
return depth
|
||||||
}
|
}
|
||||||
|
|
||||||
return getShapeDepth(data, getShape(data, id).parentId, depth + 1)
|
return getShapeDepth(data, tld.getShape(data, id).parentId, depth + 1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data } from 'types'
|
import { Data } from 'types'
|
||||||
import { getPage } from 'utils'
|
import tld from 'utils/tld'
|
||||||
import { HandleSnapshot } from 'state/sessions/handle-session'
|
import { HandleSnapshot } from 'state/sessions/handle-session'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ export default function handleCommand(
|
||||||
do(data) {
|
do(data) {
|
||||||
const { initialShape } = after
|
const { initialShape } = after
|
||||||
|
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
const shape = page.shapes[initialShape.id]
|
const shape = page.shapes[initialShape.id]
|
||||||
|
|
||||||
getShapeUtils(shape)
|
getShapeUtils(shape)
|
||||||
|
@ -28,7 +28,7 @@ export default function handleCommand(
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { initialShape } = before
|
const { initialShape } = before
|
||||||
|
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
page.shapes[initialShape.id] = initialShape
|
page.shapes[initialShape.id] = initialShape
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,7 +4,6 @@ import changePage from './change-page'
|
||||||
import createPage from './create-page'
|
import createPage from './create-page'
|
||||||
import deletePage from './delete-page'
|
import deletePage from './delete-page'
|
||||||
import deleteSelected from './delete-selected'
|
import deleteSelected from './delete-selected'
|
||||||
import direct from './direct'
|
|
||||||
import distribute from './distribute'
|
import distribute from './distribute'
|
||||||
import doublePointHandle from './double-point-handle'
|
import doublePointHandle from './double-point-handle'
|
||||||
import draw from './draw'
|
import draw from './draw'
|
||||||
|
@ -36,7 +35,6 @@ const commands = {
|
||||||
createPage,
|
createPage,
|
||||||
deletePage,
|
deletePage,
|
||||||
deleteSelected,
|
deleteSelected,
|
||||||
direct,
|
|
||||||
distribute,
|
distribute,
|
||||||
doublePointHandle,
|
doublePointHandle,
|
||||||
draw,
|
draw,
|
||||||
|
|
|
@ -1,24 +1,18 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data } from 'types'
|
import { Data } from 'types'
|
||||||
import {
|
import { setToArray, uniqueArray } from 'utils'
|
||||||
getDocumentBranch,
|
import tld from 'utils/tld'
|
||||||
getPage,
|
|
||||||
getPageState,
|
|
||||||
getSelectedIds,
|
|
||||||
setToArray,
|
|
||||||
uniqueArray,
|
|
||||||
} from 'utils'
|
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import storage from 'state/storage'
|
import storage from 'state/storage'
|
||||||
|
|
||||||
export default function moveToPageCommand(data: Data, newPageId: string): void {
|
export default function moveToPageCommand(data: Data, newPageId: string): void {
|
||||||
const { currentPageId: oldPageId } = data
|
const { currentPageId: oldPageId } = data
|
||||||
const oldPage = getPage(data)
|
const oldPage = tld.getPage(data)
|
||||||
const selectedIds = setToArray(getSelectedIds(data))
|
const selectedIds = setToArray(tld.getSelectedIds(data))
|
||||||
|
|
||||||
const idsToMove = uniqueArray(
|
const idsToMove = uniqueArray(
|
||||||
...selectedIds.flatMap((id) => getDocumentBranch(data, id))
|
...selectedIds.flatMap((id) => tld.getDocumentBranch(data, id))
|
||||||
)
|
)
|
||||||
|
|
||||||
const oldParentIds = Object.fromEntries(
|
const oldParentIds = Object.fromEntries(
|
||||||
|
@ -39,7 +33,7 @@ export default function moveToPageCommand(data: Data, newPageId: string): void {
|
||||||
const fromPageId = oldPageId
|
const fromPageId = oldPageId
|
||||||
const toPageId = newPageId
|
const toPageId = newPageId
|
||||||
|
|
||||||
const fromPage = getPage(data)
|
const fromPage = tld.getPage(data)
|
||||||
|
|
||||||
// Get all of the selected shapes and their descendents
|
// Get all of the selected shapes and their descendents
|
||||||
const shapesToMove = idsToMove.map((id) => fromPage.shapes[id])
|
const shapesToMove = idsToMove.map((id) => fromPage.shapes[id])
|
||||||
|
@ -65,7 +59,7 @@ export default function moveToPageCommand(data: Data, newPageId: string): void {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Clear the current page state's selected ids
|
// Clear the current page state's selected ids
|
||||||
getPageState(data).selectedIds.clear()
|
tld.getPageState(data).selectedIds.clear()
|
||||||
|
|
||||||
// Save the "from" page
|
// Save the "from" page
|
||||||
storage.savePage(data, data.document.id, fromPageId)
|
storage.savePage(data, data.document.id, fromPageId)
|
||||||
|
@ -74,7 +68,7 @@ export default function moveToPageCommand(data: Data, newPageId: string): void {
|
||||||
storage.loadPage(data, toPageId)
|
storage.loadPage(data, toPageId)
|
||||||
|
|
||||||
// The page we're moving the shapes to
|
// The page we're moving the shapes to
|
||||||
const toPage = getPage(data)
|
const toPage = tld.getPage(data)
|
||||||
|
|
||||||
// Add all of the selected shapes to the "from" page.
|
// Add all of the selected shapes to the "from" page.
|
||||||
shapesToMove.forEach((shape) => {
|
shapesToMove.forEach((shape) => {
|
||||||
|
@ -89,7 +83,7 @@ export default function moveToPageCommand(data: Data, newPageId: string): void {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Select the selected ids on the new page
|
// Select the selected ids on the new page
|
||||||
getPageState(data).selectedIds = new Set(selectedIds)
|
tld.getPageState(data).selectedIds = new Set(selectedIds)
|
||||||
|
|
||||||
// Move to the new page
|
// Move to the new page
|
||||||
data.currentPageId = toPageId
|
data.currentPageId = toPageId
|
||||||
|
@ -98,7 +92,7 @@ export default function moveToPageCommand(data: Data, newPageId: string): void {
|
||||||
const fromPageId = newPageId
|
const fromPageId = newPageId
|
||||||
const toPageId = oldPageId
|
const toPageId = oldPageId
|
||||||
|
|
||||||
const fromPage = getPage(data)
|
const fromPage = tld.getPage(data)
|
||||||
|
|
||||||
const shapesToMove = idsToMove.map((id) => fromPage.shapes[id])
|
const shapesToMove = idsToMove.map((id) => fromPage.shapes[id])
|
||||||
|
|
||||||
|
@ -119,13 +113,13 @@ export default function moveToPageCommand(data: Data, newPageId: string): void {
|
||||||
delete fromPage.shapes[shape.id]
|
delete fromPage.shapes[shape.id]
|
||||||
})
|
})
|
||||||
|
|
||||||
getPageState(data).selectedIds.clear()
|
tld.getPageState(data).selectedIds.clear()
|
||||||
|
|
||||||
storage.savePage(data, data.document.id, fromPageId)
|
storage.savePage(data, data.document.id, fromPageId)
|
||||||
|
|
||||||
storage.loadPage(data, toPageId)
|
storage.loadPage(data, toPageId)
|
||||||
|
|
||||||
const toPage = getPage(data)
|
const toPage = tld.getPage(data)
|
||||||
|
|
||||||
shapesToMove.forEach((shape) => {
|
shapesToMove.forEach((shape) => {
|
||||||
toPage.shapes[shape.id] = shape
|
toPage.shapes[shape.id] = shape
|
||||||
|
@ -144,7 +138,7 @@ export default function moveToPageCommand(data: Data, newPageId: string): void {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
getPageState(data).selectedIds = new Set(selectedIds)
|
tld.getPageState(data).selectedIds = new Set(selectedIds)
|
||||||
|
|
||||||
data.currentPageId = toPageId
|
data.currentPageId = toPageId
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,19 +1,14 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data, MoveType, Shape } from 'types'
|
import { Data, MoveType, Shape } from 'types'
|
||||||
import {
|
import { setToArray } from 'utils'
|
||||||
forceIntegerChildIndices,
|
import tld from 'utils/tld'
|
||||||
getChildren,
|
|
||||||
getPage,
|
|
||||||
getSelectedIds,
|
|
||||||
setToArray,
|
|
||||||
} from 'utils'
|
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
|
||||||
export default function moveCommand(data: Data, type: MoveType): void {
|
export default function moveCommand(data: Data, type: MoveType): void {
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
const selectedIds = setToArray(getSelectedIds(data))
|
const selectedIds = setToArray(tld.getSelectedIds(data))
|
||||||
|
|
||||||
const initialIndices = Object.fromEntries(
|
const initialIndices = Object.fromEntries(
|
||||||
selectedIds.map((id) => [id, page.shapes[id].childIndex])
|
selectedIds.map((id) => [id, page.shapes[id].childIndex])
|
||||||
|
@ -26,7 +21,7 @@ export default function moveCommand(data: Data, type: MoveType): void {
|
||||||
category: 'canvas',
|
category: 'canvas',
|
||||||
manualSelection: true,
|
manualSelection: true,
|
||||||
do(data) {
|
do(data) {
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
const shapes = selectedIds.map((id) => page.shapes[id])
|
const shapes = selectedIds.map((id) => page.shapes[id])
|
||||||
|
|
||||||
|
@ -44,20 +39,20 @@ export default function moveCommand(data: Data, type: MoveType): void {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MoveType.ToFront: {
|
case MoveType.ToFront: {
|
||||||
for (const id in shapesByParentId) {
|
for (const id in shapesByParentId) {
|
||||||
moveToFront(shapesByParentId[id], getChildren(data, id))
|
moveToFront(shapesByParentId[id], tld.getChildren(data, id))
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case MoveType.ToBack: {
|
case MoveType.ToBack: {
|
||||||
for (const id in shapesByParentId) {
|
for (const id in shapesByParentId) {
|
||||||
moveToBack(shapesByParentId[id], getChildren(data, id))
|
moveToBack(shapesByParentId[id], tld.getChildren(data, id))
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case MoveType.Forward: {
|
case MoveType.Forward: {
|
||||||
for (const id in shapesByParentId) {
|
for (const id in shapesByParentId) {
|
||||||
const visited = new Set<string>()
|
const visited = new Set<string>()
|
||||||
const siblings = getChildren(data, id)
|
const siblings = tld.getChildren(data, id)
|
||||||
shapesByParentId[id]
|
shapesByParentId[id]
|
||||||
.sort((a, b) => b.childIndex - a.childIndex)
|
.sort((a, b) => b.childIndex - a.childIndex)
|
||||||
.forEach((shape) => moveForward(shape, siblings, visited))
|
.forEach((shape) => moveForward(shape, siblings, visited))
|
||||||
|
@ -67,7 +62,7 @@ export default function moveCommand(data: Data, type: MoveType): void {
|
||||||
case MoveType.Backward: {
|
case MoveType.Backward: {
|
||||||
for (const id in shapesByParentId) {
|
for (const id in shapesByParentId) {
|
||||||
const visited = new Set<string>()
|
const visited = new Set<string>()
|
||||||
const siblings = getChildren(data, id)
|
const siblings = tld.getChildren(data, id)
|
||||||
shapesByParentId[id]
|
shapesByParentId[id]
|
||||||
.sort((a, b) => a.childIndex - b.childIndex)
|
.sort((a, b) => a.childIndex - b.childIndex)
|
||||||
.forEach((shape) => moveBackward(shape, siblings, visited))
|
.forEach((shape) => moveBackward(shape, siblings, visited))
|
||||||
|
@ -77,7 +72,7 @@ export default function moveCommand(data: Data, type: MoveType): void {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
for (const id of selectedIds) {
|
for (const id of selectedIds) {
|
||||||
const shape = page.shapes[id]
|
const shape = page.shapes[id]
|
||||||
|
@ -143,7 +138,7 @@ function moveForward(shape: Shape, siblings: Shape[], visited: Set<string>) {
|
||||||
: Math.ceil(nextSibling.childIndex + 1)
|
: Math.ceil(nextSibling.childIndex + 1)
|
||||||
|
|
||||||
if (nextIndex === nextSibling.childIndex) {
|
if (nextIndex === nextSibling.childIndex) {
|
||||||
forceIntegerChildIndices(siblings)
|
tld.forceIntegerChildIndices(siblings)
|
||||||
|
|
||||||
nextIndex = nextNextSibling
|
nextIndex = nextNextSibling
|
||||||
? (nextSibling.childIndex + nextNextSibling.childIndex) / 2
|
? (nextSibling.childIndex + nextNextSibling.childIndex) / 2
|
||||||
|
@ -169,7 +164,7 @@ function moveBackward(shape: Shape, siblings: Shape[], visited: Set<string>) {
|
||||||
: nextSibling.childIndex / 2
|
: nextSibling.childIndex / 2
|
||||||
|
|
||||||
if (shape.childIndex === nextSibling.childIndex) {
|
if (shape.childIndex === nextSibling.childIndex) {
|
||||||
forceIntegerChildIndices(siblings)
|
tld.forceIntegerChildIndices(siblings)
|
||||||
|
|
||||||
nextNextSibling
|
nextNextSibling
|
||||||
? (nextSibling.childIndex + nextNextSibling.childIndex) / 2
|
? (nextSibling.childIndex + nextNextSibling.childIndex) / 2
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data, Shape } from 'types'
|
import { Data, Shape } from 'types'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import { getPage, updateParents } from 'utils'
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
// Used when changing the properties of one or more shapes,
|
// Used when changing the properties of one or more shapes,
|
||||||
// without changing selection or deleting any shapes.
|
// without changing selection or deleting any shapes.
|
||||||
|
@ -19,27 +19,27 @@ export default function mutateShapesCommand(
|
||||||
name,
|
name,
|
||||||
category: 'canvas',
|
category: 'canvas',
|
||||||
do(data) {
|
do(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
after.forEach((shape) => {
|
after.forEach((shape) => {
|
||||||
shapes[shape.id] = shape
|
shapes[shape.id] = shape
|
||||||
getShapeUtils(shape).onSessionComplete(shape)
|
getShapeUtils(shape).onSessionComplete(shape)
|
||||||
})
|
})
|
||||||
|
|
||||||
// updateParents(
|
tld.updateParents(
|
||||||
// data,
|
data,
|
||||||
// after.map((shape) => shape.id)
|
after.map((shape) => shape.id)
|
||||||
// )
|
)
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
before.forEach((shape) => {
|
before.forEach((shape) => {
|
||||||
shapes[shape.id] = shape
|
shapes[shape.id] = shape
|
||||||
getShapeUtils(shape).onSessionComplete(shape)
|
getShapeUtils(shape).onSessionComplete(shape)
|
||||||
})
|
})
|
||||||
|
|
||||||
updateParents(
|
tld.updateParents(
|
||||||
data,
|
data,
|
||||||
before.map((shape) => shape.id)
|
before.map((shape) => shape.id)
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,17 +1,11 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data } from 'types'
|
import { Data } from 'types'
|
||||||
import { getPage, getSelectedShapes } from 'utils'
|
import tld from 'utils/tld'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
|
||||||
import vec from 'utils/vec'
|
import vec from 'utils/vec'
|
||||||
|
|
||||||
export default function nudgeCommand(data: Data, delta: number[]): void {
|
export default function nudgeCommand(data: Data, delta: number[]): void {
|
||||||
const selectedShapes = getSelectedShapes(data)
|
const initialShapes = tld.getSelectedShapeSnapshot(data, () => null)
|
||||||
const shapeBounds = Object.fromEntries(
|
|
||||||
selectedShapes.map(
|
|
||||||
(shape) => [shape.id, getShapeUtils(shape).getBounds(shape)] as const
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
history.execute(
|
history.execute(
|
||||||
data,
|
data,
|
||||||
|
@ -19,28 +13,22 @@ export default function nudgeCommand(data: Data, delta: number[]): void {
|
||||||
name: 'nudge_shapes',
|
name: 'nudge_shapes',
|
||||||
category: 'canvas',
|
category: 'canvas',
|
||||||
do(data) {
|
do(data) {
|
||||||
const { shapes } = getPage(data)
|
tld.mutateShapes(
|
||||||
|
data,
|
||||||
for (const id in shapeBounds) {
|
initialShapes.map((shape) => shape.id),
|
||||||
const shape = shapes[id]
|
(shape, utils) => {
|
||||||
getShapeUtils(shape).setProperty(
|
utils.setProperty(shape, 'point', vec.add(shape.point, delta))
|
||||||
shape,
|
}
|
||||||
'point',
|
)
|
||||||
vec.add(shape.point, delta)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { shapes } = getPage(data)
|
tld.mutateShapes(
|
||||||
|
data,
|
||||||
for (const id in shapeBounds) {
|
initialShapes.map((shape) => shape.id),
|
||||||
const shape = shapes[id]
|
(shape, utils) => {
|
||||||
getShapeUtils(shape).setProperty(
|
utils.setProperty(shape, 'point', vec.sub(shape.point, delta))
|
||||||
shape,
|
}
|
||||||
'point',
|
)
|
||||||
vec.sub(shape.point, delta)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,21 +1,15 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data, Shape } from 'types'
|
import { Data, Shape } from 'types'
|
||||||
import {
|
import { getCommonBounds, setToArray } from 'utils'
|
||||||
getCommonBounds,
|
import tld from 'utils/tld'
|
||||||
getPage,
|
|
||||||
getSelectedIds,
|
|
||||||
screenToWorld,
|
|
||||||
setSelectedIds,
|
|
||||||
setToArray,
|
|
||||||
} from 'utils'
|
|
||||||
import { uniqueId } from 'utils'
|
import { uniqueId } from 'utils'
|
||||||
import vec from 'utils/vec'
|
import vec from 'utils/vec'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import state from 'state/state'
|
import state from 'state/state'
|
||||||
|
|
||||||
export default function pasteCommand(data: Data, initialShapes: Shape[]): void {
|
export default function pasteCommand(data: Data, initialShapes: Shape[]): void {
|
||||||
const center = screenToWorld(
|
const center = tld.screenToWorld(
|
||||||
[window.innerWidth / 2, window.innerHeight / 2],
|
[window.innerWidth / 2, window.innerHeight / 2],
|
||||||
data
|
data
|
||||||
)
|
)
|
||||||
|
@ -32,7 +26,7 @@ export default function pasteCommand(data: Data, initialShapes: Shape[]): void {
|
||||||
initialShapes.map((shape) => [shape.id, uniqueId()])
|
initialShapes.map((shape) => [shape.id, uniqueId()])
|
||||||
)
|
)
|
||||||
|
|
||||||
const oldSelectedIds = setToArray(getSelectedIds(data))
|
const oldSelectedIds = setToArray(tld.getSelectedIds(data))
|
||||||
|
|
||||||
history.execute(
|
history.execute(
|
||||||
data,
|
data,
|
||||||
|
@ -41,7 +35,7 @@ export default function pasteCommand(data: Data, initialShapes: Shape[]): void {
|
||||||
category: 'canvas',
|
category: 'canvas',
|
||||||
manualSelection: true,
|
manualSelection: true,
|
||||||
do(data) {
|
do(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
let childIndex =
|
let childIndex =
|
||||||
(state.values.currentShapes[state.values.currentShapes.length - 1]
|
(state.values.currentShapes[state.values.currentShapes.length - 1]
|
||||||
|
@ -62,14 +56,14 @@ export default function pasteCommand(data: Data, initialShapes: Shape[]): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedIds(data, Object.values(newIdMap))
|
tld.setSelectedIds(data, Object.values(newIdMap))
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
Object.values(newIdMap).forEach((id) => delete shapes[id])
|
Object.values(newIdMap).forEach((id) => delete shapes[id])
|
||||||
|
|
||||||
setSelectedIds(data, oldSelectedIds)
|
tld.setSelectedIds(data, oldSelectedIds)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data } from 'types'
|
import { Data } from 'types'
|
||||||
import { getPage, getSelectedShapes, updateParents } from 'utils'
|
import tld from 'utils/tld'
|
||||||
import { current } from 'immer'
|
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
|
||||||
|
|
||||||
export default function resetBoundsCommand(data: Data): void {
|
export default function resetBoundsCommand(data: Data): void {
|
||||||
const initialShapes = Object.fromEntries(
|
const initialShapes = tld.getSelectedShapeSnapshot(data)
|
||||||
getSelectedShapes(current(data)).map((shape) => [shape.id, shape])
|
|
||||||
)
|
|
||||||
|
|
||||||
history.execute(
|
history.execute(
|
||||||
data,
|
data,
|
||||||
|
@ -16,21 +12,18 @@ export default function resetBoundsCommand(data: Data): void {
|
||||||
name: 'reset_bounds',
|
name: 'reset_bounds',
|
||||||
category: 'canvas',
|
category: 'canvas',
|
||||||
do(data) {
|
do(data) {
|
||||||
getSelectedShapes(data).forEach((shape) => {
|
tld.mutateShapes(
|
||||||
if (shape.isLocked) return
|
data,
|
||||||
getShapeUtils(shape).onBoundsReset(shape)
|
initialShapes.map((shape) => shape.id),
|
||||||
})
|
(shape, utils) => void utils.onBoundsReset(shape)
|
||||||
|
)
|
||||||
updateParents(data, Object.keys(initialShapes))
|
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const page = getPage(data)
|
tld.mutateShapes(
|
||||||
getSelectedShapes(data).forEach((shape) => {
|
data,
|
||||||
if (shape.isLocked) return
|
initialShapes.map((shape) => shape.id),
|
||||||
page.shapes[shape.id] = initialShapes[shape.id]
|
(_, __, i) => initialShapes[i]
|
||||||
})
|
)
|
||||||
|
|
||||||
updateParents(data, Object.keys(initialShapes))
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data } from 'types'
|
import { Data } from 'types'
|
||||||
import {
|
import { getBoundsCenter, getCommonBounds } from 'utils'
|
||||||
getBoundsCenter,
|
import tld from 'utils/tld'
|
||||||
getCommonBounds,
|
|
||||||
getPage,
|
|
||||||
getSelectedShapes,
|
|
||||||
} from 'utils'
|
|
||||||
import vec from 'utils/vec'
|
import vec from 'utils/vec'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
|
||||||
|
@ -15,10 +11,10 @@ const PI2 = Math.PI * 2
|
||||||
export default function rotateCcwCommand(data: Data): void {
|
export default function rotateCcwCommand(data: Data): void {
|
||||||
const { boundsRotation } = data
|
const { boundsRotation } = data
|
||||||
|
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
const initialShapes = Object.fromEntries(
|
const initialShapes = Object.fromEntries(
|
||||||
getSelectedShapes(data).map((shape) => {
|
tld.getSelectedShapes(data).map((shape) => {
|
||||||
const bounds = getShapeUtils(shape).getBounds(shape)
|
const bounds = getShapeUtils(shape).getBounds(shape)
|
||||||
return [
|
return [
|
||||||
shape.id,
|
shape.id,
|
||||||
|
@ -63,7 +59,7 @@ export default function rotateCcwCommand(data: Data): void {
|
||||||
name: 'rotate_ccw',
|
name: 'rotate_ccw',
|
||||||
category: 'canvas',
|
category: 'canvas',
|
||||||
do(data) {
|
do(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
for (const id in nextShapes) {
|
for (const id in nextShapes) {
|
||||||
const shape = shapes[id]
|
const shape = shapes[id]
|
||||||
|
@ -77,7 +73,7 @@ export default function rotateCcwCommand(data: Data): void {
|
||||||
data.boundsRotation = nextboundsRotation
|
data.boundsRotation = nextboundsRotation
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
for (const id in initialShapes) {
|
for (const id in initialShapes) {
|
||||||
const { point, rotation } = initialShapes[id]
|
const { point, rotation } = initialShapes[id]
|
||||||
|
|
|
@ -2,8 +2,8 @@ import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data } from 'types'
|
import { Data } from 'types'
|
||||||
import { RotateSnapshot } from 'state/sessions/rotate-session'
|
import { RotateSnapshot } from 'state/sessions/rotate-session'
|
||||||
import { getPage } from 'utils'
|
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
export default function rotateCommand(
|
export default function rotateCommand(
|
||||||
data: Data,
|
data: Data,
|
||||||
|
@ -16,7 +16,7 @@ export default function rotateCommand(
|
||||||
name: 'rotate_shapes',
|
name: 'rotate_shapes',
|
||||||
category: 'canvas',
|
category: 'canvas',
|
||||||
do(data) {
|
do(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
for (const { id, point, rotation } of after.initialShapes) {
|
for (const { id, point, rotation } of after.initialShapes) {
|
||||||
const shape = shapes[id]
|
const shape = shapes[id]
|
||||||
|
@ -29,7 +29,7 @@ export default function rotateCommand(
|
||||||
data.boundsRotation = after.boundsRotation
|
data.boundsRotation = after.boundsRotation
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
for (const { id, point, rotation } of before.initialShapes) {
|
for (const { id, point, rotation } of before.initialShapes) {
|
||||||
const shape = shapes[id]
|
const shape = shapes[id]
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { StretchType, Data, Corner } from 'types'
|
import { StretchType, Data, Corner } from 'types'
|
||||||
import { deepClone, getCommonBounds, getPage, getSelectedShapes } from 'utils'
|
import { deepClone, getCommonBounds } from 'utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
|
||||||
export default function stretchCommand(data: Data, type: StretchType): void {
|
export default function stretchCommand(data: Data, type: StretchType): void {
|
||||||
const initialShapes = getSelectedShapes(data).map((shape) => deepClone(shape))
|
const initialShapes = tld
|
||||||
|
.getSelectedShapes(data)
|
||||||
|
.map((shape) => deepClone(shape))
|
||||||
|
|
||||||
const snapshot = Object.fromEntries(
|
const snapshot = Object.fromEntries(
|
||||||
initialShapes.map((shape) => [
|
initialShapes.map((shape) => [
|
||||||
|
@ -27,7 +30,7 @@ export default function stretchCommand(data: Data, type: StretchType): void {
|
||||||
name: 'stretched_shapes',
|
name: 'stretched_shapes',
|
||||||
category: 'canvas',
|
category: 'canvas',
|
||||||
do(data) {
|
do(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case StretchType.Horizontal: {
|
case StretchType.Horizontal: {
|
||||||
|
@ -75,7 +78,7 @@ export default function stretchCommand(data: Data, type: StretchType): void {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
initialShapes.forEach((shape) => (shapes[shape.id] = shape))
|
initialShapes.forEach((shape) => (shapes[shape.id] = shape))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data, ShapeStyles } from 'types'
|
import { Data, ShapeStyles } from 'types'
|
||||||
import { getDocumentBranch, getPage, getSelectedIds, setToArray } from 'utils'
|
import tld from 'utils/tld'
|
||||||
|
import { setToArray } from 'utils'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import { current } from 'immer'
|
import { current } from 'immer'
|
||||||
|
|
||||||
|
@ -10,12 +11,12 @@ export default function styleCommand(
|
||||||
styles: Partial<ShapeStyles>
|
styles: Partial<ShapeStyles>
|
||||||
): void {
|
): void {
|
||||||
const cData = current(data)
|
const cData = current(data)
|
||||||
const page = getPage(cData)
|
const page = tld.getPage(cData)
|
||||||
|
|
||||||
const selectedIds = setToArray(getSelectedIds(data))
|
const selectedIds = setToArray(tld.getSelectedIds(data))
|
||||||
|
|
||||||
const shapesToStyle = selectedIds
|
const shapesToStyle = selectedIds
|
||||||
.flatMap((id) => getDocumentBranch(data, id))
|
.flatMap((id) => tld.getDocumentBranch(data, id))
|
||||||
.map((id) => page.shapes[id])
|
.map((id) => page.shapes[id])
|
||||||
|
|
||||||
history.execute(
|
history.execute(
|
||||||
|
@ -25,7 +26,7 @@ export default function styleCommand(
|
||||||
category: 'canvas',
|
category: 'canvas',
|
||||||
manualSelection: true,
|
manualSelection: true,
|
||||||
do(data) {
|
do(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
for (const { id } of shapesToStyle) {
|
for (const { id } of shapesToStyle) {
|
||||||
const shape = shapes[id]
|
const shape = shapes[id]
|
||||||
|
@ -33,7 +34,7 @@ export default function styleCommand(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
for (const { id, style } of shapesToStyle) {
|
for (const { id, style } of shapesToStyle) {
|
||||||
const shape = shapes[id]
|
const shape = shapes[id]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data, Shape } from 'types'
|
import { Data, Shape } from 'types'
|
||||||
import { getPage, getSelectedShapes } from 'utils'
|
import tld from 'utils/tld'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import { PropsOfType } from 'types'
|
import { PropsOfType } from 'types'
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ export default function toggleCommand(
|
||||||
data: Data,
|
data: Data,
|
||||||
prop: PropsOfType<Shape>
|
prop: PropsOfType<Shape>
|
||||||
): void {
|
): void {
|
||||||
const selectedShapes = getSelectedShapes(data)
|
const selectedShapes = tld.getSelectedShapes(data)
|
||||||
const isAllToggled = selectedShapes.every((shape) => shape[prop])
|
const isAllToggled = selectedShapes.every((shape) => shape[prop])
|
||||||
const initialShapes = Object.fromEntries(
|
const initialShapes = Object.fromEntries(
|
||||||
selectedShapes.map((shape) => [shape.id, shape[prop]])
|
selectedShapes.map((shape) => [shape.id, shape[prop]])
|
||||||
|
@ -21,7 +21,7 @@ export default function toggleCommand(
|
||||||
name: 'toggle_prop',
|
name: 'toggle_prop',
|
||||||
category: 'canvas',
|
category: 'canvas',
|
||||||
do(data) {
|
do(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
for (const id in initialShapes) {
|
for (const id in initialShapes) {
|
||||||
const shape = shapes[id]
|
const shape = shapes[id]
|
||||||
|
@ -33,7 +33,7 @@ export default function toggleCommand(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
for (const id in initialShapes) {
|
for (const id in initialShapes) {
|
||||||
const shape = shapes[id]
|
const shape = shapes[id]
|
||||||
|
|
|
@ -3,7 +3,7 @@ import history from '../history'
|
||||||
import { Data } from 'types'
|
import { Data } from 'types'
|
||||||
import { current } from 'immer'
|
import { current } from 'immer'
|
||||||
import { TransformSingleSnapshot } from 'state/sessions/transform-single-session'
|
import { TransformSingleSnapshot } from 'state/sessions/transform-single-session'
|
||||||
import { getPage, setSelectedIds, updateParents } from 'utils'
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
export default function transformSingleCommand(
|
export default function transformSingleCommand(
|
||||||
data: Data,
|
data: Data,
|
||||||
|
@ -11,7 +11,7 @@ export default function transformSingleCommand(
|
||||||
after: TransformSingleSnapshot,
|
after: TransformSingleSnapshot,
|
||||||
isCreating: boolean
|
isCreating: boolean
|
||||||
): void {
|
): void {
|
||||||
const shape = current(getPage(data).shapes[after.id])
|
const shape = current(tld.getPage(data).shapes[after.id])
|
||||||
|
|
||||||
history.execute(
|
history.execute(
|
||||||
data,
|
data,
|
||||||
|
@ -22,27 +22,27 @@ export default function transformSingleCommand(
|
||||||
do(data) {
|
do(data) {
|
||||||
const { id } = after
|
const { id } = after
|
||||||
|
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
setSelectedIds(data, [id])
|
tld.setSelectedIds(data, [id])
|
||||||
|
|
||||||
shapes[id] = shape
|
shapes[id] = shape
|
||||||
|
|
||||||
updateParents(data, [id])
|
tld.updateParents(data, [id])
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { id, initialShape } = before
|
const { id, initialShape } = before
|
||||||
|
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
if (isCreating) {
|
if (isCreating) {
|
||||||
setSelectedIds(data, [])
|
tld.setSelectedIds(data, [])
|
||||||
delete shapes[id]
|
delete shapes[id]
|
||||||
} else {
|
} else {
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
page.shapes[id] = initialShape
|
page.shapes[id] = initialShape
|
||||||
updateParents(data, [id])
|
tld.updateParents(data, [id])
|
||||||
setSelectedIds(data, [id])
|
tld.setSelectedIds(data, [id])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data } from 'types'
|
import { Data } from 'types'
|
||||||
import { TransformSnapshot } from 'state/sessions/transform-session'
|
import { TransformSnapshot } from 'state/sessions/transform-session'
|
||||||
import { getPage, updateParents } from 'utils'
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
export default function transformCommand(
|
export default function transformCommand(
|
||||||
data: Data,
|
data: Data,
|
||||||
|
@ -17,23 +17,23 @@ export default function transformCommand(
|
||||||
do(data) {
|
do(data) {
|
||||||
const { shapeBounds } = after
|
const { shapeBounds } = after
|
||||||
|
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
for (const id in shapeBounds) {
|
for (const id in shapeBounds) {
|
||||||
shapes[id] = shapeBounds[id].initialShape
|
shapes[id] = shapeBounds[id].initialShape
|
||||||
}
|
}
|
||||||
|
|
||||||
updateParents(data, Object.keys(shapeBounds))
|
tld.updateParents(data, Object.keys(shapeBounds))
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { shapeBounds } = before
|
const { shapeBounds } = before
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
for (const id in shapeBounds) {
|
for (const id in shapeBounds) {
|
||||||
shapes[id] = shapeBounds[id].initialShape
|
shapes[id] = shapeBounds[id].initialShape
|
||||||
}
|
}
|
||||||
|
|
||||||
updateParents(data, Object.keys(shapeBounds))
|
tld.updateParents(data, Object.keys(shapeBounds))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,12 +2,7 @@ import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { TranslateSnapshot } from 'state/sessions/translate-session'
|
import { TranslateSnapshot } from 'state/sessions/translate-session'
|
||||||
import { Data } from 'types'
|
import { Data } from 'types'
|
||||||
import {
|
import tld from 'utils/tld'
|
||||||
getDocumentBranch,
|
|
||||||
getPage,
|
|
||||||
setSelectedIds,
|
|
||||||
updateParents,
|
|
||||||
} from 'utils'
|
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
|
||||||
export default function translateCommand(
|
export default function translateCommand(
|
||||||
|
@ -26,7 +21,7 @@ export default function translateCommand(
|
||||||
if (initial) return
|
if (initial) return
|
||||||
|
|
||||||
const { initialShapes } = after
|
const { initialShapes } = after
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
// Restore clones to document
|
// Restore clones to document
|
||||||
if (isCloning) {
|
if (isCloning) {
|
||||||
|
@ -47,31 +42,31 @@ export default function translateCommand(
|
||||||
|
|
||||||
// Move shapes (these initialShapes will include clones if any)
|
// Move shapes (these initialShapes will include clones if any)
|
||||||
for (const { id, point } of initialShapes) {
|
for (const { id, point } of initialShapes) {
|
||||||
getDocumentBranch(data, id).forEach((id) => {
|
tld.getDocumentBranch(data, id).forEach((id) => {
|
||||||
const shape = shapes[id]
|
const shape = shapes[id]
|
||||||
getShapeUtils(shape).translateTo(shape, point)
|
getShapeUtils(shape).translateTo(shape, point)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set selected shapes
|
// Set selected shapes
|
||||||
setSelectedIds(
|
tld.setSelectedIds(
|
||||||
data,
|
data,
|
||||||
initialShapes.map((s) => s.id)
|
initialShapes.map((s) => s.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Update parents
|
// Update parents
|
||||||
updateParents(
|
tld.updateParents(
|
||||||
data,
|
data,
|
||||||
initialShapes.map((s) => s.id)
|
initialShapes.map((s) => s.id)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { initialShapes, clones, initialParents } = before
|
const { initialShapes, clones, initialParents } = before
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
// Move shapes back to where they started
|
// Move shapes back to where they started
|
||||||
for (const { id, point } of initialShapes) {
|
for (const { id, point } of initialShapes) {
|
||||||
getDocumentBranch(data, id).forEach((id) => {
|
tld.getDocumentBranch(data, id).forEach((id) => {
|
||||||
const shape = shapes[id]
|
const shape = shapes[id]
|
||||||
getShapeUtils(shape).translateTo(shape, point)
|
getShapeUtils(shape).translateTo(shape, point)
|
||||||
})
|
})
|
||||||
|
@ -81,7 +76,7 @@ export default function translateCommand(
|
||||||
if (isCloning) for (const { id } of clones) delete shapes[id]
|
if (isCloning) for (const { id } of clones) delete shapes[id]
|
||||||
|
|
||||||
// Set selected shapes
|
// Set selected shapes
|
||||||
setSelectedIds(
|
tld.setSelectedIds(
|
||||||
data,
|
data,
|
||||||
initialShapes.map((s) => s.id)
|
initialShapes.map((s) => s.id)
|
||||||
)
|
)
|
||||||
|
@ -93,7 +88,7 @@ export default function translateCommand(
|
||||||
})
|
})
|
||||||
|
|
||||||
// Update parents
|
// Update parents
|
||||||
updateParents(
|
tld.updateParents(
|
||||||
data,
|
data,
|
||||||
initialShapes.map((s) => s.id)
|
initialShapes.map((s) => s.id)
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data, ShapeType } from 'types'
|
import { Data, ShapeType } from 'types'
|
||||||
import { getPage, getSelectedShapes, setSelectedIds } from 'utils'
|
|
||||||
import { current } from 'immer'
|
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
|
import { deepClone } from 'utils'
|
||||||
|
|
||||||
export default function ungroupCommand(data: Data): void {
|
export default function ungroupCommand(data: Data): void {
|
||||||
const cData = current(data)
|
const { currentPageId } = data
|
||||||
const { currentPageId } = cData
|
|
||||||
|
|
||||||
const selectedGroups = getSelectedShapes(cData)
|
const selectedGroups = tld
|
||||||
|
.getSelectedShapes(data)
|
||||||
.filter((shape) => shape.type === ShapeType.Group)
|
.filter((shape) => shape.type === ShapeType.Group)
|
||||||
.sort((a, b) => a.childIndex - b.childIndex)
|
.sort((a, b) => a.childIndex - b.childIndex)
|
||||||
|
.map((shape) => deepClone(shape))
|
||||||
|
|
||||||
// Are all of the shapes already in the same group?
|
// Are all of the shapes already in the same group?
|
||||||
// - ungroup the shapes
|
// - ungroup the shapes
|
||||||
|
@ -24,7 +25,7 @@ export default function ungroupCommand(data: Data): void {
|
||||||
name: 'ungroup_shapes',
|
name: 'ungroup_shapes',
|
||||||
category: 'canvas',
|
category: 'canvas',
|
||||||
do(data) {
|
do(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
// Remove shapes from old parents
|
// Remove shapes from old parents
|
||||||
for (const oldGroupShape of selectedGroups) {
|
for (const oldGroupShape of selectedGroups) {
|
||||||
|
@ -62,13 +63,13 @@ export default function ungroupCommand(data: Data): void {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
setSelectedIds(data, oldGroupShape.children)
|
tld.setSelectedIds(data, oldGroupShape.children)
|
||||||
|
|
||||||
delete shapes[oldGroupShape.id]
|
delete shapes[oldGroupShape.id]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
selectedGroups.forEach((group) => {
|
selectedGroups.forEach((group) => {
|
||||||
shapes[group.id] = group
|
shapes[group.id] = group
|
||||||
|
@ -80,7 +81,7 @@ export default function ungroupCommand(data: Data): void {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
setSelectedIds(
|
tld.setSelectedIds(
|
||||||
data,
|
data,
|
||||||
selectedGroups.map((g) => g.id)
|
selectedGroups.map((g) => g.id)
|
||||||
)
|
)
|
||||||
|
@ -94,5 +95,5 @@ export default function ungroupCommand(data: Data): void {
|
||||||
// return depth
|
// return depth
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// return getShapeDepth(data, getShape(data, id).parentId, depth + 1)
|
// return getShapeDepth(data, tld.getShape(data, id).parentId, depth + 1)
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
import { DrawShape, PointerInfo } from 'types'
|
import { DrawShape, PointerInfo } from 'types'
|
||||||
import {
|
import { setToArray } from 'utils'
|
||||||
getCameraZoom,
|
import tld from 'utils/tld'
|
||||||
getCurrentCamera,
|
|
||||||
getSelectedIds,
|
|
||||||
screenToWorld,
|
|
||||||
setToArray,
|
|
||||||
} from 'utils'
|
|
||||||
import { freeze } from 'immer'
|
import { freeze } from 'immer'
|
||||||
import session from './session'
|
import session from './session'
|
||||||
import state from './state'
|
import state from './state'
|
||||||
|
@ -24,12 +19,12 @@ export function fastDrawUpdate(info: PointerInfo): void {
|
||||||
|
|
||||||
session.update<Session.DrawSession>(
|
session.update<Session.DrawSession>(
|
||||||
data,
|
data,
|
||||||
screenToWorld(info.point, data),
|
tld.screenToWorld(info.point, data),
|
||||||
info.pressure,
|
info.pressure,
|
||||||
info.shiftKey
|
info.shiftKey
|
||||||
)
|
)
|
||||||
|
|
||||||
const selectedId = setToArray(getSelectedIds(data))[0]
|
const selectedId = setToArray(tld.getSelectedIds(data))[0]
|
||||||
|
|
||||||
const shape = data.document.pages[data.currentPageId].shapes[
|
const shape = data.document.pages[data.currentPageId].shapes[
|
||||||
selectedId
|
selectedId
|
||||||
|
@ -45,7 +40,7 @@ export function fastDrawUpdate(info: PointerInfo): void {
|
||||||
|
|
||||||
export function fastPanUpdate(delta: number[]): void {
|
export function fastPanUpdate(delta: number[]): void {
|
||||||
const data = { ...state.data }
|
const data = { ...state.data }
|
||||||
const camera = getCurrentCamera(data)
|
const camera = tld.getCurrentCamera(data)
|
||||||
camera.point = vec.sub(camera.point, vec.div(delta, camera.zoom))
|
camera.point = vec.sub(camera.point, vec.div(delta, camera.zoom))
|
||||||
|
|
||||||
data.pageStates[data.currentPageId].camera = { ...camera }
|
data.pageStates[data.currentPageId].camera = { ...camera }
|
||||||
|
@ -55,13 +50,13 @@ export function fastPanUpdate(delta: number[]): void {
|
||||||
|
|
||||||
export function fastZoomUpdate(point: number[], delta: number): void {
|
export function fastZoomUpdate(point: number[], delta: number): void {
|
||||||
const data = { ...state.data }
|
const data = { ...state.data }
|
||||||
const camera = getCurrentCamera(data)
|
const camera = tld.getCurrentCamera(data)
|
||||||
|
|
||||||
const next = camera.zoom - (delta / 100) * camera.zoom
|
const next = camera.zoom - (delta / 100) * camera.zoom
|
||||||
|
|
||||||
const p0 = screenToWorld(point, data)
|
const p0 = tld.screenToWorld(point, data)
|
||||||
camera.zoom = getCameraZoom(next)
|
camera.zoom = tld.getCameraZoom(next)
|
||||||
const p1 = screenToWorld(point, data)
|
const p1 = tld.screenToWorld(point, data)
|
||||||
camera.point = vec.add(camera.point, vec.sub(p1, p0))
|
camera.point = vec.add(camera.point, vec.sub(p1, p0))
|
||||||
|
|
||||||
data.pageStates[data.currentPageId].camera = { ...camera }
|
data.pageStates[data.currentPageId].camera = { ...camera }
|
||||||
|
@ -75,15 +70,15 @@ export function fastPinchCamera(
|
||||||
distanceDelta: number
|
distanceDelta: number
|
||||||
): void {
|
): void {
|
||||||
const data = { ...state.data }
|
const data = { ...state.data }
|
||||||
const camera = getCurrentCamera(data)
|
const camera = tld.getCurrentCamera(data)
|
||||||
|
|
||||||
camera.point = vec.sub(camera.point, vec.div(delta, camera.zoom))
|
camera.point = vec.sub(camera.point, vec.div(delta, camera.zoom))
|
||||||
|
|
||||||
const next = camera.zoom - (distanceDelta / 350) * camera.zoom
|
const next = camera.zoom - (distanceDelta / 350) * camera.zoom
|
||||||
|
|
||||||
const p0 = screenToWorld(point, data)
|
const p0 = tld.screenToWorld(point, data)
|
||||||
camera.zoom = getCameraZoom(next)
|
camera.zoom = tld.getCameraZoom(next)
|
||||||
const p1 = screenToWorld(point, data)
|
const p1 = tld.screenToWorld(point, data)
|
||||||
camera.point = vec.add(camera.point, vec.sub(p1, p0))
|
camera.point = vec.add(camera.point, vec.sub(p1, p0))
|
||||||
|
|
||||||
const pageState = data.pageStates[data.currentPageId]
|
const pageState = data.pageStates[data.currentPageId]
|
||||||
|
@ -97,7 +92,7 @@ export function fastPinchCamera(
|
||||||
export function fastBrushSelect(point: number[]): void {
|
export function fastBrushSelect(point: number[]): void {
|
||||||
const data = { ...state.data }
|
const data = { ...state.data }
|
||||||
|
|
||||||
session.update<Session.BrushSession>(data, screenToWorld(point, data))
|
session.update<Session.BrushSession>(data, tld.screenToWorld(point, data))
|
||||||
|
|
||||||
state.forceData(freeze(data))
|
state.forceData(freeze(data))
|
||||||
}
|
}
|
||||||
|
@ -107,7 +102,7 @@ export function fastTranslate(info: PointerInfo): void {
|
||||||
|
|
||||||
session.update<Session.TranslateSession>(
|
session.update<Session.TranslateSession>(
|
||||||
data,
|
data,
|
||||||
screenToWorld(info.point, data),
|
tld.screenToWorld(info.point, data),
|
||||||
info.shiftKey,
|
info.shiftKey,
|
||||||
info.altKey
|
info.altKey
|
||||||
)
|
)
|
||||||
|
@ -120,7 +115,7 @@ export function fastTransform(info: PointerInfo): void {
|
||||||
|
|
||||||
session.update<Session.TransformSession | Session.TransformSingleSession>(
|
session.update<Session.TransformSession | Session.TransformSingleSession>(
|
||||||
data,
|
data,
|
||||||
screenToWorld(info.point, data),
|
tld.screenToWorld(info.point, data),
|
||||||
info.shiftKey
|
info.shiftKey
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
140
state/pusher/client-supa.ts
Normal file
140
state/pusher/client-supa.ts
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
import state from 'state/state'
|
||||||
|
// import { Shape } from 'types'
|
||||||
|
import { RealtimeClient, RealtimeSubscription } from '@supabase/realtime-js'
|
||||||
|
|
||||||
|
class RoomClient {
|
||||||
|
id: string
|
||||||
|
roomId: string
|
||||||
|
client: RealtimeClient
|
||||||
|
channel: RealtimeSubscription
|
||||||
|
lastCursorEventTime = 0
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
// Create client
|
||||||
|
this.client = new RealtimeClient(
|
||||||
|
'https://mntnflsepfmpvthazvvu.supabase.com'
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set event listeners
|
||||||
|
this.client.onOpen(() =>
|
||||||
|
state.send('RT_CHANGED_STATUS', { status: 'Socket opened.' })
|
||||||
|
)
|
||||||
|
|
||||||
|
this.client.onClose(() =>
|
||||||
|
state.send('RT_CHANGED_STATUS', { status: 'Socket closed.' })
|
||||||
|
)
|
||||||
|
|
||||||
|
this.client.onError((e: Error) =>
|
||||||
|
state.send('RT_CHANGED_STATUS', { status: `Socket error: ${e.message}` })
|
||||||
|
)
|
||||||
|
|
||||||
|
// Connect to client
|
||||||
|
this.client.connect()
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(roomId: string) {
|
||||||
|
this.roomId = roomId
|
||||||
|
|
||||||
|
// Unsubscribe from any existing channel
|
||||||
|
|
||||||
|
if (this.channel !== undefined) {
|
||||||
|
this.channel.unsubscribe()
|
||||||
|
delete this.channel
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new channel for this room id
|
||||||
|
|
||||||
|
this.channel = this.client.channel(`realtime:public:${this.roomId}`)
|
||||||
|
this.channel.on('*', (e: any) => console.log(e))
|
||||||
|
this.channel.on('INSERT', (e: any) => console.log(e))
|
||||||
|
this.channel.on('UPDATE', (e: any) => console.log(e))
|
||||||
|
this.channel.on('DELETE', (e: any) => console.log(e))
|
||||||
|
|
||||||
|
// Subscribe to the channel
|
||||||
|
this.channel
|
||||||
|
.subscribe()
|
||||||
|
.receive('ok', () => console.log('Connected.'))
|
||||||
|
.receive('error', () => console.log('Failed.'))
|
||||||
|
.receive('timeout', () => console.log('Timed out, retrying.'))
|
||||||
|
|
||||||
|
// Old
|
||||||
|
|
||||||
|
// this.channel = this.pusher.subscribe(
|
||||||
|
// this.room
|
||||||
|
// ) as PusherTypes.PresenceChannel
|
||||||
|
|
||||||
|
// this.channel.bind('pusher:subscription_error', (err: string) => {
|
||||||
|
// console.warn(err)
|
||||||
|
// state.send('RT_CHANGED_STATUS', { status: 'subscription-error' })
|
||||||
|
// })
|
||||||
|
|
||||||
|
// this.channel.bind('pusher:subscription_succeeded', () => {
|
||||||
|
// const me = this.channel.members.me
|
||||||
|
// const userId = me.id
|
||||||
|
|
||||||
|
// this.id = userId
|
||||||
|
|
||||||
|
// state.send('RT_CHANGED_STATUS', { status: 'subscribed' })
|
||||||
|
// })
|
||||||
|
|
||||||
|
// this.channel.bind(
|
||||||
|
// 'created_shape',
|
||||||
|
// (payload: { id: string; pageId: string; shape: Shape }) => {
|
||||||
|
// if (payload.id === this.id) return
|
||||||
|
// state.send('RT_CREATED_SHAPE', payload)
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
|
||||||
|
// this.channel.bind(
|
||||||
|
// 'deleted_shape',
|
||||||
|
// (payload: { id: string; pageId: string; shape: Shape }) => {
|
||||||
|
// if (payload.id === this.id) return
|
||||||
|
// state.send('RT_DELETED_SHAPE', payload)
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
|
||||||
|
// this.channel.bind(
|
||||||
|
// 'edited_shape',
|
||||||
|
// (payload: { id: string; pageId: string; change: Partial<Shape> }) => {
|
||||||
|
// if (payload.id === this.id) return
|
||||||
|
// state.send('RT_EDITED_SHAPE', payload)
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
|
||||||
|
// this.channel.bind(
|
||||||
|
// 'client-moved-cursor',
|
||||||
|
// (payload: { id: string; pageId: string; point: number[] }) => {
|
||||||
|
// if (payload.id === this.id) return
|
||||||
|
// state.send('RT_MOVED_CURSOR', payload)
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect() {
|
||||||
|
this.channel.unsubscribe()
|
||||||
|
this.client.disconnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
reconnect() {
|
||||||
|
this.connect(this.roomId)
|
||||||
|
}
|
||||||
|
|
||||||
|
moveCursor(pageId: string, point: number[]) {
|
||||||
|
if (!this.channel) return
|
||||||
|
|
||||||
|
const now = Date.now()
|
||||||
|
|
||||||
|
if (now - this.lastCursorEventTime > 200) {
|
||||||
|
this.lastCursorEventTime = now
|
||||||
|
|
||||||
|
this.channel?.trigger('client-moved-cursor', {
|
||||||
|
id: this.id,
|
||||||
|
pageId,
|
||||||
|
point,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new RoomClient()
|
|
@ -3,14 +3,9 @@ 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 {
|
import { getBoundsFromPoints, setToArray } from 'utils'
|
||||||
getBoundsFromPoints,
|
|
||||||
getPage,
|
|
||||||
getSelectedIds,
|
|
||||||
setToArray,
|
|
||||||
updateParents,
|
|
||||||
} from 'utils'
|
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
export default class ArrowSession extends BaseSession {
|
export default class ArrowSession extends BaseSession {
|
||||||
points: number[][]
|
points: number[][]
|
||||||
|
@ -56,7 +51,7 @@ export default class ArrowSession extends BaseSession {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const shape = getPage(data).shapes[id] as ArrowShape
|
const shape = tld.getPage(data).shapes[id] as ArrowShape
|
||||||
|
|
||||||
getShapeUtils(shape).onHandleChange(shape, {
|
getShapeUtils(shape).onHandleChange(shape, {
|
||||||
end: {
|
end: {
|
||||||
|
@ -65,25 +60,25 @@ export default class ArrowSession extends BaseSession {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
updateParents(data, [shape.id])
|
tld.updateParents(data, [shape.id])
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel(data: Data): void {
|
cancel(data: Data): void {
|
||||||
const { id, initialShape } = this.snapshot
|
const { id, initialShape } = this.snapshot
|
||||||
|
|
||||||
const shape = getPage(data).shapes[id] as ArrowShape
|
const shape = tld.getPage(data).shapes[id] as ArrowShape
|
||||||
|
|
||||||
getShapeUtils(shape)
|
getShapeUtils(shape)
|
||||||
.onHandleChange(shape, { end: initialShape.handles.end })
|
.onHandleChange(shape, { end: initialShape.handles.end })
|
||||||
.setProperty(shape, 'point', initialShape.point)
|
.setProperty(shape, 'point', initialShape.point)
|
||||||
|
|
||||||
updateParents(data, [shape.id])
|
tld.updateParents(data, [shape.id])
|
||||||
}
|
}
|
||||||
|
|
||||||
complete(data: Data): void {
|
complete(data: Data): void {
|
||||||
const { id } = this.snapshot
|
const { id } = this.snapshot
|
||||||
|
|
||||||
const shape = getPage(data).shapes[id] as ArrowShape
|
const shape = tld.getPage(data).shapes[id] as ArrowShape
|
||||||
|
|
||||||
const { start, end, bend } = shape.handles
|
const { start, end, bend } = shape.handles
|
||||||
|
|
||||||
|
@ -115,12 +110,12 @@ export default class ArrowSession extends BaseSession {
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
export function getArrowSnapshot(data: Data, id: string) {
|
export function getArrowSnapshot(data: Data, id: string) {
|
||||||
const initialShape = getPage(current(data)).shapes[id] as ArrowShape
|
const initialShape = tld.getPage(current(data)).shapes[id] as ArrowShape
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
initialShape,
|
initialShape,
|
||||||
selectedIds: setToArray(getSelectedIds(data)),
|
selectedIds: setToArray(tld.getSelectedIds(data)),
|
||||||
currentPageId: data.currentPageId,
|
currentPageId: data.currentPageId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,9 @@ import { current } from 'immer'
|
||||||
import { Bounds, Data, ShapeType } from 'types'
|
import { Bounds, Data, ShapeType } from 'types'
|
||||||
import BaseSession from './base-session'
|
import BaseSession from './base-session'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import {
|
import { getBoundsFromPoints, setToArray } from 'utils'
|
||||||
getBoundsFromPoints,
|
|
||||||
getPageState,
|
|
||||||
getShapes,
|
|
||||||
getTopParentId,
|
|
||||||
setSelectedIds,
|
|
||||||
setToArray,
|
|
||||||
} from 'utils'
|
|
||||||
import vec from 'utils/vec'
|
import vec from 'utils/vec'
|
||||||
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
export default class BrushSession extends BaseSession {
|
export default class BrushSession extends BaseSession {
|
||||||
origin: number[]
|
origin: number[]
|
||||||
|
@ -51,14 +45,14 @@ export default class BrushSession extends BaseSession {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getPageState(data).selectedIds = selectedIds
|
tld.getPageState(data).selectedIds = selectedIds
|
||||||
|
|
||||||
data.brush = brushBounds
|
data.brush = brushBounds
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel = (data: Data): void => {
|
cancel = (data: Data): void => {
|
||||||
data.brush = undefined
|
data.brush = undefined
|
||||||
setSelectedIds(data, this.snapshot.selectedIds)
|
tld.setSelectedIds(data, this.snapshot.selectedIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
complete = (data: Data): void => {
|
complete = (data: Data): void => {
|
||||||
|
@ -74,9 +68,10 @@ export default class BrushSession extends BaseSession {
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
export function getBrushSnapshot(data: Data) {
|
export function getBrushSnapshot(data: Data) {
|
||||||
const cData = current(data)
|
const cData = current(data)
|
||||||
const { selectedIds } = getPageState(cData)
|
const { selectedIds } = tld.getPageState(cData)
|
||||||
|
|
||||||
const shapesToTest = getShapes(cData)
|
const shapesToTest = tld
|
||||||
|
.getShapes(cData)
|
||||||
.filter((shape) => shape.type !== ShapeType.Group && !shape.isHidden)
|
.filter((shape) => shape.type !== ShapeType.Group && !shape.isHidden)
|
||||||
.filter(
|
.filter(
|
||||||
(shape) => !(selectedIds.has(shape.id) || selectedIds.has(shape.parentId))
|
(shape) => !(selectedIds.has(shape.id) || selectedIds.has(shape.parentId))
|
||||||
|
@ -89,7 +84,7 @@ export function getBrushSnapshot(data: Data) {
|
||||||
return [
|
return [
|
||||||
shape.id,
|
shape.id,
|
||||||
{
|
{
|
||||||
selectId: getTopParentId(cData, shape.id),
|
selectId: tld.getTopParentId(cData, shape.id),
|
||||||
test: (bounds: Bounds) =>
|
test: (bounds: Bounds) =>
|
||||||
getShapeUtils(shape).hitTestBounds(shape, bounds),
|
getShapeUtils(shape).hitTestBounds(shape, bounds),
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { Data, LineShape, RayShape } from 'types'
|
import { Data, Shape } from 'types'
|
||||||
import 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 tld from 'utils/tld'
|
||||||
import { getPage, getSelectedIds } from 'utils'
|
import { deepClone } from 'utils'
|
||||||
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
|
||||||
export default class DirectionSession extends BaseSession {
|
export default class DirectionSession extends BaseSession {
|
||||||
delta = [0, 0]
|
delta = [0, 0]
|
||||||
|
@ -17,48 +18,52 @@ export default class DirectionSession extends BaseSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
update(data: Data, point: number[]): void {
|
update(data: Data, point: number[]): void {
|
||||||
const { shapes } = this.snapshot
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
const page = getPage(data)
|
this.snapshot.forEach((initialShape) => {
|
||||||
|
const shape = page.shapes[initialShape.id]
|
||||||
|
|
||||||
for (const { id } of shapes) {
|
if ('direction' in shape) {
|
||||||
const shape = page.shapes[id] as RayShape | LineShape
|
getShapeUtils(shape).setProperty(
|
||||||
|
shape,
|
||||||
shape.direction = vec.uni(vec.vec(shape.point, point))
|
'direction',
|
||||||
}
|
vec.uni(vec.vec(shape.point, point))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel(data: Data): void {
|
cancel(data: Data): void {
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
for (const { id, direction } of this.snapshot.shapes) {
|
this.snapshot.forEach((initialShape) => {
|
||||||
const shape = page.shapes[id] as RayShape | LineShape
|
const shape = page.shapes[initialShape.id]
|
||||||
shape.direction = direction
|
|
||||||
}
|
if ('direction' in shape && 'direction' in initialShape) {
|
||||||
|
getShapeUtils(shape).setProperty(
|
||||||
|
shape,
|
||||||
|
'direction',
|
||||||
|
initialShape.direction
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
complete(data: Data): void {
|
complete(data: Data): void {
|
||||||
commands.direct(data, this.snapshot, getDirectionSnapshot(data))
|
commands.mutate(
|
||||||
|
data,
|
||||||
|
this.snapshot,
|
||||||
|
getDirectionSnapshot(data),
|
||||||
|
'change_direction'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
export function getDirectionSnapshot(data: Data): Shape[] {
|
||||||
export function getDirectionSnapshot(data: Data) {
|
return tld
|
||||||
const { shapes } = getPage(current(data))
|
.getSelectedShapes(data)
|
||||||
|
.filter((shape) => 'direction' in shape)
|
||||||
const snapshapes: { id: string; direction: number[] }[] = []
|
.map((shape) => deepClone(shape))
|
||||||
|
|
||||||
getSelectedIds(data).forEach((id) => {
|
|
||||||
const shape = shapes[id]
|
|
||||||
if ('direction' in shape) {
|
|
||||||
snapshapes.push({ id: shape.id, direction: shape.direction })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
currentPageId: data.currentPageId,
|
|
||||||
shapes: snapshapes,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DirectionSnapshot = ReturnType<typeof getDirectionSnapshot>
|
export type DirectionSnapshot = ReturnType<typeof getDirectionSnapshot>
|
||||||
|
|
|
@ -2,7 +2,8 @@ import { current } from 'immer'
|
||||||
import { Data, DrawShape } from 'types'
|
import { Data, DrawShape } from 'types'
|
||||||
import BaseSession from './base-session'
|
import BaseSession from './base-session'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import { getBoundsFromPoints, getPage, getShape, updateParents } from 'utils'
|
import { getBoundsFromPoints } from 'utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
import vec from 'utils/vec'
|
import vec from 'utils/vec'
|
||||||
import commands from 'state/commands'
|
import commands from 'state/commands'
|
||||||
|
|
||||||
|
@ -27,11 +28,11 @@ export default class DrawSession extends BaseSession {
|
||||||
// points, this single point will be interpreted as a "dot" shape.
|
// points, this single point will be interpreted as a "dot" shape.
|
||||||
this.points = [[0, 0, 0.5]]
|
this.points = [[0, 0, 0.5]]
|
||||||
|
|
||||||
const shape = getPage(data).shapes[id]
|
const shape = tld.getPage(data).shapes[id]
|
||||||
|
|
||||||
getShapeUtils(shape).translateTo(shape, point)
|
getShapeUtils(shape).translateTo(shape, point)
|
||||||
|
|
||||||
updateParents(data, [shape.id])
|
tld.updateParents(data, [shape.id])
|
||||||
}
|
}
|
||||||
|
|
||||||
update = (
|
update = (
|
||||||
|
@ -103,26 +104,26 @@ export default class DrawSession extends BaseSession {
|
||||||
if (this.points.length <= 2) return
|
if (this.points.length <= 2) return
|
||||||
|
|
||||||
// Update the points and update the shape's parents.
|
// Update the points and update the shape's parents.
|
||||||
const shape = getShape(data, snapshot.id) as DrawShape
|
const shape = tld.getShape(data, snapshot.id) as DrawShape
|
||||||
|
|
||||||
// Note: Normally we would want to spread the points to create a new
|
// Note: Normally we would want to spread the points to create a new
|
||||||
// array, however we create the new array in hacks/fastDrawUpdate.
|
// array, however we create the new array in hacks/fastDrawUpdate.
|
||||||
getShapeUtils(shape).setProperty(shape, 'points', this.points)
|
getShapeUtils(shape).setProperty(shape, 'points', this.points)
|
||||||
|
|
||||||
updateParents(data, [shape.id])
|
tld.updateParents(data, [shape.id])
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel = (data: Data): void => {
|
cancel = (data: Data): void => {
|
||||||
const { snapshot } = this
|
const { snapshot } = this
|
||||||
const shape = getShape(data, snapshot.id) as DrawShape
|
const shape = tld.getShape(data, snapshot.id) as DrawShape
|
||||||
getShapeUtils(shape).translateTo(shape, snapshot.point)
|
getShapeUtils(shape).translateTo(shape, snapshot.point)
|
||||||
getShapeUtils(shape).setProperty(shape, 'points', snapshot.points)
|
getShapeUtils(shape).setProperty(shape, 'points', snapshot.points)
|
||||||
updateParents(data, [shape.id])
|
tld.updateParents(data, [shape.id])
|
||||||
}
|
}
|
||||||
|
|
||||||
complete = (data: Data): void => {
|
complete = (data: Data): void => {
|
||||||
const { snapshot } = this
|
const { snapshot } = this
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
const shape = page.shapes[snapshot.id] as DrawShape
|
const shape = page.shapes[snapshot.id] as DrawShape
|
||||||
|
|
||||||
if (shape.points.length < this.points.length) {
|
if (shape.points.length < this.points.length) {
|
||||||
|
@ -131,7 +132,7 @@ export default class DrawSession extends BaseSession {
|
||||||
|
|
||||||
getShapeUtils(shape).onSessionComplete(shape)
|
getShapeUtils(shape).onSessionComplete(shape)
|
||||||
|
|
||||||
updateParents(data, [shape.id])
|
tld.updateParents(data, [shape.id])
|
||||||
|
|
||||||
commands.draw(data, this.snapshot.id)
|
commands.draw(data, this.snapshot.id)
|
||||||
}
|
}
|
||||||
|
@ -139,7 +140,7 @@ export default class DrawSession extends BaseSession {
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
export function getDrawSnapshot(data: Data, shapeId: string) {
|
export function getDrawSnapshot(data: Data, shapeId: string) {
|
||||||
const page = getPage(current(data))
|
const page = tld.getPage(current(data))
|
||||||
const { points, point } = page.shapes[shapeId] as DrawShape
|
const { points, point } = page.shapes[shapeId] as DrawShape
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -2,8 +2,8 @@ import { Data, Shape } from 'types'
|
||||||
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 { getPage, getSelectedShapes, getShape } from 'utils'
|
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
export default class EditSession extends BaseSession {
|
export default class EditSession extends BaseSession {
|
||||||
snapshot: EditSnapshot
|
snapshot: EditSnapshot
|
||||||
|
@ -15,7 +15,7 @@ export default class EditSession extends BaseSession {
|
||||||
|
|
||||||
update(data: Data, change: Partial<Shape>): void {
|
update(data: Data, change: Partial<Shape>): void {
|
||||||
const initialShape = this.snapshot.initialShape
|
const initialShape = this.snapshot.initialShape
|
||||||
const shape = getShape(data, initialShape.id)
|
const shape = tld.getShape(data, initialShape.id)
|
||||||
const utils = getShapeUtils(shape)
|
const utils = getShapeUtils(shape)
|
||||||
Object.entries(change).forEach(([key, value]) => {
|
Object.entries(change).forEach(([key, value]) => {
|
||||||
utils.setProperty(shape, key as keyof Shape, value as Shape[keyof Shape])
|
utils.setProperty(shape, key as keyof Shape, value as Shape[keyof Shape])
|
||||||
|
@ -24,7 +24,7 @@ export default class EditSession extends BaseSession {
|
||||||
|
|
||||||
cancel(data: Data): void {
|
cancel(data: Data): void {
|
||||||
const initialShape = this.snapshot.initialShape
|
const initialShape = this.snapshot.initialShape
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
page.shapes[initialShape.id] = initialShape
|
page.shapes[initialShape.id] = initialShape
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ export default class EditSession extends BaseSession {
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
export function getEditSnapshot(data: Data) {
|
export function getEditSnapshot(data: Data) {
|
||||||
const initialShape = getSelectedShapes(current(data))[0]
|
const initialShape = tld.getSelectedShapes(current(data))[0]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentPageId: data.currentPageId,
|
currentPageId: data.currentPageId,
|
||||||
|
|
|
@ -3,7 +3,7 @@ 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 { getPage } from 'utils'
|
import tld from 'utils/tld'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
|
||||||
export default class HandleSession extends BaseSession {
|
export default class HandleSession extends BaseSession {
|
||||||
|
@ -19,7 +19,7 @@ export default class HandleSession extends BaseSession {
|
||||||
|
|
||||||
update(data: Data, point: number[], isAligned: boolean): void {
|
update(data: Data, point: number[], isAligned: boolean): void {
|
||||||
const { handleId, initialShape } = this.snapshot
|
const { handleId, initialShape } = this.snapshot
|
||||||
const shape = getPage(data).shapes[initialShape.id]
|
const shape = tld.getPage(data).shapes[initialShape.id]
|
||||||
|
|
||||||
const delta = vec.vec(this.origin, point)
|
const delta = vec.vec(this.origin, point)
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ export default class HandleSession extends BaseSession {
|
||||||
|
|
||||||
cancel(data: Data): void {
|
cancel(data: Data): void {
|
||||||
const { initialShape } = this.snapshot
|
const { initialShape } = this.snapshot
|
||||||
getPage(data).shapes[initialShape.id] = initialShape
|
tld.getPage(data).shapes[initialShape.id] = initialShape
|
||||||
}
|
}
|
||||||
|
|
||||||
complete(data: Data): void {
|
complete(data: Data): void {
|
||||||
|
@ -69,7 +69,7 @@ export function getHandleSnapshot(
|
||||||
shapeId: string,
|
shapeId: string,
|
||||||
handleId: string
|
handleId: string
|
||||||
) {
|
) {
|
||||||
const initialShape = getPage(current(data)).shapes[shapeId]
|
const initialShape = tld.getPage(current(data)).shapes[shapeId]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentPageId: data.currentPageId,
|
currentPageId: data.currentPageId,
|
||||||
|
|
|
@ -7,14 +7,9 @@ import {
|
||||||
clampToRotationToSegments,
|
clampToRotationToSegments,
|
||||||
getBoundsCenter,
|
getBoundsCenter,
|
||||||
getCommonBounds,
|
getCommonBounds,
|
||||||
getPage,
|
|
||||||
getRotatedBounds,
|
|
||||||
getShapeBounds,
|
|
||||||
updateParents,
|
|
||||||
getDocumentBranch,
|
|
||||||
setToArray,
|
setToArray,
|
||||||
getSelectedIds,
|
|
||||||
} from 'utils'
|
} from 'utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
|
||||||
const PI2 = Math.PI * 2
|
const PI2 = Math.PI * 2
|
||||||
|
@ -34,7 +29,7 @@ export default class RotateSession extends BaseSession {
|
||||||
update(data: Data, point: number[], isLocked: boolean): void {
|
update(data: Data, point: number[], isLocked: boolean): void {
|
||||||
const { commonBoundsCenter, initialShapes } = this.snapshot
|
const { commonBoundsCenter, initialShapes } = this.snapshot
|
||||||
|
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
const a1 = vec.angle(commonBoundsCenter, this.origin)
|
const a1 = vec.angle(commonBoundsCenter, this.origin)
|
||||||
const a2 = vec.angle(commonBoundsCenter, point)
|
const a2 = vec.angle(commonBoundsCenter, point)
|
||||||
|
|
||||||
|
@ -69,7 +64,7 @@ export default class RotateSession extends BaseSession {
|
||||||
.translateTo(shape, nextPoint)
|
.translateTo(shape, nextPoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateParents(
|
tld.updateParents(
|
||||||
data,
|
data,
|
||||||
initialShapes.map((s) => s.id)
|
initialShapes.map((s) => s.id)
|
||||||
)
|
)
|
||||||
|
@ -77,7 +72,7 @@ export default class RotateSession extends BaseSession {
|
||||||
|
|
||||||
cancel(data: Data): void {
|
cancel(data: Data): void {
|
||||||
const { initialShapes } = this.snapshot
|
const { initialShapes } = this.snapshot
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
for (const { id, point, rotation } of initialShapes) {
|
for (const { id, point, rotation } of initialShapes) {
|
||||||
const shape = page.shapes[id]
|
const shape = page.shapes[id]
|
||||||
|
@ -86,7 +81,7 @@ export default class RotateSession extends BaseSession {
|
||||||
.translateTo(shape, point)
|
.translateTo(shape, point)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateParents(
|
tld.updateParents(
|
||||||
data,
|
data,
|
||||||
initialShapes.map((s) => s.id)
|
initialShapes.map((s) => s.id)
|
||||||
)
|
)
|
||||||
|
@ -101,16 +96,18 @@ export default class RotateSession extends BaseSession {
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
export function getRotateSnapshot(data: Data) {
|
export function getRotateSnapshot(data: Data) {
|
||||||
const cData = current(data)
|
const cData = current(data)
|
||||||
const page = getPage(cData)
|
const page = tld.getPage(cData)
|
||||||
|
|
||||||
const initialShapes = setToArray(getSelectedIds(data))
|
const initialShapes = setToArray(tld.getSelectedIds(data))
|
||||||
.flatMap((id) => getDocumentBranch(cData, id).map((id) => page.shapes[id]))
|
.flatMap((id) =>
|
||||||
|
tld.getDocumentBranch(cData, id).map((id) => page.shapes[id])
|
||||||
|
)
|
||||||
.filter((shape) => !shape.isLocked)
|
.filter((shape) => !shape.isLocked)
|
||||||
|
|
||||||
const hasUnlockedShapes = initialShapes.length > 0
|
const hasUnlockedShapes = initialShapes.length > 0
|
||||||
|
|
||||||
const shapesBounds = Object.fromEntries(
|
const shapesBounds = Object.fromEntries(
|
||||||
initialShapes.map((shape) => [shape.id, getShapeBounds(shape)])
|
initialShapes.map((shape) => [shape.id, tld.getShapeBounds(shape)])
|
||||||
)
|
)
|
||||||
|
|
||||||
const bounds = getCommonBounds(...Object.values(shapesBounds))
|
const bounds = getCommonBounds(...Object.values(shapesBounds))
|
||||||
|
@ -131,7 +128,7 @@ export function getRotateSnapshot(data: Data) {
|
||||||
|
|
||||||
const rotationOffset = vec.sub(
|
const rotationOffset = vec.sub(
|
||||||
center,
|
center,
|
||||||
getBoundsCenter(getRotatedBounds(shape))
|
getBoundsCenter(tld.getRotatedBounds(shape))
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -3,18 +3,15 @@ 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 { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
import {
|
import {
|
||||||
deepClone,
|
deepClone,
|
||||||
getBoundsCenter,
|
getBoundsCenter,
|
||||||
getBoundsFromPoints,
|
getBoundsFromPoints,
|
||||||
getCommonBounds,
|
getCommonBounds,
|
||||||
getDocumentBranch,
|
|
||||||
getPage,
|
|
||||||
getRelativeTransformedBoundingBox,
|
getRelativeTransformedBoundingBox,
|
||||||
getSelectedIds,
|
|
||||||
getTransformedBoundingBox,
|
getTransformedBoundingBox,
|
||||||
setToArray,
|
setToArray,
|
||||||
updateParents,
|
|
||||||
} from 'utils'
|
} from 'utils'
|
||||||
|
|
||||||
export default class TransformSession extends BaseSession {
|
export default class TransformSession extends BaseSession {
|
||||||
|
@ -36,7 +33,7 @@ export default class TransformSession extends BaseSession {
|
||||||
|
|
||||||
const { shapeBounds, initialBounds, isAllAspectRatioLocked } = this.snapshot
|
const { shapeBounds, initialBounds, isAllAspectRatioLocked } = this.snapshot
|
||||||
|
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
const newBoundingBox = getTransformedBoundingBox(
|
const newBoundingBox = getTransformedBoundingBox(
|
||||||
initialBounds,
|
initialBounds,
|
||||||
|
@ -76,13 +73,13 @@ export default class TransformSession extends BaseSession {
|
||||||
shapes[id] = { ...shape }
|
shapes[id] = { ...shape }
|
||||||
}
|
}
|
||||||
|
|
||||||
updateParents(data, Object.keys(shapeBounds))
|
tld.updateParents(data, Object.keys(shapeBounds))
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel(data: Data): void {
|
cancel(data: Data): void {
|
||||||
const { shapeBounds } = this.snapshot
|
const { shapeBounds } = this.snapshot
|
||||||
|
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
for (const id in shapeBounds) {
|
for (const id in shapeBounds) {
|
||||||
const shape = shapes[id]
|
const shape = shapes[id]
|
||||||
|
@ -98,7 +95,7 @@ export default class TransformSession extends BaseSession {
|
||||||
transformOrigin,
|
transformOrigin,
|
||||||
})
|
})
|
||||||
|
|
||||||
updateParents(data, Object.keys(shapeBounds))
|
tld.updateParents(data, Object.keys(shapeBounds))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +104,7 @@ export default class TransformSession extends BaseSession {
|
||||||
|
|
||||||
if (!hasUnlockedShapes) return
|
if (!hasUnlockedShapes) return
|
||||||
|
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
const finalShapes = initialShapes.map((shape) =>
|
const finalShapes = initialShapes.map((shape) =>
|
||||||
deepClone(page.shapes[shape.id])
|
deepClone(page.shapes[shape.id])
|
||||||
|
@ -120,10 +117,12 @@ export default class TransformSession extends BaseSession {
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
export function getTransformSnapshot(data: Data, transformType: Edge | Corner) {
|
export function getTransformSnapshot(data: Data, transformType: Edge | Corner) {
|
||||||
const { currentPageId } = data
|
const { currentPageId } = data
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
const initialShapes = setToArray(getSelectedIds(data))
|
const initialShapes = setToArray(tld.getSelectedIds(data))
|
||||||
.flatMap((id) => getDocumentBranch(data, id).map((id) => page.shapes[id]))
|
.flatMap((id) =>
|
||||||
|
tld.getDocumentBranch(data, id).map((id) => page.shapes[id])
|
||||||
|
)
|
||||||
.filter((shape) => !shape.isLocked)
|
.filter((shape) => !shape.isLocked)
|
||||||
.map((shape) => deepClone(shape))
|
.map((shape) => deepClone(shape))
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,8 @@ 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 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import {
|
import { getTransformedBoundingBox } from 'utils'
|
||||||
getTransformedBoundingBox,
|
import tld from 'utils/tld'
|
||||||
getPage,
|
|
||||||
getShape,
|
|
||||||
getSelectedShapes,
|
|
||||||
updateParents,
|
|
||||||
} from 'utils'
|
|
||||||
|
|
||||||
export default class TransformSingleSession extends BaseSession {
|
export default class TransformSingleSession extends BaseSession {
|
||||||
transformType: Edge | Corner
|
transformType: Edge | Corner
|
||||||
|
@ -38,7 +33,7 @@ export default class TransformSingleSession extends BaseSession {
|
||||||
|
|
||||||
const { initialShapeBounds, initialShape, id } = this.snapshot
|
const { initialShapeBounds, initialShape, id } = this.snapshot
|
||||||
|
|
||||||
const shape = getShape(data, id)
|
const shape = tld.getShape(data, id)
|
||||||
|
|
||||||
const newBoundingBox = getTransformedBoundingBox(
|
const newBoundingBox = getTransformedBoundingBox(
|
||||||
initialShapeBounds,
|
initialShapeBounds,
|
||||||
|
@ -61,16 +56,16 @@ export default class TransformSingleSession extends BaseSession {
|
||||||
|
|
||||||
data.document.pages[data.currentPageId].shapes[shape.id] = { ...shape }
|
data.document.pages[data.currentPageId].shapes[shape.id] = { ...shape }
|
||||||
|
|
||||||
updateParents(data, [id])
|
tld.updateParents(data, [id])
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel(data: Data): void {
|
cancel(data: Data): void {
|
||||||
const { id, initialShape } = this.snapshot
|
const { id, initialShape } = this.snapshot
|
||||||
|
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
page.shapes[id] = initialShape
|
page.shapes[id] = initialShape
|
||||||
|
|
||||||
updateParents(data, [id])
|
tld.updateParents(data, [id])
|
||||||
}
|
}
|
||||||
|
|
||||||
complete(data: Data): void {
|
complete(data: Data): void {
|
||||||
|
@ -90,7 +85,7 @@ export function getTransformSingleSnapshot(
|
||||||
data: Data,
|
data: Data,
|
||||||
transformType: Edge | Corner
|
transformType: Edge | Corner
|
||||||
) {
|
) {
|
||||||
const shape = getSelectedShapes(current(data))[0]
|
const shape = tld.getSelectedShapes(current(data))[0]
|
||||||
const bounds = getShapeUtils(shape).getBounds(shape)
|
const bounds = getShapeUtils(shape).getBounds(shape)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -4,15 +4,8 @@ import BaseSession from './base-session'
|
||||||
import commands from 'state/commands'
|
import commands from 'state/commands'
|
||||||
import { current } from 'immer'
|
import { current } from 'immer'
|
||||||
import { uniqueId } from 'utils'
|
import { uniqueId } from 'utils'
|
||||||
import {
|
|
||||||
getChildIndexAbove,
|
|
||||||
getDocumentBranch,
|
|
||||||
getPage,
|
|
||||||
getSelectedShapes,
|
|
||||||
setSelectedIds,
|
|
||||||
updateParents,
|
|
||||||
} from 'utils'
|
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
export default class TranslateSession extends BaseSession {
|
export default class TranslateSession extends BaseSession {
|
||||||
delta = [0, 0]
|
delta = [0, 0]
|
||||||
|
@ -34,7 +27,7 @@ export default class TranslateSession extends BaseSession {
|
||||||
isCloning: boolean
|
isCloning: boolean
|
||||||
): void {
|
): void {
|
||||||
const { clones, initialShapes, initialParents } = this.snapshot
|
const { clones, initialShapes, initialParents } = this.snapshot
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
const delta = vec.vec(this.origin, point)
|
const delta = vec.vec(this.origin, point)
|
||||||
|
|
||||||
|
@ -89,12 +82,12 @@ export default class TranslateSession extends BaseSession {
|
||||||
shapes[id] = { ...shape }
|
shapes[id] = { ...shape }
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedIds(
|
tld.setSelectedIds(
|
||||||
data,
|
data,
|
||||||
clones.map((c) => c.id)
|
clones.map((c) => c.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
updateParents(
|
tld.updateParents(
|
||||||
data,
|
data,
|
||||||
clones.map((c) => c.id)
|
clones.map((c) => c.id)
|
||||||
)
|
)
|
||||||
|
@ -102,7 +95,7 @@ export default class TranslateSession extends BaseSession {
|
||||||
if (this.isCloning) {
|
if (this.isCloning) {
|
||||||
this.isCloning = false
|
this.isCloning = false
|
||||||
|
|
||||||
setSelectedIds(
|
tld.setSelectedIds(
|
||||||
data,
|
data,
|
||||||
initialShapes.map((c) => c.id)
|
initialShapes.map((c) => c.id)
|
||||||
)
|
)
|
||||||
|
@ -112,7 +105,7 @@ export default class TranslateSession extends BaseSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const initialShape of initialShapes) {
|
for (const initialShape of initialShapes) {
|
||||||
getDocumentBranch(data, initialShape.id).forEach((id) => {
|
tld.getDocumentBranch(data, initialShape.id).forEach((id) => {
|
||||||
const shape = shapes[id]
|
const shape = shapes[id]
|
||||||
getShapeUtils(shape).translateBy(shape, delta)
|
getShapeUtils(shape).translateBy(shape, delta)
|
||||||
shapes[id] = { ...shape }
|
shapes[id] = { ...shape }
|
||||||
|
@ -126,7 +119,7 @@ export default class TranslateSession extends BaseSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const initialShape of initialShapes) {
|
for (const initialShape of initialShapes) {
|
||||||
getDocumentBranch(data, initialShape.id).forEach((id) => {
|
tld.getDocumentBranch(data, initialShape.id).forEach((id) => {
|
||||||
const shape = shapes[id]
|
const shape = shapes[id]
|
||||||
getShapeUtils(shape).translateBy(shape, trueDelta)
|
getShapeUtils(shape).translateBy(shape, trueDelta)
|
||||||
|
|
||||||
|
@ -134,7 +127,7 @@ export default class TranslateSession extends BaseSession {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
updateParents(
|
tld.updateParents(
|
||||||
data,
|
data,
|
||||||
initialShapes.map((s) => s.id)
|
initialShapes.map((s) => s.id)
|
||||||
)
|
)
|
||||||
|
@ -143,10 +136,10 @@ export default class TranslateSession extends BaseSession {
|
||||||
|
|
||||||
cancel(data: Data): void {
|
cancel(data: Data): void {
|
||||||
const { initialShapes, initialParents, clones } = this.snapshot
|
const { initialShapes, initialParents, clones } = this.snapshot
|
||||||
const { shapes } = getPage(data)
|
const { shapes } = tld.getPage(data)
|
||||||
|
|
||||||
for (const { id } of initialShapes) {
|
for (const { id } of initialShapes) {
|
||||||
getDocumentBranch(data, id).forEach((id) => {
|
tld.getDocumentBranch(data, id).forEach((id) => {
|
||||||
const shape = shapes[id]
|
const shape = shapes[id]
|
||||||
getShapeUtils(shape).translateBy(shape, vec.neg(this.delta))
|
getShapeUtils(shape).translateBy(shape, vec.neg(this.delta))
|
||||||
})
|
})
|
||||||
|
@ -161,7 +154,7 @@ export default class TranslateSession extends BaseSession {
|
||||||
getShapeUtils(shape).setProperty(shape, 'children', children)
|
getShapeUtils(shape).setProperty(shape, 'children', children)
|
||||||
})
|
})
|
||||||
|
|
||||||
updateParents(
|
tld.updateParents(
|
||||||
data,
|
data,
|
||||||
initialShapes.map((s) => s.id)
|
initialShapes.map((s) => s.id)
|
||||||
)
|
)
|
||||||
|
@ -190,10 +183,10 @@ export function getTranslateSnapshot(data: Data) {
|
||||||
// End up with an array of ids for all of the shapes that will change
|
// End up with an array of ids for all of the shapes that will change
|
||||||
// Map into shapes from data snapshot
|
// Map into shapes from data snapshot
|
||||||
|
|
||||||
const page = getPage(cData)
|
const page = tld.getPage(cData)
|
||||||
const selectedShapes = getSelectedShapes(cData).filter(
|
const selectedShapes = tld
|
||||||
(shape) => !shape.isLocked
|
.getSelectedShapes(cData)
|
||||||
)
|
.filter((shape) => !shape.isLocked)
|
||||||
|
|
||||||
const hasUnlockedShapes = selectedShapes.length > 0
|
const hasUnlockedShapes = selectedShapes.length > 0
|
||||||
|
|
||||||
|
@ -220,7 +213,7 @@ export function getTranslateSnapshot(data: Data) {
|
||||||
id: uniqueId(),
|
id: uniqueId(),
|
||||||
|
|
||||||
parentId: shape.parentId,
|
parentId: shape.parentId,
|
||||||
childIndex: getChildIndexAbove(cData, shape.id),
|
childIndex: tld.getChildIndexAbove(cData, shape.id),
|
||||||
isGenerated: false,
|
isGenerated: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +229,7 @@ export type TranslateSnapshot = ReturnType<typeof getTranslateSnapshot>
|
||||||
// return [clone]
|
// return [clone]
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// const page = getPage(data)
|
// const page = tld.getPage(data)
|
||||||
// const childClones = clone.children.flatMap((id) => {
|
// const childClones = clone.children.flatMap((id) => {
|
||||||
// const newId = uniqueId()
|
// const newId = uniqueId()
|
||||||
// const source = page.shapes[id]
|
// const source = page.shapes[id]
|
||||||
|
|
237
state/state.ts
237
state/state.ts
|
@ -7,31 +7,18 @@ import history from './history'
|
||||||
import storage from './storage'
|
import storage from './storage'
|
||||||
import clipboard from './clipboard'
|
import clipboard from './clipboard'
|
||||||
import * as Sessions from './sessions'
|
import * as Sessions from './sessions'
|
||||||
import pusher from './pusher/client'
|
import pusher from './pusher/client-supa'
|
||||||
import commands from './commands'
|
import commands from './commands'
|
||||||
import {
|
import {
|
||||||
getChildren,
|
|
||||||
getCommonBounds,
|
getCommonBounds,
|
||||||
getCurrentCamera,
|
|
||||||
getPage,
|
|
||||||
getSelectedBounds,
|
|
||||||
getSelectedShapes,
|
|
||||||
getShape,
|
|
||||||
screenToWorld,
|
|
||||||
setZoomCSS,
|
|
||||||
rotateBounds,
|
rotateBounds,
|
||||||
getBoundsCenter,
|
getBoundsCenter,
|
||||||
getDocumentBranch,
|
|
||||||
getCameraZoom,
|
|
||||||
getSelectedIds,
|
|
||||||
setSelectedIds,
|
|
||||||
getPageState,
|
|
||||||
setToArray,
|
setToArray,
|
||||||
deepClone,
|
deepClone,
|
||||||
pointInBounds,
|
pointInBounds,
|
||||||
uniqueId,
|
uniqueId,
|
||||||
} from 'utils'
|
} from 'utils'
|
||||||
|
import tld from 'utils/tld'
|
||||||
import {
|
import {
|
||||||
Data,
|
Data,
|
||||||
PointerInfo,
|
PointerInfo,
|
||||||
|
@ -49,6 +36,7 @@ import {
|
||||||
SizeStyle,
|
SizeStyle,
|
||||||
ColorStyle,
|
ColorStyle,
|
||||||
} from 'types'
|
} from 'types'
|
||||||
|
|
||||||
import session from './session'
|
import session from './session'
|
||||||
|
|
||||||
const initialData: Data = {
|
const initialData: Data = {
|
||||||
|
@ -1041,10 +1029,10 @@ const state = createState({
|
||||||
return ShapeType.Rectangle
|
return ShapeType.Rectangle
|
||||||
},
|
},
|
||||||
firstSelectedShape(data) {
|
firstSelectedShape(data) {
|
||||||
return getSelectedShapes(data)[0]
|
return tld.getSelectedShapes(data)[0]
|
||||||
},
|
},
|
||||||
editingShape(data) {
|
editingShape(data) {
|
||||||
return getShape(data, data.editingId)
|
return tld.getShape(data, data.editingId)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
conditions: {
|
conditions: {
|
||||||
|
@ -1058,7 +1046,7 @@ const state = createState({
|
||||||
return payload.target === 'canvas'
|
return payload.target === 'canvas'
|
||||||
},
|
},
|
||||||
isPointingBounds(data, payload: PointerInfo) {
|
isPointingBounds(data, payload: PointerInfo) {
|
||||||
return getSelectedIds(data).size > 0 && payload.target === 'bounds'
|
return tld.getSelectedIds(data).size > 0 && payload.target === 'bounds'
|
||||||
},
|
},
|
||||||
isPointingShape(data, payload: PointerInfo) {
|
isPointingShape(data, payload: PointerInfo) {
|
||||||
return (
|
return (
|
||||||
|
@ -1083,7 +1071,7 @@ const state = createState({
|
||||||
return payload.target !== undefined
|
return payload.target !== undefined
|
||||||
},
|
},
|
||||||
isPointedShapeSelected(data) {
|
isPointedShapeSelected(data) {
|
||||||
return getSelectedIds(data).has(data.pointedId)
|
return tld.getSelectedIds(data).has(data.pointedId)
|
||||||
},
|
},
|
||||||
isPressingShiftKey(data, payload: PointerInfo) {
|
isPressingShiftKey(data, payload: PointerInfo) {
|
||||||
return payload.shiftKey
|
return payload.shiftKey
|
||||||
|
@ -1099,18 +1087,18 @@ const state = createState({
|
||||||
|
|
||||||
if (!bounds) return false
|
if (!bounds) return false
|
||||||
|
|
||||||
return pointInBounds(screenToWorld(payload.point, data), bounds)
|
return pointInBounds(tld.screenToWorld(payload.point, data), bounds)
|
||||||
},
|
},
|
||||||
pointHitsShape(data, payload: PointerInfo) {
|
pointHitsShape(data, payload: PointerInfo) {
|
||||||
const shape = getShape(data, payload.target)
|
const shape = tld.getShape(data, payload.target)
|
||||||
|
|
||||||
return getShapeUtils(shape).hitTest(
|
return getShapeUtils(shape).hitTest(
|
||||||
shape,
|
shape,
|
||||||
screenToWorld(payload.point, data)
|
tld.screenToWorld(payload.point, data)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
hasPointedId(data, payload: PointerInfo) {
|
hasPointedId(data, payload: PointerInfo) {
|
||||||
return getShape(data, payload.target) !== undefined
|
return tld.getShape(data, payload.target) !== undefined
|
||||||
},
|
},
|
||||||
isPointingRotationHandle(
|
isPointingRotationHandle(
|
||||||
data,
|
data,
|
||||||
|
@ -1119,13 +1107,13 @@ const state = createState({
|
||||||
return payload.target === 'rotate'
|
return payload.target === 'rotate'
|
||||||
},
|
},
|
||||||
hasSelection(data) {
|
hasSelection(data) {
|
||||||
return getSelectedIds(data).size > 0
|
return tld.getSelectedIds(data).size > 0
|
||||||
},
|
},
|
||||||
hasSingleSelection(data) {
|
hasSingleSelection(data) {
|
||||||
return getSelectedIds(data).size === 1
|
return tld.getSelectedIds(data).size === 1
|
||||||
},
|
},
|
||||||
hasMultipleSelection(data) {
|
hasMultipleSelection(data) {
|
||||||
return getSelectedIds(data).size > 1
|
return tld.getSelectedIds(data).size > 1
|
||||||
},
|
},
|
||||||
isToolLocked(data) {
|
isToolLocked(data) {
|
||||||
return data.settings.isToolLocked
|
return data.settings.isToolLocked
|
||||||
|
@ -1137,9 +1125,9 @@ const state = createState({
|
||||||
return Object.keys(data.document.pages).length === 1
|
return Object.keys(data.document.pages).length === 1
|
||||||
},
|
},
|
||||||
selectionIncludesGroups(data) {
|
selectionIncludesGroups(data) {
|
||||||
return getSelectedShapes(data).some(
|
return tld
|
||||||
(shape) => shape.type === ShapeType.Group
|
.getSelectedShapes(data)
|
||||||
)
|
.some((shape) => shape.type === ShapeType.Group)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
@ -1174,7 +1162,7 @@ const state = createState({
|
||||||
Object.assign(data.document[pageId].shapes[shape.id], shape)
|
Object.assign(data.document[pageId].shapes[shape.id], shape)
|
||||||
},
|
},
|
||||||
sendRtCursorMove(data, payload: PointerInfo) {
|
sendRtCursorMove(data, payload: PointerInfo) {
|
||||||
const point = screenToWorld(payload.point, data)
|
const point = tld.screenToWorld(payload.point, data)
|
||||||
pusher.moveCursor(data.currentPageId, point)
|
pusher.moveCursor(data.currentPageId, point)
|
||||||
},
|
},
|
||||||
moveRtCursor(
|
moveRtCursor(
|
||||||
|
@ -1250,7 +1238,7 @@ const state = createState({
|
||||||
},
|
},
|
||||||
/* --------------------- Shapes --------------------- */
|
/* --------------------- Shapes --------------------- */
|
||||||
resetShapes(data) {
|
resetShapes(data) {
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
Object.values(page.shapes).forEach((shape) => {
|
Object.values(page.shapes).forEach((shape) => {
|
||||||
page.shapes[shape.id] = { ...shape }
|
page.shapes[shape.id] = { ...shape }
|
||||||
})
|
})
|
||||||
|
@ -1259,11 +1247,11 @@ const state = createState({
|
||||||
createShape(data, payload, type: ShapeType) {
|
createShape(data, payload, type: ShapeType) {
|
||||||
const shape = createShape(type, {
|
const shape = createShape(type, {
|
||||||
parentId: data.currentPageId,
|
parentId: data.currentPageId,
|
||||||
point: vec.round(screenToWorld(payload.point, data)),
|
point: vec.round(tld.screenToWorld(payload.point, data)),
|
||||||
style: deepClone(data.currentStyle),
|
style: deepClone(data.currentStyle),
|
||||||
})
|
})
|
||||||
|
|
||||||
const siblings = getChildren(data, shape.parentId)
|
const siblings = tld.getChildren(data, shape.parentId)
|
||||||
const childIndex = siblings.length
|
const childIndex = siblings.length
|
||||||
? siblings[siblings.length - 1].childIndex + 1
|
? siblings[siblings.length - 1].childIndex + 1
|
||||||
: 1
|
: 1
|
||||||
|
@ -1272,9 +1260,9 @@ const state = createState({
|
||||||
|
|
||||||
getShapeUtils(shape).setProperty(shape, 'childIndex', childIndex)
|
getShapeUtils(shape).setProperty(shape, 'childIndex', childIndex)
|
||||||
|
|
||||||
getPage(data).shapes[shape.id] = shape
|
tld.getPage(data).shapes[shape.id] = shape
|
||||||
|
|
||||||
setSelectedIds(data, [shape.id])
|
tld.setSelectedIds(data, [shape.id])
|
||||||
},
|
},
|
||||||
/* -------------------- Sessions -------------------- */
|
/* -------------------- Sessions -------------------- */
|
||||||
|
|
||||||
|
@ -1303,33 +1291,33 @@ const state = createState({
|
||||||
// Brushing
|
// Brushing
|
||||||
startBrushSession(data, payload: PointerInfo) {
|
startBrushSession(data, payload: PointerInfo) {
|
||||||
session.begin(
|
session.begin(
|
||||||
new Sessions.BrushSession(data, screenToWorld(payload.point, data))
|
new Sessions.BrushSession(data, tld.screenToWorld(payload.point, data))
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
updateBrushSession(data, payload: PointerInfo) {
|
updateBrushSession(data, payload: PointerInfo) {
|
||||||
session.update<Sessions.BrushSession>(
|
session.update<Sessions.BrushSession>(
|
||||||
data,
|
data,
|
||||||
screenToWorld(payload.point, data)
|
tld.screenToWorld(payload.point, data)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Rotating
|
// Rotating
|
||||||
startRotateSession(data, payload: PointerInfo) {
|
startRotateSession(data, payload: PointerInfo) {
|
||||||
session.begin(
|
session.begin(
|
||||||
new Sessions.RotateSession(data, screenToWorld(payload.point, data))
|
new Sessions.RotateSession(data, tld.screenToWorld(payload.point, data))
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
keyUpdateRotateSession(data, payload: PointerInfo) {
|
keyUpdateRotateSession(data, payload: PointerInfo) {
|
||||||
session.update<Sessions.RotateSession>(
|
session.update<Sessions.RotateSession>(
|
||||||
data,
|
data,
|
||||||
screenToWorld(inputs.pointer.point, data),
|
tld.screenToWorld(inputs.pointer.point, data),
|
||||||
payload.shiftKey
|
payload.shiftKey
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
updateRotateSession(data, payload: PointerInfo) {
|
updateRotateSession(data, payload: PointerInfo) {
|
||||||
session.update<Sessions.RotateSession>(
|
session.update<Sessions.RotateSession>(
|
||||||
data,
|
data,
|
||||||
screenToWorld(payload.point, data),
|
tld.screenToWorld(payload.point, data),
|
||||||
payload.shiftKey
|
payload.shiftKey
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -1339,7 +1327,7 @@ const state = createState({
|
||||||
session.begin(
|
session.begin(
|
||||||
new Sessions.TranslateSession(
|
new Sessions.TranslateSession(
|
||||||
data,
|
data,
|
||||||
screenToWorld(inputs.pointer.origin, data)
|
tld.screenToWorld(inputs.pointer.origin, data)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -1349,7 +1337,7 @@ const state = createState({
|
||||||
) {
|
) {
|
||||||
session.update<Sessions.TranslateSession>(
|
session.update<Sessions.TranslateSession>(
|
||||||
data,
|
data,
|
||||||
screenToWorld(inputs.pointer.point, data),
|
tld.screenToWorld(inputs.pointer.point, data),
|
||||||
payload.shiftKey,
|
payload.shiftKey,
|
||||||
payload.altKey
|
payload.altKey
|
||||||
)
|
)
|
||||||
|
@ -1357,7 +1345,7 @@ const state = createState({
|
||||||
updateTranslateSession(data, payload: PointerInfo) {
|
updateTranslateSession(data, payload: PointerInfo) {
|
||||||
session.update<Sessions.TranslateSession>(
|
session.update<Sessions.TranslateSession>(
|
||||||
data,
|
data,
|
||||||
screenToWorld(payload.point, data),
|
tld.screenToWorld(payload.point, data),
|
||||||
payload.shiftKey,
|
payload.shiftKey,
|
||||||
payload.altKey
|
payload.altKey
|
||||||
)
|
)
|
||||||
|
@ -1365,13 +1353,13 @@ const state = createState({
|
||||||
|
|
||||||
// Handles
|
// Handles
|
||||||
doublePointHandle(data, payload: PointerInfo) {
|
doublePointHandle(data, payload: PointerInfo) {
|
||||||
const id = setToArray(getSelectedIds(data))[0]
|
const id = setToArray(tld.getSelectedIds(data))[0]
|
||||||
commands.doublePointHandle(data, id, payload)
|
commands.doublePointHandle(data, id, payload)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Dragging Handle
|
// Dragging Handle
|
||||||
startHandleSession(data, payload: PointerInfo) {
|
startHandleSession(data, payload: PointerInfo) {
|
||||||
const shapeId = Array.from(getSelectedIds(data).values())[0]
|
const shapeId = Array.from(tld.getSelectedIds(data).values())[0]
|
||||||
const handleId = payload.target
|
const handleId = payload.target
|
||||||
|
|
||||||
session.begin(
|
session.begin(
|
||||||
|
@ -1379,7 +1367,7 @@ const state = createState({
|
||||||
data,
|
data,
|
||||||
shapeId,
|
shapeId,
|
||||||
handleId,
|
handleId,
|
||||||
screenToWorld(inputs.pointer.origin, data)
|
tld.screenToWorld(inputs.pointer.origin, data)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -1389,14 +1377,14 @@ const state = createState({
|
||||||
) {
|
) {
|
||||||
session.update<Sessions.HandleSession>(
|
session.update<Sessions.HandleSession>(
|
||||||
data,
|
data,
|
||||||
screenToWorld(inputs.pointer.point, data),
|
tld.screenToWorld(inputs.pointer.point, data),
|
||||||
payload.shiftKey
|
payload.shiftKey
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
updateHandleSession(data, payload: PointerInfo) {
|
updateHandleSession(data, payload: PointerInfo) {
|
||||||
session.update<Sessions.HandleSession>(
|
session.update<Sessions.HandleSession>(
|
||||||
data,
|
data,
|
||||||
screenToWorld(payload.point, data),
|
tld.screenToWorld(payload.point, data),
|
||||||
payload.shiftKey
|
payload.shiftKey
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -1406,9 +1394,9 @@ const state = createState({
|
||||||
data,
|
data,
|
||||||
payload: PointerInfo & { target: Corner | Edge }
|
payload: PointerInfo & { target: Corner | Edge }
|
||||||
) {
|
) {
|
||||||
const point = screenToWorld(inputs.pointer.origin, data)
|
const point = tld.screenToWorld(inputs.pointer.origin, data)
|
||||||
session.begin(
|
session.begin(
|
||||||
getSelectedIds(data).size === 1
|
tld.getSelectedIds(data).size === 1
|
||||||
? new Sessions.TransformSingleSession(data, payload.target, point)
|
? new Sessions.TransformSingleSession(data, payload.target, point)
|
||||||
: new Sessions.TransformSession(data, payload.target, point)
|
: new Sessions.TransformSession(data, payload.target, point)
|
||||||
)
|
)
|
||||||
|
@ -1418,7 +1406,7 @@ const state = createState({
|
||||||
new Sessions.TransformSingleSession(
|
new Sessions.TransformSingleSession(
|
||||||
data,
|
data,
|
||||||
Corner.BottomRight,
|
Corner.BottomRight,
|
||||||
screenToWorld(payload.point, data),
|
tld.screenToWorld(payload.point, data),
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -1426,14 +1414,14 @@ const state = createState({
|
||||||
keyUpdateTransformSession(data, payload: PointerInfo) {
|
keyUpdateTransformSession(data, payload: PointerInfo) {
|
||||||
session.update<Sessions.TransformSession>(
|
session.update<Sessions.TransformSession>(
|
||||||
data,
|
data,
|
||||||
screenToWorld(inputs.pointer.point, data),
|
tld.screenToWorld(inputs.pointer.point, data),
|
||||||
payload.shiftKey
|
payload.shiftKey
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
updateTransformSession(data, payload: PointerInfo) {
|
updateTransformSession(data, payload: PointerInfo) {
|
||||||
session.update<Sessions.TransformSession>(
|
session.update<Sessions.TransformSession>(
|
||||||
data,
|
data,
|
||||||
screenToWorld(payload.point, data),
|
tld.screenToWorld(payload.point, data),
|
||||||
payload.shiftKey
|
payload.shiftKey
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -1443,32 +1431,32 @@ const state = createState({
|
||||||
session.begin(
|
session.begin(
|
||||||
new Sessions.DirectionSession(
|
new Sessions.DirectionSession(
|
||||||
data,
|
data,
|
||||||
screenToWorld(inputs.pointer.origin, data)
|
tld.screenToWorld(inputs.pointer.origin, data)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
updateDirectionSession(data, payload: PointerInfo) {
|
updateDirectionSession(data, payload: PointerInfo) {
|
||||||
session.update<Sessions.DirectionSession>(
|
session.update<Sessions.DirectionSession>(
|
||||||
data,
|
data,
|
||||||
screenToWorld(payload.point, data)
|
tld.screenToWorld(payload.point, data)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Drawing
|
// Drawing
|
||||||
startDrawSession(data) {
|
startDrawSession(data) {
|
||||||
const id = Array.from(getSelectedIds(data).values())[0]
|
const id = Array.from(tld.getSelectedIds(data).values())[0]
|
||||||
session.begin(
|
session.begin(
|
||||||
new Sessions.DrawSession(
|
new Sessions.DrawSession(
|
||||||
data,
|
data,
|
||||||
id,
|
id,
|
||||||
screenToWorld(inputs.pointer.origin, data)
|
tld.screenToWorld(inputs.pointer.origin, data)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
keyUpdateDrawSession(data, payload: PointerInfo) {
|
keyUpdateDrawSession(data, payload: PointerInfo) {
|
||||||
session.update<Sessions.DrawSession>(
|
session.update<Sessions.DrawSession>(
|
||||||
data,
|
data,
|
||||||
screenToWorld(inputs.pointer.point, data),
|
tld.screenToWorld(inputs.pointer.point, data),
|
||||||
payload.pressure,
|
payload.pressure,
|
||||||
payload.shiftKey
|
payload.shiftKey
|
||||||
)
|
)
|
||||||
|
@ -1476,7 +1464,7 @@ const state = createState({
|
||||||
updateDrawSession(data, payload: PointerInfo) {
|
updateDrawSession(data, payload: PointerInfo) {
|
||||||
session.update<Sessions.DrawSession>(
|
session.update<Sessions.DrawSession>(
|
||||||
data,
|
data,
|
||||||
screenToWorld(payload.point, data),
|
tld.screenToWorld(payload.point, data),
|
||||||
payload.pressure,
|
payload.pressure,
|
||||||
payload.shiftKey
|
payload.shiftKey
|
||||||
)
|
)
|
||||||
|
@ -1484,13 +1472,13 @@ const state = createState({
|
||||||
|
|
||||||
// Arrow
|
// Arrow
|
||||||
startArrowSession(data, payload: PointerInfo) {
|
startArrowSession(data, payload: PointerInfo) {
|
||||||
const id = Array.from(getSelectedIds(data).values())[0]
|
const id = Array.from(tld.getSelectedIds(data).values())[0]
|
||||||
|
|
||||||
session.begin(
|
session.begin(
|
||||||
new Sessions.ArrowSession(
|
new Sessions.ArrowSession(
|
||||||
data,
|
data,
|
||||||
id,
|
id,
|
||||||
screenToWorld(inputs.pointer.origin, data),
|
tld.screenToWorld(inputs.pointer.origin, data),
|
||||||
payload.shiftKey
|
payload.shiftKey
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -1498,14 +1486,14 @@ const state = createState({
|
||||||
keyUpdateArrowSession(data, payload: PointerInfo) {
|
keyUpdateArrowSession(data, payload: PointerInfo) {
|
||||||
session.update<Sessions.ArrowSession>(
|
session.update<Sessions.ArrowSession>(
|
||||||
data,
|
data,
|
||||||
screenToWorld(inputs.pointer.point, data),
|
tld.screenToWorld(inputs.pointer.point, data),
|
||||||
payload.shiftKey
|
payload.shiftKey
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
updateArrowSession(data, payload: PointerInfo) {
|
updateArrowSession(data, payload: PointerInfo) {
|
||||||
session.update<Sessions.ArrowSession>(
|
session.update<Sessions.ArrowSession>(
|
||||||
data,
|
data,
|
||||||
screenToWorld(payload.point, data),
|
tld.screenToWorld(payload.point, data),
|
||||||
payload.shiftKey
|
payload.shiftKey
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -1530,8 +1518,8 @@ const state = createState({
|
||||||
},
|
},
|
||||||
|
|
||||||
selectAll(data) {
|
selectAll(data) {
|
||||||
const selectedIds = getSelectedIds(data)
|
const selectedIds = tld.getSelectedIds(data)
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
selectedIds.clear()
|
selectedIds.clear()
|
||||||
for (const id in page.shapes) {
|
for (const id in page.shapes) {
|
||||||
if (page.shapes[id].parentId === data.currentPageId) {
|
if (page.shapes[id].parentId === data.currentPageId) {
|
||||||
|
@ -1561,15 +1549,15 @@ const state = createState({
|
||||||
data.pointedId = undefined
|
data.pointedId = undefined
|
||||||
},
|
},
|
||||||
clearSelectedIds(data) {
|
clearSelectedIds(data) {
|
||||||
setSelectedIds(data, [])
|
tld.setSelectedIds(data, [])
|
||||||
},
|
},
|
||||||
pullPointedIdFromSelectedIds(data) {
|
pullPointedIdFromSelectedIds(data) {
|
||||||
const { pointedId } = data
|
const { pointedId } = data
|
||||||
const selectedIds = getSelectedIds(data)
|
const selectedIds = tld.getSelectedIds(data)
|
||||||
selectedIds.delete(pointedId)
|
selectedIds.delete(pointedId)
|
||||||
},
|
},
|
||||||
pushPointedIdToSelectedIds(data) {
|
pushPointedIdToSelectedIds(data) {
|
||||||
getSelectedIds(data).add(data.pointedId)
|
tld.getSelectedIds(data).add(data.pointedId)
|
||||||
},
|
},
|
||||||
moveSelection(data, payload: { type: MoveType }) {
|
moveSelection(data, payload: { type: MoveType }) {
|
||||||
commands.move(data, payload.type)
|
commands.move(data, payload.type)
|
||||||
|
@ -1620,12 +1608,12 @@ const state = createState({
|
||||||
/* --------------------- Editing -------------------- */
|
/* --------------------- Editing -------------------- */
|
||||||
|
|
||||||
setEditingId(data) {
|
setEditingId(data) {
|
||||||
const selectedShape = getSelectedShapes(data)[0]
|
const selectedShape = tld.getSelectedShapes(data)[0]
|
||||||
if (getShapeUtils(selectedShape).canEdit) {
|
if (getShapeUtils(selectedShape).canEdit) {
|
||||||
data.editingId = selectedShape.id
|
data.editingId = selectedShape.id
|
||||||
}
|
}
|
||||||
|
|
||||||
getPageState(data).selectedIds = new Set([selectedShape.id])
|
tld.getPageState(data).selectedIds = new Set([selectedShape.id])
|
||||||
},
|
},
|
||||||
clearEditingId(data) {
|
clearEditingId(data) {
|
||||||
data.editingId = null
|
data.editingId = null
|
||||||
|
@ -1670,44 +1658,43 @@ const state = createState({
|
||||||
/* --------------------- Camera --------------------- */
|
/* --------------------- Camera --------------------- */
|
||||||
|
|
||||||
zoomIn(data) {
|
zoomIn(data) {
|
||||||
const camera = getCurrentCamera(data)
|
const camera = tld.getCurrentCamera(data)
|
||||||
const i = Math.round((camera.zoom * 100) / 25)
|
const i = Math.round((camera.zoom * 100) / 25)
|
||||||
const center = [window.innerWidth / 2, window.innerHeight / 2]
|
const center = [window.innerWidth / 2, window.innerHeight / 2]
|
||||||
|
|
||||||
const p0 = screenToWorld(center, data)
|
const p0 = tld.screenToWorld(center, data)
|
||||||
camera.zoom = getCameraZoom((i + 1) * 0.25)
|
camera.zoom = tld.getCameraZoom((i + 1) * 0.25)
|
||||||
const p1 = screenToWorld(center, data)
|
const p1 = tld.screenToWorld(center, data)
|
||||||
camera.point = vec.add(camera.point, vec.sub(p1, p0))
|
camera.point = vec.add(camera.point, vec.sub(p1, p0))
|
||||||
|
|
||||||
setZoomCSS(camera.zoom)
|
tld.setZoomCSS(camera.zoom)
|
||||||
},
|
},
|
||||||
zoomOut(data) {
|
zoomOut(data) {
|
||||||
const camera = getCurrentCamera(data)
|
const camera = tld.getCurrentCamera(data)
|
||||||
const i = Math.round((camera.zoom * 100) / 25)
|
const i = Math.round((camera.zoom * 100) / 25)
|
||||||
const center = [window.innerWidth / 2, window.innerHeight / 2]
|
const center = [window.innerWidth / 2, window.innerHeight / 2]
|
||||||
|
|
||||||
const p0 = screenToWorld(center, data)
|
const p0 = tld.screenToWorld(center, data)
|
||||||
camera.zoom = getCameraZoom((i - 1) * 0.25)
|
camera.zoom = tld.getCameraZoom((i - 1) * 0.25)
|
||||||
const p1 = screenToWorld(center, data)
|
const p1 = tld.screenToWorld(center, data)
|
||||||
camera.point = vec.add(camera.point, vec.sub(p1, p0))
|
camera.point = vec.add(camera.point, vec.sub(p1, p0))
|
||||||
|
|
||||||
setZoomCSS(camera.zoom)
|
tld.setZoomCSS(camera.zoom)
|
||||||
},
|
},
|
||||||
zoomCameraToActual(data) {
|
zoomCameraToActual(data) {
|
||||||
const camera = getCurrentCamera(data)
|
const camera = tld.getCurrentCamera(data)
|
||||||
const center = [window.innerWidth / 2, window.innerHeight / 2]
|
const center = [window.innerWidth / 2, window.innerHeight / 2]
|
||||||
|
|
||||||
const p0 = screenToWorld(center, data)
|
const p0 = tld.screenToWorld(center, data)
|
||||||
camera.zoom = 1
|
camera.zoom = 1
|
||||||
const p1 = screenToWorld(center, data)
|
const p1 = tld.screenToWorld(center, data)
|
||||||
camera.point = vec.add(camera.point, vec.sub(p1, p0))
|
camera.point = vec.add(camera.point, vec.sub(p1, p0))
|
||||||
|
|
||||||
setZoomCSS(camera.zoom)
|
tld.setZoomCSS(camera.zoom)
|
||||||
},
|
},
|
||||||
zoomCameraToSelectionActual(data) {
|
zoomCameraToSelectionActual(data) {
|
||||||
const camera = getCurrentCamera(data)
|
const camera = tld.getCurrentCamera(data)
|
||||||
|
const bounds = tld.getSelectedBounds(data)
|
||||||
const bounds = getSelectedBounds(data)
|
|
||||||
|
|
||||||
const mx = (window.innerWidth - bounds.width) / 2
|
const mx = (window.innerWidth - bounds.width) / 2
|
||||||
const my = (window.innerHeight - bounds.height) / 2
|
const my = (window.innerHeight - bounds.height) / 2
|
||||||
|
@ -1715,13 +1702,13 @@ const state = createState({
|
||||||
camera.zoom = 1
|
camera.zoom = 1
|
||||||
camera.point = vec.add([-bounds.minX, -bounds.minY], [mx, my])
|
camera.point = vec.add([-bounds.minX, -bounds.minY], [mx, my])
|
||||||
|
|
||||||
setZoomCSS(camera.zoom)
|
tld.setZoomCSS(camera.zoom)
|
||||||
},
|
},
|
||||||
zoomCameraToSelection(data) {
|
zoomCameraToSelection(data) {
|
||||||
const camera = getCurrentCamera(data)
|
const camera = tld.getCurrentCamera(data)
|
||||||
const bounds = getSelectedBounds(data)
|
const bounds = tld.getSelectedBounds(data)
|
||||||
|
|
||||||
const zoom = getCameraZoom(
|
const zoom = tld.getCameraZoom(
|
||||||
bounds.width > bounds.height
|
bounds.width > bounds.height
|
||||||
? (window.innerWidth - 128) / bounds.width
|
? (window.innerWidth - 128) / bounds.width
|
||||||
: (window.innerHeight - 128) / bounds.height
|
: (window.innerHeight - 128) / bounds.height
|
||||||
|
@ -1733,11 +1720,11 @@ const state = createState({
|
||||||
camera.zoom = zoom
|
camera.zoom = zoom
|
||||||
camera.point = vec.add([-bounds.minX, -bounds.minY], [mx, my])
|
camera.point = vec.add([-bounds.minX, -bounds.minY], [mx, my])
|
||||||
|
|
||||||
setZoomCSS(camera.zoom)
|
tld.setZoomCSS(camera.zoom)
|
||||||
},
|
},
|
||||||
zoomCameraToFit(data) {
|
zoomCameraToFit(data) {
|
||||||
const camera = getCurrentCamera(data)
|
const camera = tld.getCurrentCamera(data)
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
const shapes = Object.values(page.shapes)
|
const shapes = Object.values(page.shapes)
|
||||||
|
|
||||||
|
@ -1751,7 +1738,7 @@ const state = createState({
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
const zoom = getCameraZoom(
|
const zoom = tld.getCameraZoom(
|
||||||
bounds.width > bounds.height
|
bounds.width > bounds.height
|
||||||
? (window.innerWidth - 128) / bounds.width
|
? (window.innerWidth - 128) / bounds.width
|
||||||
: (window.innerHeight - 128) / bounds.height
|
: (window.innerHeight - 128) / bounds.height
|
||||||
|
@ -1763,26 +1750,26 @@ const state = createState({
|
||||||
camera.zoom = zoom
|
camera.zoom = zoom
|
||||||
camera.point = vec.add([-bounds.minX, -bounds.minY], [mx, my])
|
camera.point = vec.add([-bounds.minX, -bounds.minY], [mx, my])
|
||||||
|
|
||||||
setZoomCSS(camera.zoom)
|
tld.setZoomCSS(camera.zoom)
|
||||||
},
|
},
|
||||||
zoomCamera(data, payload: { delta: number; point: number[] }) {
|
zoomCamera(data, payload: { delta: number; point: number[] }) {
|
||||||
const camera = getCurrentCamera(data)
|
const camera = tld.getCurrentCamera(data)
|
||||||
const next = camera.zoom - (payload.delta / 100) * camera.zoom
|
const next = camera.zoom - (payload.delta / 100) * camera.zoom
|
||||||
|
|
||||||
const p0 = screenToWorld(payload.point, data)
|
const p0 = tld.screenToWorld(payload.point, data)
|
||||||
camera.zoom = getCameraZoom(next)
|
camera.zoom = tld.getCameraZoom(next)
|
||||||
const p1 = screenToWorld(payload.point, data)
|
const p1 = tld.screenToWorld(payload.point, data)
|
||||||
camera.point = vec.add(camera.point, vec.sub(p1, p0))
|
camera.point = vec.add(camera.point, vec.sub(p1, p0))
|
||||||
|
|
||||||
setZoomCSS(camera.zoom)
|
tld.setZoomCSS(camera.zoom)
|
||||||
},
|
},
|
||||||
panCamera(data, payload: { delta: number[] }) {
|
panCamera(data, payload: { delta: number[] }) {
|
||||||
const camera = getCurrentCamera(data)
|
const camera = tld.getCurrentCamera(data)
|
||||||
camera.point = vec.sub(camera.point, vec.div(payload.delta, camera.zoom))
|
camera.point = vec.sub(camera.point, vec.div(payload.delta, camera.zoom))
|
||||||
},
|
},
|
||||||
updateZoomCSS(data) {
|
updateZoomCSS(data) {
|
||||||
const camera = getCurrentCamera(data)
|
const camera = tld.getCurrentCamera(data)
|
||||||
setZoomCSS(camera.zoom)
|
tld.setZoomCSS(camera.zoom)
|
||||||
},
|
},
|
||||||
pinchCamera(
|
pinchCamera(
|
||||||
data,
|
data,
|
||||||
|
@ -1795,20 +1782,20 @@ const state = createState({
|
||||||
) {
|
) {
|
||||||
// This is usually replaced with hacks.fastPinchCamera!
|
// This is usually replaced with hacks.fastPinchCamera!
|
||||||
|
|
||||||
const camera = getCurrentCamera(data)
|
const camera = tld.getCurrentCamera(data)
|
||||||
camera.point = vec.sub(camera.point, vec.div(payload.delta, camera.zoom))
|
camera.point = vec.sub(camera.point, vec.div(payload.delta, camera.zoom))
|
||||||
|
|
||||||
const next = camera.zoom - (payload.distanceDelta / 300) * camera.zoom
|
const next = camera.zoom - (payload.distanceDelta / 300) * camera.zoom
|
||||||
|
|
||||||
const p0 = screenToWorld(payload.point, data)
|
const p0 = tld.screenToWorld(payload.point, data)
|
||||||
camera.zoom = getCameraZoom(next)
|
camera.zoom = tld.getCameraZoom(next)
|
||||||
const p1 = screenToWorld(payload.point, data)
|
const p1 = tld.screenToWorld(payload.point, data)
|
||||||
camera.point = vec.add(camera.point, vec.sub(p1, p0))
|
camera.point = vec.add(camera.point, vec.sub(p1, p0))
|
||||||
|
|
||||||
setZoomCSS(camera.zoom)
|
tld.setZoomCSS(camera.zoom)
|
||||||
},
|
},
|
||||||
resetCamera(data) {
|
resetCamera(data) {
|
||||||
const camera = getCurrentCamera(data)
|
const camera = tld.getCurrentCamera(data)
|
||||||
camera.zoom = 1
|
camera.zoom = 1
|
||||||
camera.point = [window.innerWidth / 2, window.innerHeight / 2]
|
camera.point = [window.innerWidth / 2, window.innerHeight / 2]
|
||||||
document.documentElement.style.setProperty('--camera-zoom', '1')
|
document.documentElement.style.setProperty('--camera-zoom', '1')
|
||||||
|
@ -1869,7 +1856,7 @@ const state = createState({
|
||||||
commands.generate(data, payload.shapes)
|
commands.generate(data, payload.shapes)
|
||||||
},
|
},
|
||||||
updateGeneratedShapes(data, payload, result: { shapes: Shape[] }) {
|
updateGeneratedShapes(data, payload, result: { shapes: Shape[] }) {
|
||||||
setSelectedIds(data, [])
|
tld.setSelectedIds(data, [])
|
||||||
|
|
||||||
history.disable()
|
history.disable()
|
||||||
|
|
||||||
|
@ -1913,7 +1900,7 @@ const state = createState({
|
||||||
},
|
},
|
||||||
|
|
||||||
copyToClipboard(data) {
|
copyToClipboard(data) {
|
||||||
clipboard.copy(getSelectedShapes(data))
|
clipboard.copy(tld.getSelectedShapes(data))
|
||||||
},
|
},
|
||||||
|
|
||||||
copyStateToClipboard(data) {
|
copyStateToClipboard(data) {
|
||||||
|
@ -1986,26 +1973,26 @@ const state = createState({
|
||||||
},
|
},
|
||||||
values: {
|
values: {
|
||||||
selectedIds(data) {
|
selectedIds(data) {
|
||||||
return setToArray(getSelectedIds(data))
|
return setToArray(tld.getSelectedIds(data))
|
||||||
},
|
},
|
||||||
selectedBounds(data) {
|
selectedBounds(data) {
|
||||||
return getSelectionBounds(data)
|
return getSelectionBounds(data)
|
||||||
},
|
},
|
||||||
currentShapes(data) {
|
currentShapes(data) {
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
return Object.values(page.shapes)
|
return Object.values(page.shapes)
|
||||||
.filter((shape) => shape.parentId === page.id)
|
.filter((shape) => shape.parentId === page.id)
|
||||||
.sort((a, b) => a.childIndex - b.childIndex)
|
.sort((a, b) => a.childIndex - b.childIndex)
|
||||||
},
|
},
|
||||||
selectedStyle(data) {
|
selectedStyle(data) {
|
||||||
const selectedIds = setToArray(getSelectedIds(data))
|
const selectedIds = setToArray(tld.getSelectedIds(data))
|
||||||
const { currentStyle } = data
|
const { currentStyle } = data
|
||||||
|
|
||||||
if (selectedIds.length === 0) {
|
if (selectedIds.length === 0) {
|
||||||
return currentStyle
|
return currentStyle
|
||||||
}
|
}
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
const shapeStyles = selectedIds.map((id) => page.shapes[id].style)
|
const shapeStyles = selectedIds.map((id) => page.shapes[id].style)
|
||||||
|
|
||||||
|
@ -2036,12 +2023,12 @@ export default state
|
||||||
export const useSelector = createSelectorHook(state)
|
export const useSelector = createSelectorHook(state)
|
||||||
|
|
||||||
function getParentId(data: Data, id: string) {
|
function getParentId(data: Data, id: string) {
|
||||||
const shape = getPage(data).shapes[id]
|
const shape = tld.getPage(data).shapes[id]
|
||||||
return shape.parentId
|
return shape.parentId
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPointedId(data: Data, id: string) {
|
function getPointedId(data: Data, id: string) {
|
||||||
const shape = getPage(data).shapes[id]
|
const shape = tld.getPage(data).shapes[id]
|
||||||
if (!shape) return id
|
if (!shape) return id
|
||||||
|
|
||||||
return shape.parentId === data.currentParentId ||
|
return shape.parentId === data.currentParentId ||
|
||||||
|
@ -2051,7 +2038,7 @@ function getPointedId(data: Data, id: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDrilledPointedId(data: Data, id: string) {
|
function getDrilledPointedId(data: Data, id: string) {
|
||||||
const shape = getPage(data).shapes[id]
|
const shape = tld.getPage(data).shapes[id]
|
||||||
return shape.parentId === data.currentPageId ||
|
return shape.parentId === data.currentPageId ||
|
||||||
shape.parentId === data.pointedId ||
|
shape.parentId === data.pointedId ||
|
||||||
shape.parentId === data.currentParentId
|
shape.parentId === data.currentParentId
|
||||||
|
@ -2076,18 +2063,18 @@ function getDrilledPointedId(data: Data, id: string) {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
function getSelectionBounds(data: Data) {
|
function getSelectionBounds(data: Data) {
|
||||||
const selectedIds = getSelectedIds(data)
|
const selectedIds = tld.getSelectedIds(data)
|
||||||
|
|
||||||
const page = getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
const shapes = getSelectedShapes(data)
|
const shapes = tld.getSelectedShapes(data)
|
||||||
|
|
||||||
if (selectedIds.size === 0) return null
|
if (selectedIds.size === 0) return null
|
||||||
|
|
||||||
if (selectedIds.size === 1) {
|
if (selectedIds.size === 1) {
|
||||||
if (!shapes[0]) {
|
if (!shapes[0]) {
|
||||||
console.error('Could not find that shape! Clearing selected IDs.')
|
console.warn('Could not find that shape! Clearing selected IDs.')
|
||||||
setSelectedIds(data, [])
|
tld.setSelectedIds(data, [])
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2120,7 +2107,7 @@ function getSelectionBounds(data: Data) {
|
||||||
const uniqueSelectedShapeIds: string[] = Array.from(
|
const uniqueSelectedShapeIds: string[] = Array.from(
|
||||||
new Set(
|
new Set(
|
||||||
Array.from(selectedIds.values()).flatMap((id) =>
|
Array.from(selectedIds.values()).flatMap((id) =>
|
||||||
getDocumentBranch(data, id)
|
tld.getDocumentBranch(data, id)
|
||||||
)
|
)
|
||||||
).values()
|
).values()
|
||||||
)
|
)
|
||||||
|
|
62
types.ts
62
types.ts
|
@ -202,43 +202,39 @@ export interface GroupShape extends BaseShape {
|
||||||
size: number[]
|
size: number[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// type DeepPartial<T> = {
|
|
||||||
// [P in keyof T]?: DeepPartial<T[P]>
|
|
||||||
// }
|
|
||||||
|
|
||||||
export type ShapeProps<T extends Shape> = {
|
export type ShapeProps<T extends Shape> = {
|
||||||
[P in keyof T]?: P extends 'style' ? Partial<T[P]> : T[P]
|
[P in keyof T]?: P extends 'style' ? Partial<T[P]> : T[P]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MutableShape =
|
export interface MutableShapes {
|
||||||
| DotShape
|
[ShapeType.Dot]: DotShape
|
||||||
| EllipseShape
|
[ShapeType.Ellipse]: EllipseShape
|
||||||
| LineShape
|
[ShapeType.Line]: LineShape
|
||||||
| RayShape
|
[ShapeType.Ray]: RayShape
|
||||||
| PolylineShape
|
[ShapeType.Polyline]: PolylineShape
|
||||||
| DrawShape
|
[ShapeType.Draw]: DrawShape
|
||||||
| RectangleShape
|
[ShapeType.Rectangle]: RectangleShape
|
||||||
| ArrowShape
|
[ShapeType.Arrow]: ArrowShape
|
||||||
| TextShape
|
[ShapeType.Text]: TextShape
|
||||||
| GroupShape
|
[ShapeType.Group]: GroupShape
|
||||||
|
|
||||||
export interface Shapes {
|
|
||||||
[ShapeType.Dot]: Readonly<DotShape>
|
|
||||||
[ShapeType.Ellipse]: Readonly<EllipseShape>
|
|
||||||
[ShapeType.Line]: Readonly<LineShape>
|
|
||||||
[ShapeType.Ray]: Readonly<RayShape>
|
|
||||||
[ShapeType.Polyline]: Readonly<PolylineShape>
|
|
||||||
[ShapeType.Draw]: Readonly<DrawShape>
|
|
||||||
[ShapeType.Rectangle]: Readonly<RectangleShape>
|
|
||||||
[ShapeType.Arrow]: Readonly<ArrowShape>
|
|
||||||
[ShapeType.Text]: Readonly<TextShape>
|
|
||||||
[ShapeType.Group]: Readonly<GroupShape>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MutableShape = MutableShapes[keyof MutableShapes]
|
||||||
|
|
||||||
|
export type Shapes = { [K in keyof MutableShapes]: Readonly<MutableShapes[K]> }
|
||||||
|
|
||||||
export type Shape = Readonly<MutableShape>
|
export type Shape = Readonly<MutableShape>
|
||||||
|
|
||||||
export type ShapeByType<T extends ShapeType> = Shapes[T]
|
export type ShapeByType<T extends ShapeType> = Shapes[T]
|
||||||
|
|
||||||
|
export type IsParent<T> = 'children' extends RequiredKeys<T> ? T : never
|
||||||
|
|
||||||
|
export type ParentShape = {
|
||||||
|
[K in keyof MutableShapes]: IsParent<MutableShapes[K]>
|
||||||
|
}[keyof MutableShapes]
|
||||||
|
|
||||||
|
export type ParentTypes = ParentShape['type'] & 'page'
|
||||||
|
|
||||||
export enum Decoration {
|
export enum Decoration {
|
||||||
Arrow = 'Arrow',
|
Arrow = 'Arrow',
|
||||||
}
|
}
|
||||||
|
@ -333,8 +329,6 @@ export interface BoundsSnapshot extends PointSnapshot {
|
||||||
nh: number
|
nh: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Difference<A, B> = A extends B ? never : A
|
|
||||||
|
|
||||||
export type ShapeSpecificProps<T extends Shape> = Pick<
|
export type ShapeSpecificProps<T extends Shape> = Pick<
|
||||||
T,
|
T,
|
||||||
Difference<keyof T, keyof BaseShape>
|
Difference<keyof T, keyof BaseShape>
|
||||||
|
@ -617,3 +611,13 @@ export interface ShapeUtility<K extends Shape> {
|
||||||
// Get whether the shape should render
|
// Get whether the shape should render
|
||||||
shouldRender(this: ShapeUtility<K>, shape: K, previous: K): boolean
|
shouldRender(this: ShapeUtility<K>, shape: K, previous: K): boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------- */
|
||||||
|
/* Utilities */
|
||||||
|
/* -------------------------------------------------- */
|
||||||
|
|
||||||
|
export type Difference<A, B> = A extends B ? never : A
|
||||||
|
|
||||||
|
export type RequiredKeys<T> = {
|
||||||
|
[K in keyof T]-?: Record<string, unknown> extends Pick<T, K> ? never : K
|
||||||
|
}[keyof T]
|
||||||
|
|
490
utils/tld.ts
Normal file
490
utils/tld.ts
Normal file
|
@ -0,0 +1,490 @@
|
||||||
|
import { clamp, deepClone, getCommonBounds, setToArray } from './utils'
|
||||||
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
import vec from './vec'
|
||||||
|
import {
|
||||||
|
Data,
|
||||||
|
Bounds,
|
||||||
|
Shape,
|
||||||
|
GroupShape,
|
||||||
|
ShapeType,
|
||||||
|
CodeFile,
|
||||||
|
Page,
|
||||||
|
PageState,
|
||||||
|
ShapeUtility,
|
||||||
|
ParentShape,
|
||||||
|
} from 'types'
|
||||||
|
import { AssertionError } from 'assert'
|
||||||
|
|
||||||
|
export default class ProjectUtils {
|
||||||
|
static getCameraZoom(zoom: number): number {
|
||||||
|
return clamp(zoom, 0.1, 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
static screenToWorld(point: number[], data: Data): number[] {
|
||||||
|
const camera = this.getCurrentCamera(data)
|
||||||
|
return vec.sub(vec.div(point, camera.zoom), camera.point)
|
||||||
|
}
|
||||||
|
|
||||||
|
static getViewport(data: Data): Bounds {
|
||||||
|
const [minX, minY] = this.screenToWorld([0, 0], data)
|
||||||
|
const [maxX, maxY] = this.screenToWorld(
|
||||||
|
[window.innerWidth, window.innerHeight],
|
||||||
|
data
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
minX,
|
||||||
|
minY,
|
||||||
|
maxX,
|
||||||
|
maxY,
|
||||||
|
height: maxX - minX,
|
||||||
|
width: maxY - minY,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static getCurrentCamera(data: Data): {
|
||||||
|
point: number[]
|
||||||
|
zoom: number
|
||||||
|
} {
|
||||||
|
return data.pageStates[data.currentPageId].camera
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a shape from the project.
|
||||||
|
* @param data
|
||||||
|
* @param shapeId
|
||||||
|
*/
|
||||||
|
static getShape(data: Data, shapeId: string): Shape {
|
||||||
|
return data.document.pages[data.currentPageId].shapes[shapeId]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current page.
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
static getPage(data: Data): Page {
|
||||||
|
return data.document.pages[data.currentPageId]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current page's page state.
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
static getPageState(data: Data): PageState {
|
||||||
|
return data.pageStates[data.currentPageId]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current page's code file.
|
||||||
|
* @param data
|
||||||
|
* @param fileId
|
||||||
|
*/
|
||||||
|
static getCurrentCode(data: Data, fileId: string): CodeFile {
|
||||||
|
return data.document.code[fileId]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current page's shapes as an array.
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
static getShapes(data: Data): Shape[] {
|
||||||
|
const page = this.getPage(data)
|
||||||
|
return Object.values(page.shapes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current selected shapes as an array.
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
static getSelectedShapes(data: Data): Shape[] {
|
||||||
|
const page = this.getPage(data)
|
||||||
|
const ids = setToArray(this.getSelectedIds(data))
|
||||||
|
return ids.map((id) => page.shapes[id])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a shape's parent.
|
||||||
|
* @param data
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
static getParent(data: Data, id: string): Shape | Page {
|
||||||
|
const page = this.getPage(data)
|
||||||
|
const shape = page.shapes[id]
|
||||||
|
|
||||||
|
return page.shapes[shape.parentId] || data.document.pages[shape.parentId]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a shape's children.
|
||||||
|
* @param data
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
static getChildren(data: Data, id: string): Shape[] {
|
||||||
|
const page = this.getPage(data)
|
||||||
|
return Object.values(page.shapes)
|
||||||
|
.filter(({ parentId }) => parentId === id)
|
||||||
|
.sort((a, b) => a.childIndex - b.childIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a shape's siblings.
|
||||||
|
* @param data
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
static getSiblings(data: Data, id: string): Shape[] {
|
||||||
|
const page = this.getPage(data)
|
||||||
|
const shape = page.shapes[id]
|
||||||
|
|
||||||
|
return Object.values(page.shapes)
|
||||||
|
.filter(({ parentId }) => parentId === shape.parentId)
|
||||||
|
.sort((a, b) => a.childIndex - b.childIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next child index above a shape.
|
||||||
|
* @param data
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
static getChildIndexAbove(data: Data, id: string): number {
|
||||||
|
const page = this.getPage(data)
|
||||||
|
|
||||||
|
const shape = page.shapes[id]
|
||||||
|
|
||||||
|
const siblings = Object.values(page.shapes)
|
||||||
|
.filter(({ parentId }) => parentId === shape.parentId)
|
||||||
|
.sort((a, b) => a.childIndex - b.childIndex)
|
||||||
|
|
||||||
|
const index = siblings.indexOf(shape)
|
||||||
|
|
||||||
|
const nextSibling = siblings[index + 1]
|
||||||
|
|
||||||
|
if (!nextSibling) {
|
||||||
|
return shape.childIndex + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
let nextIndex = (shape.childIndex + nextSibling.childIndex) / 2
|
||||||
|
|
||||||
|
if (nextIndex === nextSibling.childIndex) {
|
||||||
|
this.forceIntegerChildIndices(siblings)
|
||||||
|
nextIndex = (shape.childIndex + nextSibling.childIndex) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next child index below a shape.
|
||||||
|
* @param data
|
||||||
|
* @param id
|
||||||
|
* @param pageId
|
||||||
|
*/
|
||||||
|
static getChildIndexBelow(data: Data, id: string): number {
|
||||||
|
const page = this.getPage(data)
|
||||||
|
|
||||||
|
const shape = page.shapes[id]
|
||||||
|
|
||||||
|
const siblings = Object.values(page.shapes)
|
||||||
|
.filter(({ parentId }) => parentId === shape.parentId)
|
||||||
|
.sort((a, b) => a.childIndex - b.childIndex)
|
||||||
|
|
||||||
|
const index = siblings.indexOf(shape)
|
||||||
|
|
||||||
|
const prevSibling = siblings[index - 1]
|
||||||
|
|
||||||
|
if (!prevSibling) {
|
||||||
|
return shape.childIndex / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
let nextIndex = (shape.childIndex + prevSibling.childIndex) / 2
|
||||||
|
|
||||||
|
if (nextIndex === prevSibling.childIndex) {
|
||||||
|
this.forceIntegerChildIndices(siblings)
|
||||||
|
nextIndex = (shape.childIndex + prevSibling.childIndex) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
return (shape.childIndex + prevSibling.childIndex) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert whether a shape can have child shapes.
|
||||||
|
* @param shape
|
||||||
|
*/
|
||||||
|
static assertParentShape(shape: Shape): asserts shape is ParentShape {
|
||||||
|
if (!('children' in shape)) {
|
||||||
|
throw new AssertionError({
|
||||||
|
message: `That shape was not a parent (it was a ${shape.type}).`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the top child index for a shape. This is potentially provisional:
|
||||||
|
* sorting all shapes on the page for each new created shape will become
|
||||||
|
* slower as the page grows. High indices aren't a problem, so consider
|
||||||
|
* tracking the highest index for the page when shapes are created / deleted.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
static getTopChildIndex(data: Data, parent: Shape | Page): number {
|
||||||
|
const page = this.getPage(data)
|
||||||
|
|
||||||
|
// If the parent is a shape, return either 1 (if no other shapes) or the
|
||||||
|
// highest sorted child index + 1.
|
||||||
|
if (parent.type === 'page') {
|
||||||
|
const children = Object.values(parent.shapes)
|
||||||
|
|
||||||
|
if (children.length === 0) return 1
|
||||||
|
|
||||||
|
return (
|
||||||
|
children.sort((a, b) => b.childIndex - a.childIndex)[0].childIndex + 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the shape is a regular shape that can accept children, return either
|
||||||
|
// 1 (if no other children) or the highest sorted child index + 1.
|
||||||
|
this.assertParentShape(parent)
|
||||||
|
|
||||||
|
if (parent.children.length === 0) return 1
|
||||||
|
|
||||||
|
return (
|
||||||
|
parent.children
|
||||||
|
.map((id) => page.shapes[id])
|
||||||
|
.sort((a, b) => b.childIndex - a.childIndex)[0].childIndex + 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Make this recursive, so that it works for parented shapes.
|
||||||
|
* Force all shapes on the page to have integer child indices.
|
||||||
|
* @param shapes
|
||||||
|
*/
|
||||||
|
static forceIntegerChildIndices(shapes: Shape[]): void {
|
||||||
|
for (let i = 0; i < shapes.length; i++) {
|
||||||
|
const shape = shapes[i]
|
||||||
|
getShapeUtils(shape).setProperty(shape, 'childIndex', i + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the zoom CSS variable.
|
||||||
|
* @param zoom ;
|
||||||
|
*/
|
||||||
|
static setZoomCSS(zoom: number): void {
|
||||||
|
document.documentElement.style.setProperty('--camera-zoom', zoom.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------- Groups --------------------- */
|
||||||
|
|
||||||
|
static getParentOffset(
|
||||||
|
data: Data,
|
||||||
|
shapeId: string,
|
||||||
|
offset = [0, 0]
|
||||||
|
): number[] {
|
||||||
|
const shape = this.getShape(data, shapeId)
|
||||||
|
return shape.parentId === data.currentPageId
|
||||||
|
? offset
|
||||||
|
: this.getParentOffset(data, shape.parentId, vec.add(offset, shape.point))
|
||||||
|
}
|
||||||
|
|
||||||
|
static getParentRotation(data: Data, shapeId: string, rotation = 0): number {
|
||||||
|
const shape = this.getShape(data, shapeId)
|
||||||
|
return shape.parentId === data.currentPageId
|
||||||
|
? rotation + shape.rotation
|
||||||
|
: this.getParentRotation(data, shape.parentId, rotation + shape.rotation)
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDocumentBranch(data: Data, id: string): string[] {
|
||||||
|
const shape = this.getPage(data).shapes[id]
|
||||||
|
|
||||||
|
if (shape.type !== ShapeType.Group) return [id]
|
||||||
|
|
||||||
|
return [
|
||||||
|
id,
|
||||||
|
...shape.children.flatMap((childId) =>
|
||||||
|
this.getDocumentBranch(data, childId)
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
static getSelectedIds(data: Data): Set<string> {
|
||||||
|
return data.pageStates[data.currentPageId].selectedIds
|
||||||
|
}
|
||||||
|
|
||||||
|
static setSelectedIds(data: Data, ids: string[]): Set<string> {
|
||||||
|
data.pageStates[data.currentPageId].selectedIds = new Set(ids)
|
||||||
|
return data.pageStates[data.currentPageId].selectedIds
|
||||||
|
}
|
||||||
|
|
||||||
|
static getTopParentId(data: Data, id: string): string {
|
||||||
|
const shape = this.getPage(data).shapes[id]
|
||||||
|
return shape.parentId === data.currentPageId ||
|
||||||
|
shape.parentId === data.currentParentId
|
||||||
|
? id
|
||||||
|
: this.getTopParentId(data, shape.parentId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------- Shapes Related ----------------- */
|
||||||
|
|
||||||
|
static getSelectedShapeSnapshot(data: Data): Shape[]
|
||||||
|
static getSelectedShapeSnapshot<K>(
|
||||||
|
data: Data,
|
||||||
|
fn: <T extends Shape>(shape: T) => K
|
||||||
|
): ({ id: string } & K)[]
|
||||||
|
static getSelectedShapeSnapshot<
|
||||||
|
K,
|
||||||
|
F extends <T extends Shape>(shape: T) => K
|
||||||
|
>(data: Data, fn?: F): (Shape | K)[] {
|
||||||
|
const copies = this.getSelectedShapes(data)
|
||||||
|
.filter((shape) => !shape.isLocked)
|
||||||
|
.map((shape) => deepClone(shape))
|
||||||
|
|
||||||
|
if (fn !== undefined) {
|
||||||
|
return copies.map((shape) => ({ id: shape.id, ...fn(shape) }))
|
||||||
|
}
|
||||||
|
|
||||||
|
return copies
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make an arbitrary change to shape.
|
||||||
|
* @param data
|
||||||
|
* @param ids
|
||||||
|
* @param fn
|
||||||
|
*/
|
||||||
|
static mutateShape<T extends Shape>(
|
||||||
|
data: Data,
|
||||||
|
id: string,
|
||||||
|
fn: (shapeUtils: ShapeUtility<T>, shape: T) => void,
|
||||||
|
updateParents = true
|
||||||
|
): T {
|
||||||
|
const page = this.getPage(data)
|
||||||
|
|
||||||
|
const shape = page.shapes[id] as T
|
||||||
|
fn(getShapeUtils(shape) as ShapeUtility<T>, shape)
|
||||||
|
|
||||||
|
if (updateParents) this.updateParents(data, [id])
|
||||||
|
|
||||||
|
return shape
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make an arbitrary change to a set of shapes.
|
||||||
|
* @param data
|
||||||
|
* @param ids
|
||||||
|
* @param fn
|
||||||
|
*/
|
||||||
|
static mutateShapes<T extends Shape>(
|
||||||
|
data: Data,
|
||||||
|
ids: string[],
|
||||||
|
fn: (shape: T, shapeUtils: ShapeUtility<T>, index: number) => T | void,
|
||||||
|
updateParents = true
|
||||||
|
): T[] {
|
||||||
|
const page = this.getPage(data)
|
||||||
|
|
||||||
|
const mutatedShapes = ids.map((id, i) => {
|
||||||
|
const shape = page.shapes[id] as T
|
||||||
|
|
||||||
|
// Define the new shape as either the (maybe new) shape returned by the
|
||||||
|
// function or the mutated shape.
|
||||||
|
page.shapes[id] =
|
||||||
|
fn(shape, getShapeUtils(shape) as ShapeUtility<T>, i) || shape
|
||||||
|
|
||||||
|
return page.shapes[id] as T
|
||||||
|
})
|
||||||
|
|
||||||
|
if (updateParents) this.updateParents(data, ids)
|
||||||
|
|
||||||
|
return mutatedShapes
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert shapes into the current page.
|
||||||
|
* @param data
|
||||||
|
* @param shapes
|
||||||
|
*/
|
||||||
|
static insertShapes(data: Data, shapes: Shape[]): void {
|
||||||
|
const page = this.getPage(data)
|
||||||
|
|
||||||
|
shapes.forEach((shape) => {
|
||||||
|
page.shapes[shape.id] = shape
|
||||||
|
|
||||||
|
// Does the shape have a parent?
|
||||||
|
if (shape.parentId !== data.currentPageId) {
|
||||||
|
// The parent shape
|
||||||
|
const parent = page.shapes[shape.parentId]
|
||||||
|
|
||||||
|
// If the parent shape doesn't exist, assign the shape as a child
|
||||||
|
// of the page instead.
|
||||||
|
if (parent === undefined) {
|
||||||
|
getShapeUtils(shape).setProperty(
|
||||||
|
shape,
|
||||||
|
'childIndex',
|
||||||
|
this.getTopChildIndex(data, parent)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Add the shape's id to the parent's children, then sort the
|
||||||
|
// new array just to be sure.
|
||||||
|
getShapeUtils(parent).setProperty(
|
||||||
|
parent,
|
||||||
|
'children',
|
||||||
|
[...parent.children, shape.id]
|
||||||
|
.map((id) => page.shapes[id])
|
||||||
|
.sort((a, b) => a.childIndex - b.childIndex)
|
||||||
|
.map((shape) => shape.id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Update any new parents
|
||||||
|
this.updateParents(
|
||||||
|
data,
|
||||||
|
shapes.map((shape) => shape.id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
static getRotatedBounds(shape: Shape): Bounds {
|
||||||
|
return getShapeUtils(shape).getRotatedBounds(shape)
|
||||||
|
}
|
||||||
|
|
||||||
|
static getShapeBounds(shape: Shape): Bounds {
|
||||||
|
return getShapeUtils(shape).getBounds(shape)
|
||||||
|
}
|
||||||
|
|
||||||
|
static getSelectedBounds(data: Data): Bounds {
|
||||||
|
return getCommonBounds(
|
||||||
|
...this.getSelectedShapes(data).map((shape) =>
|
||||||
|
getShapeUtils(shape).getBounds(shape)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively update shape parents.
|
||||||
|
* @param data
|
||||||
|
* @param changedShapeIds
|
||||||
|
*/
|
||||||
|
static updateParents(data: Data, changedShapeIds: string[]): void {
|
||||||
|
if (changedShapeIds.length === 0) return
|
||||||
|
|
||||||
|
const { shapes } = this.getPage(data)
|
||||||
|
|
||||||
|
const parentToUpdateIds = Array.from(
|
||||||
|
new Set(changedShapeIds.map((id) => shapes[id].parentId).values())
|
||||||
|
).filter((id) => id !== data.currentPageId)
|
||||||
|
|
||||||
|
for (const parentId of parentToUpdateIds) {
|
||||||
|
const parent = shapes[parentId] as GroupShape
|
||||||
|
|
||||||
|
getShapeUtils(parent).onChildrenChange(
|
||||||
|
parent,
|
||||||
|
parent.children.map((id) => shapes[id])
|
||||||
|
)
|
||||||
|
|
||||||
|
shapes[parentId] = { ...parent }
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateParents(data, parentToUpdateIds)
|
||||||
|
}
|
||||||
|
}
|
316
utils/utils.ts
316
utils/utils.ts
|
@ -1,22 +1,9 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {
|
import { Bounds, Edge, Corner, BezierCurveSegment } from 'types'
|
||||||
Data,
|
|
||||||
Bounds,
|
|
||||||
Edge,
|
|
||||||
Corner,
|
|
||||||
Shape,
|
|
||||||
GroupShape,
|
|
||||||
ShapeType,
|
|
||||||
CodeFile,
|
|
||||||
Page,
|
|
||||||
PageState,
|
|
||||||
BezierCurveSegment,
|
|
||||||
} from 'types'
|
|
||||||
import { v4 as uuid } from 'uuid'
|
import { v4 as uuid } from 'uuid'
|
||||||
import vec from './vec'
|
import vec from './vec'
|
||||||
import _isMobile from 'ismobilejs'
|
import _isMobile from 'ismobilejs'
|
||||||
import { intersectPolygonBounds } from './intersections'
|
import { intersectPolygonBounds } from './intersections'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
|
||||||
|
|
||||||
/* ----------- Numbers and Data Structures ---------- */
|
/* ----------- Numbers and Data Structures ---------- */
|
||||||
|
|
||||||
|
@ -1789,304 +1776,3 @@ export function commandKey(): string {
|
||||||
|
|
||||||
// return delta
|
// return delta
|
||||||
// }
|
// }
|
||||||
|
|
||||||
/* -------------------------------------------------- */
|
|
||||||
/* State Utils */
|
|
||||||
/* -------------------------------------------------- */
|
|
||||||
|
|
||||||
export function getCameraZoom(zoom: number): number {
|
|
||||||
return clamp(zoom, 0.1, 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function screenToWorld(point: number[], data: Data): number[] {
|
|
||||||
const camera = getCurrentCamera(data)
|
|
||||||
return vec.sub(vec.div(point, camera.zoom), camera.point)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getViewport(data: Data): Bounds {
|
|
||||||
const [minX, minY] = screenToWorld([0, 0], data)
|
|
||||||
const [maxX, maxY] = screenToWorld(
|
|
||||||
[window.innerWidth, window.innerHeight],
|
|
||||||
data
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
minX,
|
|
||||||
minY,
|
|
||||||
maxX,
|
|
||||||
maxY,
|
|
||||||
height: maxX - minX,
|
|
||||||
width: maxY - minY,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCurrentCamera(data: Data): {
|
|
||||||
point: number[]
|
|
||||||
zoom: number
|
|
||||||
} {
|
|
||||||
return data.pageStates[data.currentPageId].camera
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a shape from the project.
|
|
||||||
* @param data
|
|
||||||
* @param shapeId
|
|
||||||
*/
|
|
||||||
export function getShape(data: Data, shapeId: string): Shape {
|
|
||||||
return data.document.pages[data.currentPageId].shapes[shapeId]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current page.
|
|
||||||
* @param data
|
|
||||||
*/
|
|
||||||
export function getPage(data: Data): Page {
|
|
||||||
return data.document.pages[data.currentPageId]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current page's page state.
|
|
||||||
* @param data
|
|
||||||
*/
|
|
||||||
export function getPageState(data: Data): PageState {
|
|
||||||
return data.pageStates[data.currentPageId]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current page's code file.
|
|
||||||
* @param data
|
|
||||||
* @param fileId
|
|
||||||
*/
|
|
||||||
export function getCurrentCode(data: Data, fileId: string): CodeFile {
|
|
||||||
return data.document.code[fileId]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current page's shapes as an array.
|
|
||||||
* @param data
|
|
||||||
*/
|
|
||||||
export function getShapes(data: Data): Shape[] {
|
|
||||||
const page = getPage(data)
|
|
||||||
return Object.values(page.shapes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current selected shapes as an array.
|
|
||||||
* @param data
|
|
||||||
*/
|
|
||||||
export function getSelectedShapes(data: Data): Shape[] {
|
|
||||||
const page = getPage(data)
|
|
||||||
const ids = setToArray(getSelectedIds(data))
|
|
||||||
return ids.map((id) => page.shapes[id])
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a shape's parent.
|
|
||||||
* @param data
|
|
||||||
* @param id
|
|
||||||
*/
|
|
||||||
export function getParent(data: Data, id: string): Shape | Page {
|
|
||||||
const page = getPage(data)
|
|
||||||
const shape = page.shapes[id]
|
|
||||||
|
|
||||||
return page.shapes[shape.parentId] || data.document.pages[shape.parentId]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a shape's children.
|
|
||||||
* @param data
|
|
||||||
* @param id
|
|
||||||
*/
|
|
||||||
export function getChildren(data: Data, id: string): Shape[] {
|
|
||||||
const page = getPage(data)
|
|
||||||
return Object.values(page.shapes)
|
|
||||||
.filter(({ parentId }) => parentId === id)
|
|
||||||
.sort((a, b) => a.childIndex - b.childIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a shape's siblings.
|
|
||||||
* @param data
|
|
||||||
* @param id
|
|
||||||
*/
|
|
||||||
export function getSiblings(data: Data, id: string): Shape[] {
|
|
||||||
const page = getPage(data)
|
|
||||||
const shape = page.shapes[id]
|
|
||||||
|
|
||||||
return Object.values(page.shapes)
|
|
||||||
.filter(({ parentId }) => parentId === shape.parentId)
|
|
||||||
.sort((a, b) => a.childIndex - b.childIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the next child index above a shape.
|
|
||||||
* @param data
|
|
||||||
* @param id
|
|
||||||
*/
|
|
||||||
export function getChildIndexAbove(data: Data, id: string): number {
|
|
||||||
const page = getPage(data)
|
|
||||||
|
|
||||||
const shape = page.shapes[id]
|
|
||||||
|
|
||||||
const siblings = Object.values(page.shapes)
|
|
||||||
.filter(({ parentId }) => parentId === shape.parentId)
|
|
||||||
.sort((a, b) => a.childIndex - b.childIndex)
|
|
||||||
|
|
||||||
const index = siblings.indexOf(shape)
|
|
||||||
|
|
||||||
const nextSibling = siblings[index + 1]
|
|
||||||
|
|
||||||
if (!nextSibling) {
|
|
||||||
return shape.childIndex + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
let nextIndex = (shape.childIndex + nextSibling.childIndex) / 2
|
|
||||||
|
|
||||||
if (nextIndex === nextSibling.childIndex) {
|
|
||||||
forceIntegerChildIndices(siblings)
|
|
||||||
nextIndex = (shape.childIndex + nextSibling.childIndex) / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
return nextIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the next child index below a shape.
|
|
||||||
* @param data
|
|
||||||
* @param id
|
|
||||||
* @param pageId
|
|
||||||
*/
|
|
||||||
export function getChildIndexBelow(data: Data, id: string): number {
|
|
||||||
const page = getPage(data)
|
|
||||||
|
|
||||||
const shape = page.shapes[id]
|
|
||||||
|
|
||||||
const siblings = Object.values(page.shapes)
|
|
||||||
.filter(({ parentId }) => parentId === shape.parentId)
|
|
||||||
.sort((a, b) => a.childIndex - b.childIndex)
|
|
||||||
|
|
||||||
const index = siblings.indexOf(shape)
|
|
||||||
|
|
||||||
const prevSibling = siblings[index - 1]
|
|
||||||
|
|
||||||
if (!prevSibling) {
|
|
||||||
return shape.childIndex / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
let nextIndex = (shape.childIndex + prevSibling.childIndex) / 2
|
|
||||||
|
|
||||||
if (nextIndex === prevSibling.childIndex) {
|
|
||||||
forceIntegerChildIndices(siblings)
|
|
||||||
nextIndex = (shape.childIndex + prevSibling.childIndex) / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
return (shape.childIndex + prevSibling.childIndex) / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
export function forceIntegerChildIndices(shapes: Shape[]): void {
|
|
||||||
for (let i = 0; i < shapes.length; i++) {
|
|
||||||
const shape = shapes[i]
|
|
||||||
getShapeUtils(shape).setProperty(shape, 'childIndex', i + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the zoom CSS variable.
|
|
||||||
* @param zoom ;
|
|
||||||
*/
|
|
||||||
export function setZoomCSS(zoom: number): void {
|
|
||||||
document.documentElement.style.setProperty('--camera-zoom', zoom.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------- Groups --------------------- */
|
|
||||||
|
|
||||||
export function getParentOffset(
|
|
||||||
data: Data,
|
|
||||||
shapeId: string,
|
|
||||||
offset = [0, 0]
|
|
||||||
): number[] {
|
|
||||||
const shape = getShape(data, shapeId)
|
|
||||||
return shape.parentId === data.currentPageId
|
|
||||||
? offset
|
|
||||||
: getParentOffset(data, shape.parentId, vec.add(offset, shape.point))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getParentRotation(
|
|
||||||
data: Data,
|
|
||||||
shapeId: string,
|
|
||||||
rotation = 0
|
|
||||||
): number {
|
|
||||||
const shape = getShape(data, shapeId)
|
|
||||||
return shape.parentId === data.currentPageId
|
|
||||||
? rotation + shape.rotation
|
|
||||||
: getParentRotation(data, shape.parentId, rotation + shape.rotation)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getDocumentBranch(data: Data, id: string): string[] {
|
|
||||||
const shape = getPage(data).shapes[id]
|
|
||||||
|
|
||||||
if (shape.type !== ShapeType.Group) return [id]
|
|
||||||
|
|
||||||
return [
|
|
||||||
id,
|
|
||||||
...shape.children.flatMap((childId) => getDocumentBranch(data, childId)),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getSelectedIds(data: Data): Set<string> {
|
|
||||||
return data.pageStates[data.currentPageId].selectedIds
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setSelectedIds(data: Data, ids: string[]): Set<string> {
|
|
||||||
data.pageStates[data.currentPageId].selectedIds = new Set(ids)
|
|
||||||
return data.pageStates[data.currentPageId].selectedIds
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getTopParentId(data: Data, id: string): string {
|
|
||||||
const shape = getPage(data).shapes[id]
|
|
||||||
return shape.parentId === data.currentPageId ||
|
|
||||||
shape.parentId === data.currentParentId
|
|
||||||
? id
|
|
||||||
: getTopParentId(data, shape.parentId)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------- Shapes Related ----------------- */
|
|
||||||
|
|
||||||
export function getRotatedBounds(shape: Shape): Bounds {
|
|
||||||
return getShapeUtils(shape).getRotatedBounds(shape)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getShapeBounds(shape: Shape): Bounds {
|
|
||||||
return getShapeUtils(shape).getBounds(shape)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getSelectedBounds(data: Data): Bounds {
|
|
||||||
return getCommonBounds(
|
|
||||||
...getSelectedShapes(data).map((shape) =>
|
|
||||||
getShapeUtils(shape).getBounds(shape)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateParents(data: Data, changedShapeIds: string[]): void {
|
|
||||||
if (changedShapeIds.length === 0) return
|
|
||||||
|
|
||||||
const { shapes } = getPage(data)
|
|
||||||
|
|
||||||
const parentToUpdateIds = Array.from(
|
|
||||||
new Set(changedShapeIds.map((id) => shapes[id].parentId).values())
|
|
||||||
).filter((id) => id !== data.currentPageId)
|
|
||||||
|
|
||||||
for (const parentId of parentToUpdateIds) {
|
|
||||||
const parent = shapes[parentId] as GroupShape
|
|
||||||
|
|
||||||
getShapeUtils(parent).onChildrenChange(
|
|
||||||
parent,
|
|
||||||
parent.children.map((id) => shapes[id])
|
|
||||||
)
|
|
||||||
|
|
||||||
shapes[parentId] = { ...parent }
|
|
||||||
}
|
|
||||||
|
|
||||||
updateParents(data, parentToUpdateIds)
|
|
||||||
}
|
|
||||||
|
|
109
yarn.lock
109
yarn.lock
|
@ -1924,6 +1924,14 @@
|
||||||
resolved "https://registry.yarnpkg.com/@stitches/react/-/react-0.2.2.tgz#8a2f8256835510c265ab20820f54a5bb0376bac6"
|
resolved "https://registry.yarnpkg.com/@stitches/react/-/react-0.2.2.tgz#8a2f8256835510c265ab20820f54a5bb0376bac6"
|
||||||
integrity sha512-/qRSX+ANPJg/0eLNr5bEywjkdvIfLbsaG2d8p83wPlI0MA7Yi9FzaRLk2H6DMAdJHuyu6ThY4HfHQIL35buY9g==
|
integrity sha512-/qRSX+ANPJg/0eLNr5bEywjkdvIfLbsaG2d8p83wPlI0MA7Yi9FzaRLk2H6DMAdJHuyu6ThY4HfHQIL35buY9g==
|
||||||
|
|
||||||
|
"@supabase/realtime-js@^1.0.9":
|
||||||
|
version "1.0.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/@supabase/realtime-js/-/realtime-js-1.0.9.tgz#a4be4f893be78db4487ad2c9de155a4a07532eae"
|
||||||
|
integrity sha512-hGClyW7hHXW0PC6reJgaKFL0c3ubC+AVt7U/MxD0VJNjVXIw4PLj7DxgMpCIpNXksHJsLOBL8ht+BMhPb6rE8Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/websocket" "^1.0.1"
|
||||||
|
websocket "^1.0.34"
|
||||||
|
|
||||||
"@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"
|
||||||
resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-1.4.2.tgz#e6786b6af5799f82f7ab3a82e53f6182d2b91a58"
|
resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-1.4.2.tgz#e6786b6af5799f82f7ab3a82e53f6182d2b91a58"
|
||||||
|
@ -2145,6 +2153,13 @@
|
||||||
anymatch "^3.0.0"
|
anymatch "^3.0.0"
|
||||||
source-map "^0.6.0"
|
source-map "^0.6.0"
|
||||||
|
|
||||||
|
"@types/websocket@^1.0.1":
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.2.tgz#d2855c6a312b7da73ed16ba6781815bf30c6187a"
|
||||||
|
integrity sha512-B5m9aq7cbbD/5/jThEr33nUY8WEfVi6A2YKCTOvw5Ldy7mtsOkqRvGjnzy6g7iMMDsgu7xREuCzqATLDLQVKcQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/yargs-parser@*":
|
"@types/yargs-parser@*":
|
||||||
version "20.2.0"
|
version "20.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9"
|
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9"
|
||||||
|
@ -2838,6 +2853,13 @@ buffer@^6.0.3:
|
||||||
base64-js "^1.3.1"
|
base64-js "^1.3.1"
|
||||||
ieee754 "^1.2.1"
|
ieee754 "^1.2.1"
|
||||||
|
|
||||||
|
bufferutil@^4.0.1:
|
||||||
|
version "4.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.3.tgz#66724b756bed23cd7c28c4d306d7994f9943cc6b"
|
||||||
|
integrity sha512-yEYTwGndELGvfXsImMBLop58eaGW+YdONi1fNjTINSY98tmMmFijBG6WXgdkfuLNt4imzQNtIE+eBp1PVpMCSw==
|
||||||
|
dependencies:
|
||||||
|
node-gyp-build "^4.2.0"
|
||||||
|
|
||||||
builtin-modules@^3.1.0:
|
builtin-modules@^3.1.0:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887"
|
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887"
|
||||||
|
@ -3286,6 +3308,14 @@ csstype@^3.0.2, csstype@^3.0.4:
|
||||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340"
|
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340"
|
||||||
integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==
|
integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==
|
||||||
|
|
||||||
|
d@1, d@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
|
||||||
|
integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==
|
||||||
|
dependencies:
|
||||||
|
es5-ext "^0.10.50"
|
||||||
|
type "^1.0.1"
|
||||||
|
|
||||||
damerau-levenshtein@^1.0.6:
|
damerau-levenshtein@^1.0.6:
|
||||||
version "1.0.7"
|
version "1.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz#64368003512a1a6992593741a09a9d31a836f55d"
|
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz#64368003512a1a6992593741a09a9d31a836f55d"
|
||||||
|
@ -3305,7 +3335,7 @@ data-urls@^2.0.0:
|
||||||
whatwg-mimetype "^2.3.0"
|
whatwg-mimetype "^2.3.0"
|
||||||
whatwg-url "^8.0.0"
|
whatwg-url "^8.0.0"
|
||||||
|
|
||||||
debug@2, debug@^2.6.9:
|
debug@2, debug@^2.2.0, debug@^2.6.9:
|
||||||
version "2.6.9"
|
version "2.6.9"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||||
|
@ -3575,11 +3605,37 @@ es-to-primitive@^1.2.1:
|
||||||
is-date-object "^1.0.1"
|
is-date-object "^1.0.1"
|
||||||
is-symbol "^1.0.2"
|
is-symbol "^1.0.2"
|
||||||
|
|
||||||
|
es5-ext@^0.10.35, es5-ext@^0.10.50:
|
||||||
|
version "0.10.53"
|
||||||
|
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1"
|
||||||
|
integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==
|
||||||
|
dependencies:
|
||||||
|
es6-iterator "~2.0.3"
|
||||||
|
es6-symbol "~3.1.3"
|
||||||
|
next-tick "~1.0.0"
|
||||||
|
|
||||||
|
es6-iterator@~2.0.3:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
|
||||||
|
integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c=
|
||||||
|
dependencies:
|
||||||
|
d "1"
|
||||||
|
es5-ext "^0.10.35"
|
||||||
|
es6-symbol "^3.1.1"
|
||||||
|
|
||||||
es6-object-assign@^1.1.0:
|
es6-object-assign@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
|
resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
|
||||||
integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=
|
integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=
|
||||||
|
|
||||||
|
es6-symbol@^3.1.1, es6-symbol@~3.1.3:
|
||||||
|
version "3.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18"
|
||||||
|
integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==
|
||||||
|
dependencies:
|
||||||
|
d "^1.0.1"
|
||||||
|
ext "^1.1.2"
|
||||||
|
|
||||||
escalade@^3.1.1:
|
escalade@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
||||||
|
@ -3903,6 +3959,13 @@ expect@^27.0.2:
|
||||||
jest-message-util "^27.0.2"
|
jest-message-util "^27.0.2"
|
||||||
jest-regex-util "^27.0.1"
|
jest-regex-util "^27.0.1"
|
||||||
|
|
||||||
|
ext@^1.1.2:
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244"
|
||||||
|
integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==
|
||||||
|
dependencies:
|
||||||
|
type "^2.0.0"
|
||||||
|
|
||||||
extend@^3.0.0:
|
extend@^3.0.0:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
|
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
|
||||||
|
@ -5932,6 +5995,11 @@ next-pwa@^5.2.21:
|
||||||
workbox-webpack-plugin "^6.1.5"
|
workbox-webpack-plugin "^6.1.5"
|
||||||
workbox-window "^6.1.5"
|
workbox-window "^6.1.5"
|
||||||
|
|
||||||
|
next-tick@~1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
|
||||||
|
integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
|
||||||
|
|
||||||
next@^11.0.1:
|
next@^11.0.1:
|
||||||
version "11.0.1"
|
version "11.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/next/-/next-11.0.1.tgz#b8e3914d153aaf7143cb98c09bcd3c8230eeb17a"
|
resolved "https://registry.yarnpkg.com/next/-/next-11.0.1.tgz#b8e3914d153aaf7143cb98c09bcd3c8230eeb17a"
|
||||||
|
@ -5993,6 +6061,11 @@ node-fetch@2.6.1, node-fetch@^2.6.0, node-fetch@^2.6.1:
|
||||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||||
|
|
||||||
|
node-gyp-build@^4.2.0:
|
||||||
|
version "4.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739"
|
||||||
|
integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==
|
||||||
|
|
||||||
node-html-parser@1.4.9:
|
node-html-parser@1.4.9:
|
||||||
version "1.4.9"
|
version "1.4.9"
|
||||||
resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-1.4.9.tgz#3c8f6cac46479fae5800725edb532e9ae8fd816c"
|
resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-1.4.9.tgz#3c8f6cac46479fae5800725edb532e9ae8fd816c"
|
||||||
|
@ -7899,6 +7972,16 @@ type-fest@^0.7.1:
|
||||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48"
|
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48"
|
||||||
integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==
|
integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==
|
||||||
|
|
||||||
|
type@^1.0.1:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0"
|
||||||
|
integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==
|
||||||
|
|
||||||
|
type@^2.0.0:
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d"
|
||||||
|
integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==
|
||||||
|
|
||||||
typedarray-to-buffer@^3.1.5:
|
typedarray-to-buffer@^3.1.5:
|
||||||
version "3.1.5"
|
version "3.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
|
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
|
||||||
|
@ -8085,6 +8168,13 @@ use-subscription@1.5.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
|
utf-8-validate@^5.0.2:
|
||||||
|
version "5.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.5.tgz#dd32c2e82c72002dc9f02eb67ba6761f43456ca1"
|
||||||
|
integrity sha512-+pnxRYsS/axEpkrrEpzYfNZGXp0IjC/9RIxwM5gntY4Koi8SHmUGSfxfWqxZdRxrtaoVstuOzUp/rbs3JSPELQ==
|
||||||
|
dependencies:
|
||||||
|
node-gyp-build "^4.2.0"
|
||||||
|
|
||||||
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
|
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
|
@ -8230,6 +8320,18 @@ webpack-sources@^1.4.3:
|
||||||
source-list-map "^2.0.0"
|
source-list-map "^2.0.0"
|
||||||
source-map "~0.6.1"
|
source-map "~0.6.1"
|
||||||
|
|
||||||
|
websocket@^1.0.34:
|
||||||
|
version "1.0.34"
|
||||||
|
resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111"
|
||||||
|
integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==
|
||||||
|
dependencies:
|
||||||
|
bufferutil "^4.0.1"
|
||||||
|
debug "^2.2.0"
|
||||||
|
es5-ext "^0.10.50"
|
||||||
|
typedarray-to-buffer "^3.1.5"
|
||||||
|
utf-8-validate "^5.0.2"
|
||||||
|
yaeti "^0.0.6"
|
||||||
|
|
||||||
whatwg-encoding@^1.0.5:
|
whatwg-encoding@^1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
|
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
|
||||||
|
@ -8546,6 +8648,11 @@ y18n@^5.0.5:
|
||||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
||||||
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
|
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
|
||||||
|
|
||||||
|
yaeti@^0.0.6:
|
||||||
|
version "0.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577"
|
||||||
|
integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=
|
||||||
|
|
||||||
yallist@^4.0.0:
|
yallist@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
|
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
|
||||||
|
|
Loading…
Reference in a new issue