Adds boolean props for lock, hide, aspect lock
This commit is contained in:
parent
b369aef7fc
commit
3329c16e57
29 changed files with 601 additions and 366 deletions
|
@ -1,15 +1,15 @@
|
||||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||||
import styled from "styles"
|
import styled from 'styles'
|
||||||
import { useStateDesigner } from "@state-designer/react"
|
import { useStateDesigner } from '@state-designer/react'
|
||||||
import React, { useEffect, useRef } from "react"
|
import React, { useEffect, useRef } from 'react'
|
||||||
import { motion } from "framer-motion"
|
import { motion } from 'framer-motion'
|
||||||
import state, { useSelector } from "state"
|
import state, { useSelector } from 'state'
|
||||||
import { CodeFile } from "types"
|
import { CodeFile } from 'types'
|
||||||
import CodeDocs from "./code-docs"
|
import CodeDocs from './code-docs'
|
||||||
import CodeEditor from "./code-editor"
|
import CodeEditor from './code-editor'
|
||||||
import { generateFromCode } from "lib/code/generate"
|
import { generateFromCode } from 'lib/code/generate'
|
||||||
import * as Panel from "../panel"
|
import * as Panel from '../panel'
|
||||||
import { IconButton } from "../shared"
|
import { IconButton } from '../shared'
|
||||||
import {
|
import {
|
||||||
X,
|
X,
|
||||||
Code,
|
Code,
|
||||||
|
@ -17,10 +17,10 @@ import {
|
||||||
PlayCircle,
|
PlayCircle,
|
||||||
ChevronUp,
|
ChevronUp,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
} from "react-feather"
|
} from 'react-feather'
|
||||||
|
|
||||||
const getErrorLineAndColumn = (e: any) => {
|
const getErrorLineAndColumn = (e: any) => {
|
||||||
if ("line" in e) {
|
if ('line' in e) {
|
||||||
return { line: Number(e.line), column: e.column }
|
return { line: Number(e.line), column: e.column }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,23 +46,23 @@ export default function CodePanel() {
|
||||||
error: null as { message: string; line: number; column: number } | null,
|
error: null as { message: string; line: number; column: number } | null,
|
||||||
},
|
},
|
||||||
on: {
|
on: {
|
||||||
MOUNTED: "setCode",
|
MOUNTED: 'setCode',
|
||||||
CHANGED_FILE: "loadFile",
|
CHANGED_FILE: 'loadFile',
|
||||||
},
|
},
|
||||||
initial: "editingCode",
|
initial: 'editingCode',
|
||||||
states: {
|
states: {
|
||||||
editingCode: {
|
editingCode: {
|
||||||
on: {
|
on: {
|
||||||
RAN_CODE: ["saveCode", "runCode"],
|
RAN_CODE: ['saveCode', 'runCode'],
|
||||||
SAVED_CODE: ["saveCode", "runCode"],
|
SAVED_CODE: ['saveCode', 'runCode'],
|
||||||
CHANGED_CODE: { secretlyDo: "setCode" },
|
CHANGED_CODE: { secretlyDo: 'setCode' },
|
||||||
CLEARED_ERROR: { if: "hasError", do: "clearError" },
|
CLEARED_ERROR: { if: 'hasError', do: 'clearError' },
|
||||||
TOGGLED_DOCS: { to: "viewingDocs" },
|
TOGGLED_DOCS: { to: 'viewingDocs' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
viewingDocs: {
|
viewingDocs: {
|
||||||
on: {
|
on: {
|
||||||
TOGGLED_DOCS: { to: "editingCode" },
|
TOGGLED_DOCS: { to: 'editingCode' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -83,7 +83,7 @@ export default function CodePanel() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { shapes, controls } = generateFromCode(data.code)
|
const { shapes, controls } = generateFromCode(data.code)
|
||||||
state.send("GENERATED_FROM_CODE", { shapes, controls })
|
state.send('GENERATED_FROM_CODE', { shapes, controls })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
error = { message: e.message, ...getErrorLineAndColumn(e) }
|
error = { message: e.message, ...getErrorLineAndColumn(e) }
|
||||||
|
@ -93,7 +93,7 @@ export default function CodePanel() {
|
||||||
},
|
},
|
||||||
saveCode(data) {
|
saveCode(data) {
|
||||||
const { code } = data
|
const { code } = data
|
||||||
state.send("SAVED_CODE", { code })
|
state.send('SAVED_CODE', { code })
|
||||||
},
|
},
|
||||||
clearError(data) {
|
clearError(data) {
|
||||||
data.error = null
|
data.error = null
|
||||||
|
@ -102,13 +102,13 @@ export default function CodePanel() {
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
local.send("CHANGED_FILE", { file })
|
local.send('CHANGED_FILE', { file })
|
||||||
}, [file])
|
}, [file])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
local.send("MOUNTED", { code: state.data.document.code[fileId].code })
|
local.send('MOUNTED', { code: state.data.document.code[fileId].code })
|
||||||
return () => {
|
return () => {
|
||||||
state.send("CHANGED_CODE", { fileId, code: local.data.code })
|
state.send('CHANGED_CODE', { fileId, code: local.data.code })
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
@ -118,32 +118,32 @@ export default function CodePanel() {
|
||||||
<Panel.Root data-bp-desktop ref={rContainer} isOpen={isOpen}>
|
<Panel.Root data-bp-desktop ref={rContainer} isOpen={isOpen}>
|
||||||
{isOpen ? (
|
{isOpen ? (
|
||||||
<Panel.Layout>
|
<Panel.Layout>
|
||||||
<Panel.Header>
|
<Panel.Header side="left">
|
||||||
<IconButton onClick={() => state.send("TOGGLED_CODE_PANEL_OPEN")}>
|
<IconButton onClick={() => state.send('TOGGLED_CODE_PANEL_OPEN')}>
|
||||||
<X />
|
<X />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<h3>Code</h3>
|
<h3>Code</h3>
|
||||||
<ButtonsGroup>
|
<ButtonsGroup>
|
||||||
<FontSizeButtons>
|
<FontSizeButtons>
|
||||||
<IconButton
|
<IconButton
|
||||||
disabled={!local.isIn("editingCode")}
|
disabled={!local.isIn('editingCode')}
|
||||||
onClick={() => state.send("INCREASED_CODE_FONT_SIZE")}
|
onClick={() => state.send('INCREASED_CODE_FONT_SIZE')}
|
||||||
>
|
>
|
||||||
<ChevronUp />
|
<ChevronUp />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
disabled={!local.isIn("editingCode")}
|
disabled={!local.isIn('editingCode')}
|
||||||
onClick={() => state.send("DECREASED_CODE_FONT_SIZE")}
|
onClick={() => state.send('DECREASED_CODE_FONT_SIZE')}
|
||||||
>
|
>
|
||||||
<ChevronDown />
|
<ChevronDown />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</FontSizeButtons>
|
</FontSizeButtons>
|
||||||
<IconButton onClick={() => local.send("TOGGLED_DOCS")}>
|
<IconButton onClick={() => local.send('TOGGLED_DOCS')}>
|
||||||
<Info />
|
<Info />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
disabled={!local.isIn("editingCode")}
|
disabled={!local.isIn('editingCode')}
|
||||||
onClick={() => local.send("SAVED_CODE")}
|
onClick={() => local.send('SAVED_CODE')}
|
||||||
>
|
>
|
||||||
<PlayCircle />
|
<PlayCircle />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
@ -155,11 +155,11 @@ export default function CodePanel() {
|
||||||
readOnly={isReadOnly}
|
readOnly={isReadOnly}
|
||||||
value={file.code}
|
value={file.code}
|
||||||
error={error}
|
error={error}
|
||||||
onChange={(code) => local.send("CHANGED_CODE", { code })}
|
onChange={(code) => local.send('CHANGED_CODE', { code })}
|
||||||
onSave={() => local.send("SAVED_CODE")}
|
onSave={() => local.send('SAVED_CODE')}
|
||||||
onKey={() => local.send("CLEARED_ERROR")}
|
onKey={() => local.send('CLEARED_ERROR')}
|
||||||
/>
|
/>
|
||||||
<CodeDocs isHidden={!local.isIn("viewingDocs")} />
|
<CodeDocs isHidden={!local.isIn('viewingDocs')} />
|
||||||
</Panel.Content>
|
</Panel.Content>
|
||||||
<Panel.Footer>
|
<Panel.Footer>
|
||||||
{error &&
|
{error &&
|
||||||
|
@ -169,7 +169,7 @@ export default function CodePanel() {
|
||||||
</Panel.Footer>
|
</Panel.Footer>
|
||||||
</Panel.Layout>
|
</Panel.Layout>
|
||||||
) : (
|
) : (
|
||||||
<IconButton onClick={() => state.send("TOGGLED_CODE_PANEL_OPEN")}>
|
<IconButton onClick={() => state.send('TOGGLED_CODE_PANEL_OPEN')}>
|
||||||
<Code />
|
<Code />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
|
@ -177,28 +177,28 @@ export default function CodePanel() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const ButtonsGroup = styled("div", {
|
const ButtonsGroup = styled('div', {
|
||||||
gridRow: "1",
|
gridRow: '1',
|
||||||
gridColumn: "3",
|
gridColumn: '3',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
})
|
})
|
||||||
|
|
||||||
const FontSizeButtons = styled("div", {
|
const FontSizeButtons = styled('div', {
|
||||||
paddingRight: 4,
|
paddingRight: 4,
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
|
|
||||||
"& > button": {
|
'& > button': {
|
||||||
height: "50%",
|
height: '50%',
|
||||||
"&:nth-of-type(1)": {
|
'&:nth-of-type(1)': {
|
||||||
alignItems: "flex-end",
|
alignItems: 'flex-end',
|
||||||
},
|
},
|
||||||
|
|
||||||
"&:nth-of-type(2)": {
|
'&:nth-of-type(2)': {
|
||||||
alignItems: "flex-start",
|
alignItems: 'flex-start',
|
||||||
},
|
},
|
||||||
|
|
||||||
"& svg": {
|
'& svg': {
|
||||||
height: 12,
|
height: 12,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import useKeyboardEvents from "hooks/useKeyboardEvents"
|
import useKeyboardEvents from 'hooks/useKeyboardEvents'
|
||||||
import useLoadOnMount from "hooks/useLoadOnMount"
|
import useLoadOnMount from 'hooks/useLoadOnMount'
|
||||||
import Canvas from "./canvas/canvas"
|
import Canvas from './canvas/canvas'
|
||||||
import StatusBar from "./status-bar"
|
import StatusBar from './status-bar'
|
||||||
import Toolbar from "./toolbar"
|
import Toolbar from './toolbar'
|
||||||
import CodePanel from "./code-panel/code-panel"
|
import CodePanel from './code-panel/code-panel'
|
||||||
import ControlsPanel from "./controls-panel/controls-panel"
|
import ControlsPanel from './controls-panel/controls-panel'
|
||||||
import styled from "styles"
|
import ToolsPanel from './tools-panel/tools-panel'
|
||||||
import StylePanel from "./style-panel/style-panel"
|
import StylePanel from './style-panel/style-panel'
|
||||||
import { useSelector } from "state"
|
import { useSelector } from 'state'
|
||||||
|
import styled from 'styles'
|
||||||
|
|
||||||
export default function Editor() {
|
export default function Editor() {
|
||||||
useKeyboardEvents()
|
useKeyboardEvents()
|
||||||
|
@ -19,9 +20,8 @@ export default function Editor() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<Canvas />
|
|
||||||
<StatusBar />
|
|
||||||
<Toolbar />
|
<Toolbar />
|
||||||
|
<Canvas />
|
||||||
<LeftPanels>
|
<LeftPanels>
|
||||||
<CodePanel />
|
<CodePanel />
|
||||||
{hasControls && <ControlsPanel />}
|
{hasControls && <ControlsPanel />}
|
||||||
|
@ -29,40 +29,43 @@ export default function Editor() {
|
||||||
<RightPanels>
|
<RightPanels>
|
||||||
<StylePanel />
|
<StylePanel />
|
||||||
</RightPanels>
|
</RightPanels>
|
||||||
|
<ToolsPanel />
|
||||||
|
<StatusBar />
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Layout = styled("div", {
|
const Layout = styled('div', {
|
||||||
position: "fixed",
|
position: 'fixed',
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
display: "grid",
|
display: 'grid',
|
||||||
gridTemplateRows: "40px 1fr 40px",
|
gridTemplateRows: '40px 1fr auto 40px',
|
||||||
gridTemplateColumns: "minmax(50%, 400px) 1fr auto",
|
gridTemplateColumns: 'minmax(50%, 400px) 1fr auto',
|
||||||
gridTemplateAreas: `
|
gridTemplateAreas: `
|
||||||
"toolbar toolbar toolbar"
|
"toolbar toolbar toolbar"
|
||||||
"leftPanels main rightPanels"
|
"leftPanels main rightPanels"
|
||||||
|
"tools tools tools"
|
||||||
"statusbar statusbar statusbar"
|
"statusbar statusbar statusbar"
|
||||||
`,
|
`,
|
||||||
})
|
})
|
||||||
|
|
||||||
const LeftPanels = styled("main", {
|
const LeftPanels = styled('main', {
|
||||||
display: "grid",
|
display: 'grid',
|
||||||
gridArea: "leftPanels",
|
gridArea: 'leftPanels',
|
||||||
gridTemplateRows: "1fr auto",
|
gridTemplateRows: '1fr auto',
|
||||||
padding: 8,
|
padding: 8,
|
||||||
gap: 8,
|
gap: 8,
|
||||||
})
|
})
|
||||||
|
|
||||||
const RightPanels = styled("main", {
|
const RightPanels = styled('main', {
|
||||||
display: "grid",
|
display: 'grid',
|
||||||
gridArea: "rightPanels",
|
gridArea: 'rightPanels',
|
||||||
gridTemplateRows: "auto",
|
gridTemplateRows: 'auto',
|
||||||
height: "fit-content",
|
height: 'fit-content',
|
||||||
justifyContent: "flex-end",
|
justifyContent: 'flex-end',
|
||||||
padding: 8,
|
padding: 8,
|
||||||
gap: 8,
|
gap: 8,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,32 +1,60 @@
|
||||||
import styled from "styles"
|
import styled from 'styles'
|
||||||
|
|
||||||
export const IconButton = styled("button", {
|
export const IconButton = styled('button', {
|
||||||
height: "32px",
|
height: '32px',
|
||||||
width: "32px",
|
width: '32px',
|
||||||
backgroundColor: "$panel",
|
backgroundColor: '$panel',
|
||||||
borderRadius: "4px",
|
borderRadius: '4px',
|
||||||
padding: "0",
|
padding: '0',
|
||||||
margin: "0",
|
margin: '0',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
justifyContent: "center",
|
justifyContent: 'center',
|
||||||
outline: "none",
|
outline: 'none',
|
||||||
border: "none",
|
border: 'none',
|
||||||
pointerEvents: "all",
|
pointerEvents: 'all',
|
||||||
cursor: "pointer",
|
cursor: 'pointer',
|
||||||
|
|
||||||
"&:hover:not(:disabled)": {
|
'&:hover:not(:disabled)': {
|
||||||
backgroundColor: "$hover",
|
backgroundColor: '$hover',
|
||||||
},
|
},
|
||||||
|
|
||||||
"&:disabled": {
|
'&:disabled': {
|
||||||
opacity: "0.5",
|
opacity: '0.5',
|
||||||
},
|
},
|
||||||
|
|
||||||
svg: {
|
'& > svg': {
|
||||||
height: "16px",
|
height: '16px',
|
||||||
width: "16px",
|
width: '16px',
|
||||||
strokeWidth: "2px",
|
// strokeWidth: '2px',
|
||||||
stroke: "$text",
|
// stroke: '$text',
|
||||||
|
},
|
||||||
|
|
||||||
|
variants: {
|
||||||
|
size: {
|
||||||
|
medium: {
|
||||||
|
height: 44,
|
||||||
|
width: 44,
|
||||||
|
'& svg': {
|
||||||
|
height: 16,
|
||||||
|
width: 16,
|
||||||
|
strokeWidth: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
large: {
|
||||||
|
height: 44,
|
||||||
|
width: 44,
|
||||||
|
'& svg': {
|
||||||
|
height: 24,
|
||||||
|
width: 24,
|
||||||
|
strokeWidth: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isActive: {
|
||||||
|
true: {
|
||||||
|
color: '$selected',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,14 +4,29 @@ import * as Panel from 'components/panel'
|
||||||
import { useRef } from 'react'
|
import { useRef } from 'react'
|
||||||
import { IconButton } from 'components/shared'
|
import { IconButton } from 'components/shared'
|
||||||
import { Circle, Copy, Lock, Trash, Unlock, X } from 'react-feather'
|
import { Circle, Copy, Lock, Trash, Unlock, X } from 'react-feather'
|
||||||
import { deepCompare, deepCompareArrays, getSelectedShapes } from 'utils/utils'
|
import {
|
||||||
|
deepCompare,
|
||||||
|
deepCompareArrays,
|
||||||
|
getPage,
|
||||||
|
getSelectedShapes,
|
||||||
|
} from 'utils/utils'
|
||||||
import { shades, fills, strokes } from 'lib/colors'
|
import { shades, fills, strokes } from 'lib/colors'
|
||||||
|
|
||||||
import ColorPicker from './color-picker'
|
import ColorPicker from './color-picker'
|
||||||
import AlignDistribute from './align-distribute'
|
import AlignDistribute from './align-distribute'
|
||||||
import { ShapeStyles } from 'types'
|
import { ShapeStyles } from 'types'
|
||||||
import WidthPicker from './width-picker'
|
import WidthPicker from './width-picker'
|
||||||
import { CopyIcon } from '@radix-ui/react-icons'
|
import {
|
||||||
|
AspectRatioIcon,
|
||||||
|
BoxIcon,
|
||||||
|
CopyIcon,
|
||||||
|
EyeClosedIcon,
|
||||||
|
EyeOpenIcon,
|
||||||
|
LockClosedIcon,
|
||||||
|
LockOpen1Icon,
|
||||||
|
RotateCounterClockwiseIcon,
|
||||||
|
TrashIcon,
|
||||||
|
} from '@radix-ui/react-icons'
|
||||||
|
|
||||||
const fillColors = { ...shades, ...fills }
|
const fillColors = { ...shades, ...fills }
|
||||||
const strokeColors = { ...shades, ...strokes }
|
const strokeColors = { ...shades, ...strokes }
|
||||||
|
@ -43,31 +58,47 @@ function SelectedShapeStyles({}: {}) {
|
||||||
deepCompareArrays
|
deepCompareArrays
|
||||||
)
|
)
|
||||||
|
|
||||||
const shapesStyle = useSelector((s) => {
|
const isAllLocked = useSelector((s) => {
|
||||||
const { currentStyle } = s.data
|
const page = getPage(s.data)
|
||||||
const shapes = getSelectedShapes(s.data)
|
return selectedIds.every((id) => page.shapes[id].isLocked)
|
||||||
|
})
|
||||||
|
|
||||||
if (shapes.length === 0) {
|
const isAllAspectLocked = useSelector((s) => {
|
||||||
|
const page = getPage(s.data)
|
||||||
|
return selectedIds.every((id) => page.shapes[id].isAspectRatioLocked)
|
||||||
|
})
|
||||||
|
|
||||||
|
const isAllHidden = useSelector((s) => {
|
||||||
|
const page = getPage(s.data)
|
||||||
|
return selectedIds.every((id) => page.shapes[id].isHidden)
|
||||||
|
})
|
||||||
|
|
||||||
|
const commonStyle = useSelector((s) => {
|
||||||
|
const { currentStyle } = s.data
|
||||||
|
|
||||||
|
if (selectedIds.length === 0) {
|
||||||
return currentStyle
|
return currentStyle
|
||||||
}
|
}
|
||||||
|
const page = getPage(s.data)
|
||||||
|
const shapeStyles = selectedIds.map((id) => page.shapes[id].style)
|
||||||
|
|
||||||
const style: Partial<ShapeStyles> = {}
|
const commonStyle: Partial<ShapeStyles> = {}
|
||||||
const overrides = new Set<string>([])
|
const overrides = new Set<string>([])
|
||||||
|
|
||||||
for (const shape of shapes) {
|
for (const shapeStyle of shapeStyles) {
|
||||||
for (let key in currentStyle) {
|
for (let key in currentStyle) {
|
||||||
if (overrides.has(key)) continue
|
if (overrides.has(key)) continue
|
||||||
if (style[key] === undefined) {
|
if (commonStyle[key] === undefined) {
|
||||||
style[key] = shape.style[key]
|
commonStyle[key] = shapeStyle[key]
|
||||||
} else {
|
} else {
|
||||||
if (style[key] === shape.style[key]) continue
|
if (commonStyle[key] === shapeStyle[key]) continue
|
||||||
style[key] = currentStyle[key]
|
commonStyle[key] = currentStyle[key]
|
||||||
overrides.add(key)
|
overrides.add(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return style
|
return commonStyle
|
||||||
}, deepCompare)
|
}, deepCompare)
|
||||||
|
|
||||||
const hasSelection = selectedIds.length > 0
|
const hasSelection = selectedIds.length > 0
|
||||||
|
@ -83,19 +114,19 @@ function SelectedShapeStyles({}: {}) {
|
||||||
<Content>
|
<Content>
|
||||||
<ColorPicker
|
<ColorPicker
|
||||||
label="Fill"
|
label="Fill"
|
||||||
color={shapesStyle.fill}
|
color={commonStyle.fill}
|
||||||
colors={fillColors}
|
colors={fillColors}
|
||||||
onChange={(color) => state.send('CHANGED_STYLE', { fill: color })}
|
onChange={(color) => state.send('CHANGED_STYLE', { fill: color })}
|
||||||
/>
|
/>
|
||||||
<ColorPicker
|
<ColorPicker
|
||||||
label="Stroke"
|
label="Stroke"
|
||||||
color={shapesStyle.stroke}
|
color={commonStyle.stroke}
|
||||||
colors={strokeColors}
|
colors={strokeColors}
|
||||||
onChange={(color) => state.send('CHANGED_STYLE', { stroke: color })}
|
onChange={(color) => state.send('CHANGED_STYLE', { stroke: color })}
|
||||||
/>
|
/>
|
||||||
<Row>
|
<Row>
|
||||||
<label htmlFor="width">Width</label>
|
<label htmlFor="width">Width</label>
|
||||||
<WidthPicker strokeWidth={Number(shapesStyle.strokeWidth)} />
|
<WidthPicker strokeWidth={Number(commonStyle.strokeWidth)} />
|
||||||
</Row>
|
</Row>
|
||||||
<AlignDistribute
|
<AlignDistribute
|
||||||
hasTwoOrMore={selectedIds.length > 1}
|
hasTwoOrMore={selectedIds.length > 1}
|
||||||
|
@ -104,19 +135,39 @@ function SelectedShapeStyles({}: {}) {
|
||||||
<ButtonsRow>
|
<ButtonsRow>
|
||||||
<IconButton
|
<IconButton
|
||||||
disabled={!hasSelection}
|
disabled={!hasSelection}
|
||||||
onClick={() => state.send('DELETED')}
|
onClick={() => state.send('DUPLICATED')}
|
||||||
>
|
>
|
||||||
<Trash />
|
<CopyIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
disabled={!hasSelection}
|
disabled={!hasSelection}
|
||||||
onClick={() => state.send('DUPLICATED')}
|
onClick={() => state.send('ROTATED_CCW')}
|
||||||
>
|
>
|
||||||
<Copy />
|
<RotateCounterClockwiseIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
<IconButton>
|
disabled={!hasSelection}
|
||||||
<Unlock />
|
onClick={() => state.send('DELETED')}
|
||||||
|
>
|
||||||
|
<TrashIcon />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
disabled={!hasSelection}
|
||||||
|
onClick={() => state.send('TOGGLED_SHAPE_HIDE')}
|
||||||
|
>
|
||||||
|
{isAllHidden ? <EyeClosedIcon /> : <EyeOpenIcon />}
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
disabled={!hasSelection}
|
||||||
|
onClick={() => state.send('TOGGLED_SHAPE_LOCK')}
|
||||||
|
>
|
||||||
|
{isAllLocked ? <LockClosedIcon /> : <LockOpen1Icon />}
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
disabled={!hasSelection}
|
||||||
|
onClick={() => state.send('TOGGLED_SHAPE_ASPECT_LOCK')}
|
||||||
|
>
|
||||||
|
{isAllAspectLocked ? <AspectRatioIcon /> : <BoxIcon />}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</ButtonsRow>
|
</ButtonsRow>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import { DotsHorizontalIcon } from '@radix-ui/react-icons'
|
|
||||||
import * as RadioGroup from '@radix-ui/react-radio-group'
|
import * as RadioGroup from '@radix-ui/react-radio-group'
|
||||||
import { IconButton } from 'components/shared'
|
|
||||||
import { ChangeEvent } from 'react'
|
import { ChangeEvent } from 'react'
|
||||||
import { Circle } from 'react-feather'
|
import { Circle } from 'react-feather'
|
||||||
import state from 'state'
|
import state from 'state'
|
||||||
|
@ -26,7 +24,7 @@ export default function WidthPicker({
|
||||||
<Circle size={12} />
|
<Circle size={12} />
|
||||||
</RadioItem>
|
</RadioItem>
|
||||||
<RadioItem value="8" isActive={strokeWidth === 8}>
|
<RadioItem value="8" isActive={strokeWidth === 8}>
|
||||||
<Circle size={18} />
|
<Circle size={22} />
|
||||||
</RadioItem>
|
</RadioItem>
|
||||||
</Group>
|
</Group>
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,63 +26,6 @@ export default function Toolbar() {
|
||||||
<Button>
|
<Button>
|
||||||
<Menu />
|
<Menu />
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => state.send('TOGGLED_TOOL_LOCK')}>
|
|
||||||
{isToolLocked ? <Lock /> : <Unlock />}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
isSelected={activeTool === 'select'}
|
|
||||||
onClick={() => state.send('SELECTED_SELECT_TOOL')}
|
|
||||||
>
|
|
||||||
Select
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
isSelected={activeTool === 'draw'}
|
|
||||||
onClick={() => state.send('SELECTED_DRAW_TOOL')}
|
|
||||||
>
|
|
||||||
Draw
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
isSelected={activeTool === 'dot'}
|
|
||||||
onClick={() => state.send('SELECTED_DOT_TOOL')}
|
|
||||||
>
|
|
||||||
Dot
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
isSelected={activeTool === 'circle'}
|
|
||||||
onClick={() => state.send('SELECTED_CIRCLE_TOOL')}
|
|
||||||
>
|
|
||||||
Circle
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
isSelected={activeTool === 'ellipse'}
|
|
||||||
onClick={() => state.send('SELECTED_ELLIPSE_TOOL')}
|
|
||||||
>
|
|
||||||
Ellipse
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
isSelected={activeTool === 'ray'}
|
|
||||||
onClick={() => state.send('SELECTED_RAY_TOOL')}
|
|
||||||
>
|
|
||||||
Ray
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
isSelected={activeTool === 'line'}
|
|
||||||
onClick={() => state.send('SELECTED_LINE_TOOL')}
|
|
||||||
>
|
|
||||||
Line
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
isSelected={activeTool === 'polyline'}
|
|
||||||
onClick={() => state.send('SELECTED_POLYLINE_TOOL')}
|
|
||||||
>
|
|
||||||
Polyline
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
isSelected={activeTool === 'rectangle'}
|
|
||||||
onClick={() => state.send('SELECTED_RECTANGLE_TOOL')}
|
|
||||||
>
|
|
||||||
Rectangle
|
|
||||||
</Button>
|
|
||||||
<Button onClick={() => state.send('RESET_CAMERA')}>Reset Camera</Button>
|
<Button onClick={() => state.send('RESET_CAMERA')}>Reset Camera</Button>
|
||||||
</Section>
|
</Section>
|
||||||
<Section>
|
<Section>
|
||||||
|
|
162
components/tools-panel/tools-panel.tsx
Normal file
162
components/tools-panel/tools-panel.tsx
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
import {
|
||||||
|
CircleIcon,
|
||||||
|
CursorArrowIcon,
|
||||||
|
DividerHorizontalIcon,
|
||||||
|
DotIcon,
|
||||||
|
LineHeightIcon,
|
||||||
|
LockClosedIcon,
|
||||||
|
LockOpen1Icon,
|
||||||
|
Pencil1Icon,
|
||||||
|
Pencil2Icon,
|
||||||
|
SewingPinIcon,
|
||||||
|
SquareIcon,
|
||||||
|
} from '@radix-ui/react-icons'
|
||||||
|
import { IconButton } from 'components/shared'
|
||||||
|
import React from 'react'
|
||||||
|
import state, { useSelector } from 'state'
|
||||||
|
import styled from 'styles'
|
||||||
|
import { ShapeType } from 'types'
|
||||||
|
|
||||||
|
const selectSelectTool = () => state.send('SELECTED_SELECT_TOOL')
|
||||||
|
const selectDrawTool = () => state.send('SELECTED_DRAW_TOOL')
|
||||||
|
const selectDotTool = () => state.send('SELECTED_DOT_TOOL')
|
||||||
|
const selectCircleTool = () => state.send('SELECTED_CIRCLE_TOOL')
|
||||||
|
const selectEllipseTool = () => state.send('SELECTED_ELLIPSE_TOOL')
|
||||||
|
const selectRayTool = () => state.send('SELECTED_RAY_TOOL')
|
||||||
|
const selectLineTool = () => state.send('SELECTED_LINE_TOOL')
|
||||||
|
const selectPolylineTool = () => state.send('SELECTED_POLYLINE_TOOL')
|
||||||
|
const selectRectangleTool = () => state.send('SELECTED_RECTANGLE_TOOL')
|
||||||
|
const selectToolLock = () => state.send('TOGGLED_TOOL_LOCK')
|
||||||
|
|
||||||
|
export default function ToolsPanel() {
|
||||||
|
const activeTool = useSelector((state) =>
|
||||||
|
state.whenIn({
|
||||||
|
selecting: 'select',
|
||||||
|
dot: ShapeType.Dot,
|
||||||
|
circle: ShapeType.Circle,
|
||||||
|
ellipse: ShapeType.Ellipse,
|
||||||
|
ray: ShapeType.Ray,
|
||||||
|
line: ShapeType.Line,
|
||||||
|
polyline: ShapeType.Polyline,
|
||||||
|
rectangle: ShapeType.Rectangle,
|
||||||
|
draw: ShapeType.Draw,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const isToolLocked = useSelector((s) => s.data.settings.isToolLocked)
|
||||||
|
|
||||||
|
const isPenLocked = useSelector((s) => s.data.settings.isPenLocked)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<OuterContainer>
|
||||||
|
<Container>
|
||||||
|
<IconButton
|
||||||
|
name="select"
|
||||||
|
size="large"
|
||||||
|
onClick={selectSelectTool}
|
||||||
|
isActive={activeTool === 'select'}
|
||||||
|
>
|
||||||
|
<CursorArrowIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Container>
|
||||||
|
<Container>
|
||||||
|
<IconButton
|
||||||
|
name={ShapeType.Draw}
|
||||||
|
size="large"
|
||||||
|
onClick={selectDrawTool}
|
||||||
|
isActive={activeTool === ShapeType.Draw}
|
||||||
|
>
|
||||||
|
<Pencil1Icon />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
name={ShapeType.Rectangle}
|
||||||
|
size="large"
|
||||||
|
onClick={selectRectangleTool}
|
||||||
|
isActive={activeTool === ShapeType.Rectangle}
|
||||||
|
>
|
||||||
|
<SquareIcon />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
name={ShapeType.Circle}
|
||||||
|
size="large"
|
||||||
|
onClick={selectCircleTool}
|
||||||
|
isActive={activeTool === ShapeType.Circle}
|
||||||
|
>
|
||||||
|
<CircleIcon />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
name={ShapeType.Ellipse}
|
||||||
|
size="large"
|
||||||
|
onClick={selectEllipseTool}
|
||||||
|
isActive={activeTool === ShapeType.Ellipse}
|
||||||
|
>
|
||||||
|
<CircleIcon transform="rotate(-45) scale(1, .8)" />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
name={ShapeType.Line}
|
||||||
|
size="large"
|
||||||
|
onClick={selectLineTool}
|
||||||
|
isActive={activeTool === ShapeType.Line}
|
||||||
|
>
|
||||||
|
<DividerHorizontalIcon transform="rotate(-45)" />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
name={ShapeType.Ray}
|
||||||
|
size="large"
|
||||||
|
onClick={selectRayTool}
|
||||||
|
isActive={activeTool === ShapeType.Ray}
|
||||||
|
>
|
||||||
|
<SewingPinIcon transform="rotate(-135)" />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
name={ShapeType.Dot}
|
||||||
|
size="large"
|
||||||
|
onClick={selectDotTool}
|
||||||
|
isActive={activeTool === ShapeType.Dot}
|
||||||
|
>
|
||||||
|
<DotIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Container>
|
||||||
|
<Container>
|
||||||
|
<IconButton size="medium" onClick={selectToolLock}>
|
||||||
|
{isToolLocked ? <LockClosedIcon /> : <LockOpen1Icon />}
|
||||||
|
</IconButton>
|
||||||
|
{isPenLocked && (
|
||||||
|
<IconButton size="medium" onClick={selectToolLock}>
|
||||||
|
<Pencil2Icon />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
|
</OuterContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const OuterContainer = styled('div', {
|
||||||
|
gridArea: 'tools',
|
||||||
|
padding: '0 8px 12px 8px',
|
||||||
|
height: '100%',
|
||||||
|
width: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: 16,
|
||||||
|
})
|
||||||
|
|
||||||
|
const Container = styled('div', {
|
||||||
|
position: 'relative',
|
||||||
|
backgroundColor: '$panel',
|
||||||
|
borderRadius: '4px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
border: '1px solid $border',
|
||||||
|
pointerEvents: 'all',
|
||||||
|
userSelect: 'none',
|
||||||
|
zIndex: 200,
|
||||||
|
boxShadow: '0px 2px 25px rgba(0,0,0,.16)',
|
||||||
|
height: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
padding: 4,
|
||||||
|
|
||||||
|
'& svg': {
|
||||||
|
strokeWidth: 0,
|
||||||
|
},
|
||||||
|
})
|
|
@ -1,7 +1,7 @@
|
||||||
import CodeShape from "./index"
|
import CodeShape from './index'
|
||||||
import { v4 as uuid } from "uuid"
|
import { v4 as uuid } from 'uuid'
|
||||||
import { CircleShape, ShapeType } from "types"
|
import { CircleShape, ShapeType } from 'types'
|
||||||
import { vectorToPoint } from "utils/utils"
|
import { vectorToPoint } from 'utils/utils'
|
||||||
|
|
||||||
export default class Circle extends CodeShape<CircleShape> {
|
export default class Circle extends CodeShape<CircleShape> {
|
||||||
constructor(props = {} as Partial<CircleShape>) {
|
constructor(props = {} as Partial<CircleShape>) {
|
||||||
|
@ -11,15 +11,18 @@ export default class Circle extends CodeShape<CircleShape> {
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
type: ShapeType.Circle,
|
type: ShapeType.Circle,
|
||||||
isGenerated: true,
|
isGenerated: true,
|
||||||
name: "Circle",
|
name: 'Circle',
|
||||||
parentId: "page0",
|
parentId: 'page0',
|
||||||
childIndex: 0,
|
childIndex: 0,
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
radius: 20,
|
radius: 20,
|
||||||
|
isAspectRatioLocked: false,
|
||||||
|
isLocked: false,
|
||||||
|
isHidden: false,
|
||||||
style: {
|
style: {
|
||||||
fill: "#c6cacb",
|
fill: '#c6cacb',
|
||||||
stroke: "#000",
|
stroke: '#000',
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
},
|
},
|
||||||
...props,
|
...props,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import CodeShape from "./index"
|
import CodeShape from './index'
|
||||||
import { v4 as uuid } from "uuid"
|
import { v4 as uuid } from 'uuid'
|
||||||
import { DotShape, ShapeType } from "types"
|
import { DotShape, ShapeType } from 'types'
|
||||||
import { vectorToPoint } from "utils/utils"
|
import { vectorToPoint } from 'utils/utils'
|
||||||
|
|
||||||
export default class Dot extends CodeShape<DotShape> {
|
export default class Dot extends CodeShape<DotShape> {
|
||||||
constructor(props = {} as Partial<DotShape>) {
|
constructor(props = {} as Partial<DotShape>) {
|
||||||
|
@ -11,14 +11,17 @@ export default class Dot extends CodeShape<DotShape> {
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
type: ShapeType.Dot,
|
type: ShapeType.Dot,
|
||||||
isGenerated: true,
|
isGenerated: true,
|
||||||
name: "Dot",
|
name: 'Dot',
|
||||||
parentId: "page0",
|
parentId: 'page0',
|
||||||
childIndex: 0,
|
childIndex: 0,
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
|
isAspectRatioLocked: false,
|
||||||
|
isLocked: false,
|
||||||
|
isHidden: false,
|
||||||
style: {
|
style: {
|
||||||
fill: "#c6cacb",
|
fill: '#c6cacb',
|
||||||
stroke: "#000",
|
stroke: '#000',
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
},
|
},
|
||||||
...props,
|
...props,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import CodeShape from "./index"
|
import CodeShape from './index'
|
||||||
import { v4 as uuid } from "uuid"
|
import { v4 as uuid } from 'uuid'
|
||||||
import { EllipseShape, ShapeType } from "types"
|
import { EllipseShape, ShapeType } from 'types'
|
||||||
import { vectorToPoint } from "utils/utils"
|
import { vectorToPoint } from 'utils/utils'
|
||||||
|
|
||||||
export default class Ellipse extends CodeShape<EllipseShape> {
|
export default class Ellipse extends CodeShape<EllipseShape> {
|
||||||
constructor(props = {} as Partial<EllipseShape>) {
|
constructor(props = {} as Partial<EllipseShape>) {
|
||||||
|
@ -11,16 +11,19 @@ export default class Ellipse extends CodeShape<EllipseShape> {
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
type: ShapeType.Ellipse,
|
type: ShapeType.Ellipse,
|
||||||
isGenerated: true,
|
isGenerated: true,
|
||||||
name: "Ellipse",
|
name: 'Ellipse',
|
||||||
parentId: "page0",
|
parentId: 'page0',
|
||||||
childIndex: 0,
|
childIndex: 0,
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
radiusX: 20,
|
radiusX: 20,
|
||||||
radiusY: 20,
|
radiusY: 20,
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
|
isAspectRatioLocked: false,
|
||||||
|
isLocked: false,
|
||||||
|
isHidden: false,
|
||||||
style: {
|
style: {
|
||||||
fill: "#c6cacb",
|
fill: '#c6cacb',
|
||||||
stroke: "#000",
|
stroke: '#000',
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
},
|
},
|
||||||
...props,
|
...props,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import CodeShape from "./index"
|
import CodeShape from './index'
|
||||||
import { v4 as uuid } from "uuid"
|
import { v4 as uuid } from 'uuid'
|
||||||
import { LineShape, ShapeType } from "types"
|
import { LineShape, ShapeType } from 'types'
|
||||||
import { vectorToPoint } from "utils/utils"
|
import { vectorToPoint } from 'utils/utils'
|
||||||
|
|
||||||
export default class Line extends CodeShape<LineShape> {
|
export default class Line extends CodeShape<LineShape> {
|
||||||
constructor(props = {} as Partial<LineShape>) {
|
constructor(props = {} as Partial<LineShape>) {
|
||||||
|
@ -12,15 +12,18 @@ export default class Line extends CodeShape<LineShape> {
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
type: ShapeType.Line,
|
type: ShapeType.Line,
|
||||||
isGenerated: true,
|
isGenerated: true,
|
||||||
name: "Line",
|
name: 'Line',
|
||||||
parentId: "page0",
|
parentId: 'page0',
|
||||||
childIndex: 0,
|
childIndex: 0,
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
direction: [-0.5, 0.5],
|
direction: [-0.5, 0.5],
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
|
isAspectRatioLocked: false,
|
||||||
|
isLocked: false,
|
||||||
|
isHidden: false,
|
||||||
style: {
|
style: {
|
||||||
fill: "#c6cacb",
|
fill: '#c6cacb',
|
||||||
stroke: "#000",
|
stroke: '#000',
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
},
|
},
|
||||||
...props,
|
...props,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import CodeShape from "./index"
|
import CodeShape from './index'
|
||||||
import { v4 as uuid } from "uuid"
|
import { v4 as uuid } from 'uuid'
|
||||||
import { PolylineShape, ShapeType } from "types"
|
import { PolylineShape, ShapeType } from 'types'
|
||||||
import { vectorToPoint } from "utils/utils"
|
import { vectorToPoint } from 'utils/utils'
|
||||||
|
|
||||||
export default class Polyline extends CodeShape<PolylineShape> {
|
export default class Polyline extends CodeShape<PolylineShape> {
|
||||||
constructor(props = {} as Partial<PolylineShape>) {
|
constructor(props = {} as Partial<PolylineShape>) {
|
||||||
|
@ -12,15 +12,18 @@ export default class Polyline extends CodeShape<PolylineShape> {
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
type: ShapeType.Polyline,
|
type: ShapeType.Polyline,
|
||||||
isGenerated: true,
|
isGenerated: true,
|
||||||
name: "Polyline",
|
name: 'Polyline',
|
||||||
parentId: "page0",
|
parentId: 'page0',
|
||||||
childIndex: 0,
|
childIndex: 0,
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
points: [[0, 0]],
|
points: [[0, 0]],
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
|
isAspectRatioLocked: false,
|
||||||
|
isLocked: false,
|
||||||
|
isHidden: false,
|
||||||
style: {
|
style: {
|
||||||
fill: "none",
|
fill: 'none',
|
||||||
stroke: "#000",
|
stroke: '#000',
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
},
|
},
|
||||||
...props,
|
...props,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import CodeShape from "./index"
|
import CodeShape from './index'
|
||||||
import { v4 as uuid } from "uuid"
|
import { v4 as uuid } from 'uuid'
|
||||||
import { RayShape, ShapeType } from "types"
|
import { RayShape, ShapeType } from 'types'
|
||||||
import { vectorToPoint } from "utils/utils"
|
import { vectorToPoint } from 'utils/utils'
|
||||||
|
|
||||||
export default class Ray extends CodeShape<RayShape> {
|
export default class Ray extends CodeShape<RayShape> {
|
||||||
constructor(props = {} as Partial<RayShape>) {
|
constructor(props = {} as Partial<RayShape>) {
|
||||||
|
@ -12,15 +12,18 @@ export default class Ray extends CodeShape<RayShape> {
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
type: ShapeType.Ray,
|
type: ShapeType.Ray,
|
||||||
isGenerated: true,
|
isGenerated: true,
|
||||||
name: "Ray",
|
name: 'Ray',
|
||||||
parentId: "page0",
|
parentId: 'page0',
|
||||||
childIndex: 0,
|
childIndex: 0,
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
direction: [0, 1],
|
direction: [0, 1],
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
|
isAspectRatioLocked: false,
|
||||||
|
isLocked: false,
|
||||||
|
isHidden: false,
|
||||||
style: {
|
style: {
|
||||||
fill: "#c6cacb",
|
fill: '#c6cacb',
|
||||||
stroke: "#000",
|
stroke: '#000',
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
},
|
},
|
||||||
...props,
|
...props,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import CodeShape from "./index"
|
import CodeShape from './index'
|
||||||
import { v4 as uuid } from "uuid"
|
import { v4 as uuid } from 'uuid'
|
||||||
import { RectangleShape, ShapeType } from "types"
|
import { RectangleShape, ShapeType } from 'types'
|
||||||
import { vectorToPoint } from "utils/utils"
|
import { vectorToPoint } from 'utils/utils'
|
||||||
|
|
||||||
export default class Rectangle extends CodeShape<RectangleShape> {
|
export default class Rectangle extends CodeShape<RectangleShape> {
|
||||||
constructor(props = {} as Partial<RectangleShape>) {
|
constructor(props = {} as Partial<RectangleShape>) {
|
||||||
|
@ -12,16 +12,19 @@ export default class Rectangle extends CodeShape<RectangleShape> {
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
type: ShapeType.Rectangle,
|
type: ShapeType.Rectangle,
|
||||||
isGenerated: true,
|
isGenerated: true,
|
||||||
name: "Rectangle",
|
name: 'Rectangle',
|
||||||
parentId: "page0",
|
parentId: 'page0',
|
||||||
childIndex: 0,
|
childIndex: 0,
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
size: [100, 100],
|
size: [100, 100],
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
radius: 2,
|
radius: 2,
|
||||||
|
isAspectRatioLocked: false,
|
||||||
|
isLocked: false,
|
||||||
|
isHidden: false,
|
||||||
style: {
|
style: {
|
||||||
fill: "#c6cacb",
|
fill: '#c6cacb',
|
||||||
stroke: "#000",
|
stroke: '#000',
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
},
|
},
|
||||||
...props,
|
...props,
|
||||||
|
|
|
@ -21,6 +21,9 @@ const circle = registerShapeUtils<CircleShape>({
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
radius: 1,
|
radius: 1,
|
||||||
|
isAspectRatioLocked: false,
|
||||||
|
isLocked: false,
|
||||||
|
isHidden: false,
|
||||||
style: {
|
style: {
|
||||||
fill: '#c6cacb',
|
fill: '#c6cacb',
|
||||||
stroke: '#000',
|
stroke: '#000',
|
||||||
|
@ -125,13 +128,8 @@ const circle = registerShapeUtils<CircleShape>({
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
setParent(shape, parentId) {
|
setProperty(shape, prop, value) {
|
||||||
shape.parentId = parentId
|
shape[prop] = value
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
setChildIndex(shape, childIndex) {
|
|
||||||
shape.childIndex = childIndex
|
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,9 @@ const dot = registerShapeUtils<DotShape>({
|
||||||
childIndex: 0,
|
childIndex: 0,
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
|
isAspectRatioLocked: false,
|
||||||
|
isLocked: false,
|
||||||
|
isHidden: false,
|
||||||
style: {
|
style: {
|
||||||
fill: '#c6cacb',
|
fill: '#c6cacb',
|
||||||
strokeWidth: '0',
|
strokeWidth: '0',
|
||||||
|
@ -94,13 +97,8 @@ const dot = registerShapeUtils<DotShape>({
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
setParent(shape, parentId) {
|
setProperty(shape, prop, value) {
|
||||||
shape.parentId = parentId
|
shape[prop] = value
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
setChildIndex(shape, childIndex) {
|
|
||||||
shape.childIndex = childIndex
|
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,9 @@ const draw = registerShapeUtils<DrawShape>({
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
points: [[0, 0]],
|
points: [[0, 0]],
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
|
isAspectRatioLocked: false,
|
||||||
|
isLocked: false,
|
||||||
|
isHidden: false,
|
||||||
...props,
|
...props,
|
||||||
style: {
|
style: {
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
|
@ -169,25 +172,8 @@ const draw = registerShapeUtils<DrawShape>({
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
setParent(shape, parentId) {
|
setProperty(shape, prop, value) {
|
||||||
shape.parentId = parentId
|
shape[prop] = value
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
setChildIndex(shape, childIndex) {
|
|
||||||
shape.childIndex = childIndex
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
setPoints(shape, points) {
|
|
||||||
// const bounds = getBoundsFromPoints(points)
|
|
||||||
// const corner = [bounds.minX, bounds.minY]
|
|
||||||
// const nudged = points.map((point) => vec.sub(point, corner))
|
|
||||||
// this.boundsCache.set(shape, translategetBoundsFromPoints(nudged))
|
|
||||||
// shape.point = vec.add(shape.point, corner)
|
|
||||||
|
|
||||||
shape.points = points
|
|
||||||
|
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,9 @@ const ellipse = registerShapeUtils<EllipseShape>({
|
||||||
radiusX: 1,
|
radiusX: 1,
|
||||||
radiusY: 1,
|
radiusY: 1,
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
|
isAspectRatioLocked: false,
|
||||||
|
isLocked: false,
|
||||||
|
isHidden: false,
|
||||||
style: {
|
style: {
|
||||||
fill: '#c6cacb',
|
fill: '#c6cacb',
|
||||||
stroke: '#000',
|
stroke: '#000',
|
||||||
|
@ -137,13 +140,8 @@ const ellipse = registerShapeUtils<EllipseShape>({
|
||||||
return this.transform(shape, bounds, info)
|
return this.transform(shape, bounds, info)
|
||||||
},
|
},
|
||||||
|
|
||||||
setParent(shape, parentId) {
|
setProperty(shape, prop, value) {
|
||||||
shape.parentId = parentId
|
shape[prop] = value
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
setChildIndex(shape, childIndex) {
|
|
||||||
shape.childIndex = childIndex
|
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -8,15 +8,16 @@ import {
|
||||||
Edge,
|
Edge,
|
||||||
ShapeByType,
|
ShapeByType,
|
||||||
ShapeStyles,
|
ShapeStyles,
|
||||||
} from "types"
|
PropsOfType,
|
||||||
import circle from "./circle"
|
} from 'types'
|
||||||
import dot from "./dot"
|
import circle from './circle'
|
||||||
import polyline from "./polyline"
|
import dot from './dot'
|
||||||
import rectangle from "./rectangle"
|
import polyline from './polyline'
|
||||||
import ellipse from "./ellipse"
|
import rectangle from './rectangle'
|
||||||
import line from "./line"
|
import ellipse from './ellipse'
|
||||||
import ray from "./ray"
|
import line from './line'
|
||||||
import draw from "./draw"
|
import ray from './ray'
|
||||||
|
import draw from './draw'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Shape Utiliies
|
Shape Utiliies
|
||||||
|
@ -82,21 +83,11 @@ export interface ShapeUtility<K extends Readonly<Shape>> {
|
||||||
}
|
}
|
||||||
): ShapeUtility<K>
|
): ShapeUtility<K>
|
||||||
|
|
||||||
// Move a shape to a new parent.
|
setProperty<P extends keyof K>(
|
||||||
setParent(this: ShapeUtility<K>, shape: K, parentId: string): ShapeUtility<K>
|
|
||||||
|
|
||||||
// Change the child index of a shape
|
|
||||||
setChildIndex(
|
|
||||||
this: ShapeUtility<K>,
|
this: ShapeUtility<K>,
|
||||||
shape: K,
|
shape: K,
|
||||||
childIndex: number
|
prop: P,
|
||||||
): ShapeUtility<K>
|
value: K[P]
|
||||||
|
|
||||||
// Add a point
|
|
||||||
setPoints?(
|
|
||||||
this: ShapeUtility<K>,
|
|
||||||
shape: K,
|
|
||||||
points: number[][]
|
|
||||||
): ShapeUtility<K>
|
): ShapeUtility<K>
|
||||||
|
|
||||||
// Render a shape to JSX.
|
// Render a shape to JSX.
|
||||||
|
@ -151,7 +142,7 @@ export function registerShapeUtils<T extends Shape>(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createShape<T extends Shape>(
|
export function createShape<T extends Shape>(
|
||||||
type: T["type"],
|
type: T['type'],
|
||||||
props: Partial<T>
|
props: Partial<T>
|
||||||
) {
|
) {
|
||||||
return shapeUtilityMap[type].create(props) as T
|
return shapeUtilityMap[type].create(props) as T
|
||||||
|
|
|
@ -21,6 +21,9 @@ const line = registerShapeUtils<LineShape>({
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
direction: [0, 0],
|
direction: [0, 0],
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
|
isAspectRatioLocked: false,
|
||||||
|
isLocked: false,
|
||||||
|
isHidden: false,
|
||||||
style: {
|
style: {
|
||||||
fill: '#c6cacb',
|
fill: '#c6cacb',
|
||||||
stroke: '#000',
|
stroke: '#000',
|
||||||
|
@ -102,13 +105,8 @@ const line = registerShapeUtils<LineShape>({
|
||||||
return this.transform(shape, bounds, info)
|
return this.transform(shape, bounds, info)
|
||||||
},
|
},
|
||||||
|
|
||||||
setParent(shape, parentId) {
|
setProperty(shape, prop, value) {
|
||||||
shape.parentId = parentId
|
shape[prop] = value
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
setChildIndex(shape, childIndex) {
|
|
||||||
shape.childIndex = childIndex
|
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,9 @@ const polyline = registerShapeUtils<PolylineShape>({
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
points: [[0, 0]],
|
points: [[0, 0]],
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
|
isAspectRatioLocked: false,
|
||||||
|
isLocked: false,
|
||||||
|
isHidden: false,
|
||||||
style: {
|
style: {
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
strokeLinecap: 'round',
|
strokeLinecap: 'round',
|
||||||
|
@ -127,13 +130,8 @@ const polyline = registerShapeUtils<PolylineShape>({
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
setParent(shape, parentId) {
|
setProperty(shape, prop, value) {
|
||||||
shape.parentId = parentId
|
shape[prop] = value
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
setChildIndex(shape, childIndex) {
|
|
||||||
shape.childIndex = childIndex
|
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,9 @@ const ray = registerShapeUtils<RayShape>({
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
direction: [0, 1],
|
direction: [0, 1],
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
|
isAspectRatioLocked: false,
|
||||||
|
isLocked: false,
|
||||||
|
isHidden: false,
|
||||||
style: {
|
style: {
|
||||||
fill: '#c6cacb',
|
fill: '#c6cacb',
|
||||||
stroke: '#000',
|
stroke: '#000',
|
||||||
|
@ -102,13 +105,8 @@ const ray = registerShapeUtils<RayShape>({
|
||||||
return this.transform(shape, bounds, info)
|
return this.transform(shape, bounds, info)
|
||||||
},
|
},
|
||||||
|
|
||||||
setParent(shape, parentId) {
|
setProperty(shape, prop, value) {
|
||||||
shape.parentId = parentId
|
shape[prop] = value
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
setChildIndex(shape, childIndex) {
|
|
||||||
shape.childIndex = childIndex
|
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,9 @@ const rectangle = registerShapeUtils<RectangleShape>({
|
||||||
size: [1, 1],
|
size: [1, 1],
|
||||||
radius: 2,
|
radius: 2,
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
|
isAspectRatioLocked: false,
|
||||||
|
isLocked: false,
|
||||||
|
isHidden: false,
|
||||||
style: {
|
style: {
|
||||||
fill: '#c6cacb',
|
fill: '#c6cacb',
|
||||||
stroke: '#000',
|
stroke: '#000',
|
||||||
|
@ -140,13 +143,8 @@ const rectangle = registerShapeUtils<RectangleShape>({
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
setParent(shape, parentId) {
|
setProperty(shape, prop, value) {
|
||||||
shape.parentId = parentId
|
shape[prop] = value
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
setChildIndex(shape, childIndex) {
|
|
||||||
shape.childIndex = childIndex
|
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -9,16 +9,16 @@ interface Core {
|
||||||
interface Instance extends Props, Core {}
|
interface Instance extends Props, Core {}
|
||||||
|
|
||||||
const defaults: Props = {
|
const defaults: Props = {
|
||||||
name: "Spot",
|
name: 'Spot',
|
||||||
}
|
}
|
||||||
|
|
||||||
const core: Core = {
|
const core: Core = {
|
||||||
id: "0",
|
id: '0',
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClassInstance<T extends object = {}> implements Instance {
|
class ClassInstance<T extends object = {}> implements Instance {
|
||||||
id = "0"
|
id = '0'
|
||||||
name = "Spot"
|
name = 'Spot'
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
props: Partial<Props> &
|
props: Partial<Props> &
|
||||||
|
@ -51,7 +51,7 @@ function getInstance<T extends object = {}>(
|
||||||
}
|
}
|
||||||
|
|
||||||
const instance = getInstance({
|
const instance = getInstance({
|
||||||
name: "Steve",
|
name: 'Steve',
|
||||||
age: 93,
|
age: 93,
|
||||||
wag(this: Instance) {
|
wag(this: Instance) {
|
||||||
return this.name
|
return this.name
|
||||||
|
@ -76,29 +76,29 @@ const getAnimal = <T extends object>(
|
||||||
): Animal & T => {
|
): Animal & T => {
|
||||||
return {
|
return {
|
||||||
// Defaults
|
// Defaults
|
||||||
name: "Animal",
|
name: 'Animal',
|
||||||
greet(name) {
|
greet(name) {
|
||||||
return "Hey " + name
|
return 'Hey ' + name
|
||||||
},
|
},
|
||||||
// Overrides
|
// Overrides
|
||||||
...props,
|
...props,
|
||||||
// Core
|
// Core
|
||||||
id: "hi",
|
id: 'hi',
|
||||||
sleep() {},
|
sleep() {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dog = getAnimal({
|
const dog = getAnimal({
|
||||||
name: "doggo",
|
name: 'doggo',
|
||||||
greet(name) {
|
greet(name) {
|
||||||
return "Woof " + this.name
|
return 'Woof ' + this.name
|
||||||
},
|
},
|
||||||
wag() {
|
wag() {
|
||||||
return "wagging..."
|
return 'wagging...'
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
dog.greet("steve")
|
dog.greet('steve')
|
||||||
dog.wag()
|
dog.wag()
|
||||||
dog.sleep()
|
dog.sleep()
|
||||||
|
|
||||||
|
@ -111,5 +111,5 @@ export default shapeTest
|
||||||
type Greet = (name: string) => string
|
type Greet = (name: string) => string
|
||||||
|
|
||||||
const greet: Greet = (name: string | number) => {
|
const greet: Greet = (name: string | number) => {
|
||||||
return "hello " + name
|
return 'hello ' + name
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,20 @@
|
||||||
import Command from "./command"
|
import Command from './command'
|
||||||
import history from "../history"
|
import history from '../history'
|
||||||
import { Data } from "types"
|
import { Data, DrawShape } from 'types'
|
||||||
import { getPage } from "utils/utils"
|
import { getPage } from 'utils/utils'
|
||||||
import { getShapeUtils } from "lib/shape-utils"
|
import { getShapeUtils } from 'lib/shape-utils'
|
||||||
import { current } from "immer"
|
import { current } from 'immer'
|
||||||
|
|
||||||
export default function drawCommand(
|
export default function drawCommand(data: Data, id: string, after: number[][]) {
|
||||||
data: Data,
|
const restoreShape = current(getPage(data)).shapes[id] as DrawShape
|
||||||
id: string,
|
|
||||||
before: number[][],
|
getShapeUtils(restoreShape).setProperty!(restoreShape, 'points', after)
|
||||||
after: number[][]
|
|
||||||
) {
|
|
||||||
const restoreShape = current(getPage(data).shapes[id])
|
|
||||||
getShapeUtils(restoreShape).setPoints!(restoreShape, after)
|
|
||||||
|
|
||||||
history.execute(
|
history.execute(
|
||||||
data,
|
data,
|
||||||
new Command({
|
new Command({
|
||||||
name: "set_points",
|
name: 'set_points',
|
||||||
category: "canvas",
|
category: 'canvas',
|
||||||
manualSelection: true,
|
manualSelection: true,
|
||||||
do(data, initial) {
|
do(data, initial) {
|
||||||
if (!initial) {
|
if (!initial) {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import transform from './transform'
|
||||||
import transformSingle from './transform-single'
|
import transformSingle from './transform-single'
|
||||||
import translate from './translate'
|
import translate from './translate'
|
||||||
import nudge from './nudge'
|
import nudge from './nudge'
|
||||||
|
import toggle from './toggle'
|
||||||
|
|
||||||
const commands = {
|
const commands = {
|
||||||
align,
|
align,
|
||||||
|
@ -30,6 +31,7 @@ const commands = {
|
||||||
transformSingle,
|
transformSingle,
|
||||||
translate,
|
translate,
|
||||||
nudge,
|
nudge,
|
||||||
|
toggle,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default commands
|
export default commands
|
||||||
|
|
46
state/commands/toggle.ts
Normal file
46
state/commands/toggle.ts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import Command from './command'
|
||||||
|
import history from '../history'
|
||||||
|
import { Data, Shape } from 'types'
|
||||||
|
import { getPage, getSelectedShapes } from 'utils/utils'
|
||||||
|
import { getShapeUtils } from 'lib/shape-utils'
|
||||||
|
import { PropsOfType } from 'types'
|
||||||
|
|
||||||
|
export default function toggleCommand(
|
||||||
|
data: Data,
|
||||||
|
prop: PropsOfType<Shape, boolean>
|
||||||
|
) {
|
||||||
|
const { currentPageId } = data
|
||||||
|
const selectedShapes = getSelectedShapes(data)
|
||||||
|
const isAllToggled = selectedShapes.every((shape) => shape[prop])
|
||||||
|
const initialShapes = Object.fromEntries(
|
||||||
|
selectedShapes.map((shape) => [shape.id, shape[prop]])
|
||||||
|
)
|
||||||
|
|
||||||
|
history.execute(
|
||||||
|
data,
|
||||||
|
new Command({
|
||||||
|
name: 'hide_shapes',
|
||||||
|
category: 'canvas',
|
||||||
|
do(data) {
|
||||||
|
const { shapes } = getPage(data, currentPageId)
|
||||||
|
|
||||||
|
for (const id in initialShapes) {
|
||||||
|
const shape = shapes[id]
|
||||||
|
getShapeUtils(shape).setProperty(
|
||||||
|
shape,
|
||||||
|
prop,
|
||||||
|
isAllToggled ? false : true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
undo(data) {
|
||||||
|
const { shapes } = getPage(data, currentPageId)
|
||||||
|
|
||||||
|
for (const id in initialShapes) {
|
||||||
|
const shape = shapes[id]
|
||||||
|
getShapeUtils(shape).setProperty(shape, prop, initialShapes[id])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
|
@ -75,6 +75,12 @@ const state = createState({
|
||||||
PANNED_CAMERA: {
|
PANNED_CAMERA: {
|
||||||
do: 'panCamera',
|
do: 'panCamera',
|
||||||
},
|
},
|
||||||
|
TOGGLED_SHAPE_LOCK: { if: 'hasSelection', do: 'lockSelection' },
|
||||||
|
TOGGLED_SHAPE_HIDE: { if: 'hasSelection', do: 'hideSelection' },
|
||||||
|
TOGGLED_SHAPE_ASPECT_LOCK: {
|
||||||
|
if: 'hasSelection',
|
||||||
|
do: 'aspectLockSelection',
|
||||||
|
},
|
||||||
SELECTED_SELECT_TOOL: { to: 'selecting' },
|
SELECTED_SELECT_TOOL: { to: 'selecting' },
|
||||||
SELECTED_DRAW_TOOL: { unless: 'isReadOnly', to: 'draw' },
|
SELECTED_DRAW_TOOL: { unless: 'isReadOnly', to: 'draw' },
|
||||||
SELECTED_DOT_TOOL: { unless: 'isReadOnly', to: 'dot' },
|
SELECTED_DOT_TOOL: { unless: 'isReadOnly', to: 'dot' },
|
||||||
|
@ -638,7 +644,7 @@ const state = createState({
|
||||||
? siblings[siblings.length - 1].childIndex + 1
|
? siblings[siblings.length - 1].childIndex + 1
|
||||||
: 1
|
: 1
|
||||||
|
|
||||||
getShapeUtils(shape).setChildIndex(shape, childIndex)
|
getShapeUtils(shape).setProperty(shape, 'childIndex', childIndex)
|
||||||
|
|
||||||
getPage(data).shapes[shape.id] = shape
|
getPage(data).shapes[shape.id] = shape
|
||||||
|
|
||||||
|
@ -834,6 +840,15 @@ const state = createState({
|
||||||
duplicateSelection(data) {
|
duplicateSelection(data) {
|
||||||
commands.duplicate(data)
|
commands.duplicate(data)
|
||||||
},
|
},
|
||||||
|
lockSelection(data) {
|
||||||
|
commands.toggle(data, 'isLocked')
|
||||||
|
},
|
||||||
|
hideSelection(data) {
|
||||||
|
commands.toggle(data, 'isHidden')
|
||||||
|
},
|
||||||
|
aspectLockSelection(data) {
|
||||||
|
commands.toggle(data, 'isAspectRatioLocked')
|
||||||
|
},
|
||||||
|
|
||||||
/* --------------------- Camera --------------------- */
|
/* --------------------- Camera --------------------- */
|
||||||
|
|
||||||
|
|
7
types.ts
7
types.ts
|
@ -78,6 +78,9 @@ export interface BaseShape {
|
||||||
point: number[]
|
point: number[]
|
||||||
rotation: number
|
rotation: number
|
||||||
style: ShapeStyles
|
style: ShapeStyles
|
||||||
|
isLocked: boolean
|
||||||
|
isHidden: boolean
|
||||||
|
isAspectRatioLocked: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DotShape extends BaseShape {
|
export interface DotShape extends BaseShape {
|
||||||
|
@ -313,3 +316,7 @@ export type CodeControl =
|
||||||
| VectorCodeControl
|
| VectorCodeControl
|
||||||
| TextCodeControl
|
| TextCodeControl
|
||||||
| SelectCodeControl
|
| SelectCodeControl
|
||||||
|
|
||||||
|
export type PropsOfType<T extends object, K> = {
|
||||||
|
[K in keyof T]: T[K] extends boolean ? K : never
|
||||||
|
}[keyof T]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue