Improves layout, code panel, tweaks draw

This commit is contained in:
Steve Ruiz 2021-06-03 17:13:23 +01:00
parent 062f446339
commit 475d04e3d0
23 changed files with 308 additions and 182 deletions

View file

@ -11,7 +11,7 @@ import Bounds from './bounds/bounding-box'
import BoundsBg from './bounds/bounds-bg'
import Selected from './selected'
import Handles from './bounds/handles'
import { isMobile } from 'utils/utils'
import { isMobile, throttle } from 'utils/utils'
export default function Canvas() {
const rCanvas = useRef<SVGSVGElement>(null)
@ -34,25 +34,13 @@ export default function Canvas() {
} else {
if (isMobile()) {
state.send('TOUCHED_CANVAS')
// state.send('POINTED_CANVAS', inputs.touchStart(e, 'canvas'))
// e.preventDefault()
// e.stopPropagation()
}
}
}, [])
// const handleTouchMove = useCallback((e: React.TouchEvent) => {
// if (!inputs.canAccept(e.touches[0].identifier)) return
// if (inputs.canAccept(e.touches[0].identifier)) {
// state.send('MOVED_POINTER', inputs.touchMove(e))
// }
// }, [])
const handlePointerMove = useCallback((e: React.PointerEvent) => {
if (!inputs.canAccept(e.pointerId)) return
if (inputs.canAccept(e.pointerId)) {
state.send('MOVED_POINTER', inputs.pointerMove(e))
}
throttledPointerMove(inputs.pointerMove(e))
}, [])
const handlePointerUp = useCallback((e: React.PointerEvent) => {
@ -94,8 +82,17 @@ const MainSVG = styled('svg', {
touchAction: 'none',
zIndex: 100,
backgroundColor: '$canvas',
pointerEvents: 'all',
'& *': {
userSelect: 'none',
},
})
// const throttledPointerMove = throttle((payload: any) => {
// state.send('MOVED_POINTER', payload)
// }, 16)
const throttledPointerMove = (payload: any) => {
state.send('MOVED_POINTER', payload)
}

View file

@ -208,6 +208,11 @@ const EditorContainer = styled('div', {
pointerEvents: 'all',
userSelect: 'all',
'& > *': {
userSelect: 'all',
pointerEvents: 'all',
},
'.editorLineError': {
backgroundColor: '$lineError',
},

View file

@ -115,11 +115,18 @@ export default function CodePanel() {
const { error } = local.data
return (
<Panel.Root data-bp-desktop ref={rContainer} isOpen={isOpen}>
<Panel.Root
bp={{ '@initial': 'mobile', '@sm': 'small' }}
data-bp-desktop
ref={rContainer}
isOpen={isOpen}
variant="code"
>
{isOpen ? (
<Panel.Layout>
<Panel.Header side="left">
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size="small"
onClick={() => state.send('TOGGLED_CODE_PANEL_OPEN')}
>
@ -129,6 +136,7 @@ export default function CodePanel() {
<ButtonsGroup>
<FontSizeButtons>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size="small"
disabled={!local.isIn('editingCode')}
onClick={() => state.send('INCREASED_CODE_FONT_SIZE')}
@ -144,12 +152,14 @@ export default function CodePanel() {
</IconButton>
</FontSizeButtons>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size="small"
onClick={() => local.send('TOGGLED_DOCS')}
>
<Info />
</IconButton>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size="small"
disabled={!local.isIn('editingCode')}
onClick={() => local.send('SAVED_CODE')}
@ -179,6 +189,7 @@ export default function CodePanel() {
</Panel.Layout>
) : (
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size="small"
onClick={() => state.send('TOGGLED_CODE_PANEL_OPEN')}
>

View file

@ -22,6 +22,7 @@ export default function ControlPanel() {
<Panel.Layout>
<Panel.Header>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size="small"
onClick={() => state.send('CLOSED_CODE_PANEL')}
>
@ -37,6 +38,7 @@ export default function ControlPanel() {
</Panel.Layout>
) : (
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size="small"
onClick={() => state.send('OPENED_CODE_PANEL')}
>

View file

@ -20,21 +20,26 @@ export default function Editor() {
return (
<Layout>
<Canvas />
<CodePanel />
<PagePanel />
<LeftPanels>
<Spacer />
<StylePanel />
<Canvas />
{/* <LeftPanels>
<CodePanel />
{hasControls && <ControlsPanel />}
</LeftPanels>
<RightPanels>
<StylePanel />
</RightPanels>
</LeftPanels> */}
<ToolsPanel />
<StatusBar />
</Layout>
)
}
const Spacer = styled('div', {
flexGrow: 2,
})
const Layout = styled('main', {
position: 'fixed',
top: 0,
@ -43,34 +48,13 @@ const Layout = styled('main', {
right: 0,
height: '100%',
width: '100%',
display: 'grid',
gridTemplateRows: '1fr auto 144px',
gridTemplateColumns: 'minmax(0, 720px) 1fr auto',
gridTemplateAreas: `
"leftPanels main rightPanels"
"tools tools tools"
"statusbar statusbar statusbar"
`,
})
const LeftPanels = styled('div', {
display: 'grid',
gridArea: 'leftPanels',
gridTemplateRows: '1fr auto',
padding: 8,
gap: 8,
zIndex: 250,
pointerEvents: 'none',
})
const RightPanels = styled('div', {
gridArea: 'rightPanels',
padding: 8,
display: 'grid',
gridTemplateRows: 'auto',
height: 'fit-content',
justifyContent: 'flex-end',
gap: 8,
zIndex: 300,
padding: '8px 8px 0 8px',
zIndex: 200,
display: 'flex',
alignItems: 'flex-start',
justifyContent: 'flex-start',
pointerEvents: 'none',
'& > *': {
PointerEvent: 'all',
},
})

View file

@ -29,89 +29,89 @@ export default function PagePanel() {
)
return (
<OuterContainer>
<DropdownMenu.Root
open={isOpen}
onOpenChange={(isOpen) => {
if (rIsOpen.current !== isOpen) {
setIsOpen(isOpen)
}
}}
>
<PanelRoot>
<DropdownMenu.Trigger as={RowButton}>
<span>{documentPages[currentPageId].name}</span>
<IconWrapper size="small">
<ChevronDownIcon />
</IconWrapper>
</DropdownMenu.Trigger>
<DropdownMenu.Content sideOffset={8}>
<PanelRoot>
<DropdownMenu.RadioGroup
as={Content}
value={currentPageId}
onValueChange={(id) => {
setIsOpen(false)
state.send('CHANGED_CURRENT_PAGE', { id })
}}
>
{sorted.map(({ id, name }) => (
<ContextMenu.Root key={id}>
<ContextMenu.Trigger>
<StyledRadioItem key={id} value={id}>
<span>{name}</span>
<DropdownMenu.ItemIndicator
as={IconWrapper}
size="small"
>
<CheckIcon />
</DropdownMenu.ItemIndicator>
</StyledRadioItem>
</ContextMenu.Trigger>
<StyledContextMenuContent>
<ContextMenu.Group>
<StyledContextMenuItem
onSelect={() => state.send('RENAMED_PAGE', { id })}
>
Rename
</StyledContextMenuItem>
<StyledContextMenuItem
onSelect={() => {
setIsOpen(false)
state.send('DELETED_PAGE', { id })
}}
>
Delete
</StyledContextMenuItem>
</ContextMenu.Group>
</StyledContextMenuContent>
</ContextMenu.Root>
))}
</DropdownMenu.RadioGroup>
<DropdownMenu.Separator />
<RowButton
onClick={() => {
setIsOpen(false)
state.send('CREATED_PAGE')
}}
>
<span>Create Page</span>
<IconWrapper size="small">
<PlusIcon />
</IconWrapper>
</RowButton>
</PanelRoot>
</DropdownMenu.Content>
</PanelRoot>
</DropdownMenu.Root>
</OuterContainer>
<DropdownMenu.Root
open={isOpen}
onOpenChange={(isOpen) => {
if (rIsOpen.current !== isOpen) {
setIsOpen(isOpen)
}
}}
>
<PanelRoot>
<DropdownMenu.Trigger
as={RowButton}
bp={{ '@initial': 'mobile', '@sm': 'small' }}
css={{ paddingRight: 12 }}
>
<span>{documentPages[currentPageId].name}</span>
</DropdownMenu.Trigger>
<DropdownMenu.Content sideOffset={8}>
<PanelRoot>
<DropdownMenu.RadioGroup
as={Content}
value={currentPageId}
onValueChange={(id) => {
setIsOpen(false)
state.send('CHANGED_CURRENT_PAGE', { id })
}}
>
{sorted.map(({ id, name }) => (
<ContextMenu.Root key={id}>
<ContextMenu.Trigger>
<StyledRadioItem
key={id}
value={id}
bp={{ '@initial': 'mobile', '@sm': 'small' }}
>
<span>{name}</span>
<DropdownMenu.ItemIndicator as={IconWrapper} size="small">
<CheckIcon />
</DropdownMenu.ItemIndicator>
</StyledRadioItem>
</ContextMenu.Trigger>
<StyledContextMenuContent>
<ContextMenu.Group>
<StyledContextMenuItem
onSelect={() => state.send('RENAMED_PAGE', { id })}
>
Rename
</StyledContextMenuItem>
<StyledContextMenuItem
onSelect={() => {
setIsOpen(false)
state.send('DELETED_PAGE', { id })
}}
>
Delete
</StyledContextMenuItem>
</ContextMenu.Group>
</StyledContextMenuContent>
</ContextMenu.Root>
))}
</DropdownMenu.RadioGroup>
<DropdownMenu.Separator />
<RowButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
onClick={() => {
setIsOpen(false)
state.send('CREATED_PAGE')
}}
>
<span>Create Page</span>
<IconWrapper size="small">
<PlusIcon />
</IconWrapper>
</RowButton>
</PanelRoot>
</DropdownMenu.Content>
</PanelRoot>
</DropdownMenu.Root>
)
}
const PanelRoot = styled('div', {
minWidth: 1,
width: 184,
maxWidth: 184,
marginLeft: 8,
zIndex: 200,
overflow: 'hidden',
position: 'relative',
display: 'flex',
@ -128,11 +128,12 @@ const PanelRoot = styled('div', {
const Content = styled(Panel.Content, {
width: '100%',
minWidth: 128,
})
const StyledRadioItem = styled(DropdownMenu.RadioItem, {
height: 32,
width: '100%',
width: 'auto',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
@ -143,24 +144,19 @@ const StyledRadioItem = styled(DropdownMenu.RadioItem, {
fontFamily: '$ui',
backgroundColor: 'transparent',
outline: 'none',
'&:hover': {
backgroundColor: '$hover',
variants: {
bp: {
mobile: {},
small: {
'&:hover': {
backgroundColor: '$hover',
},
'&:focus-within': {
backgroundColor: '$hover',
},
},
},
},
'&:focus-within': {
backgroundColor: '$hover',
},
})
const OuterContainer = styled('div', {
position: 'fixed',
top: 8,
left: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '100%',
zIndex: 200,
height: 44,
})
const StyledContextMenuContent = styled(ContextMenu.Content, {
@ -184,8 +180,13 @@ const StyledContextMenuItem = styled(ContextMenu.Item, {
fontFamily: '$ui',
backgroundColor: 'transparent',
outline: 'none',
'&:hover': {
backgroundColor: '$hover',
bp: {
mobile: {},
small: {
'&:hover:not(:disabled)': {
backgroundColor: '$hover',
},
},
},
})

View file

@ -12,6 +12,13 @@ export const Root = styled('div', {
boxShadow: '0px 2px 4px rgba(0,0,0,.2)',
variants: {
bp: {
mobile: {},
small: {},
},
variant: {
code: {},
},
isOpen: {
true: {},
false: {
@ -21,6 +28,35 @@ export const Root = styled('div', {
},
},
},
compoundVariants: [
{
isOpen: true,
variant: 'code',
css: {
position: 'absolute',
top: 8,
left: 8,
right: 8,
bottom: 48,
maxWidth: 720,
zIndex: 1000,
},
},
{
isOpen: true,
variant: 'code',
bp: 'small',
css: {
position: 'absolute',
top: 8,
left: 8,
right: 8,
bottom: 128,
maxWidth: 720,
zIndex: 1000,
},
},
],
})
export const Layout = styled('div', {

View file

@ -23,15 +23,19 @@ export const IconButton = styled('button', {
gridColumn: 1,
},
'&:hover:not(:disabled)': {
backgroundColor: '$hover',
},
'&:disabled': {
opacity: '0.5',
},
variants: {
bp: {
mobile: {},
small: {
'&:hover:not(:disabled)': {
backgroundColor: '$hover',
},
},
},
size: {
small: {
'& svg': {
@ -80,10 +84,6 @@ export const RowButton = styled('button', {
padding: '4px 6px 4px 12px',
borderRadius: 4,
'&:hover': {
backgroundColor: '$hover',
},
'& label': {
fontWeight: '$1',
margin: 0,
@ -98,6 +98,14 @@ export const RowButton = styled('button', {
},
variants: {
bp: {
mobile: {},
small: {
'&:hover:not(:disabled)': {
backgroundColor: '$hover',
},
},
},
size: {
icon: {
padding: '4px ',

View file

@ -32,6 +32,7 @@ const StatusBarContainer = styled('div', {
bottom: 0,
left: 0,
width: '100%',
zIndex: 300,
height: 40,
userSelect: 'none',
borderTop: '1px solid black',
@ -43,7 +44,6 @@ const StatusBarContainer = styled('div', {
gap: 8,
fontSize: '$0',
padding: '0 16px',
zIndex: 200,
variants: {
size: {

View file

@ -64,20 +64,32 @@ export default function AlignDistribute({
}) {
return (
<Container>
<IconButton size="small" disabled={!hasTwoOrMore} onClick={alignLeft}>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size="small"
disabled={!hasTwoOrMore}
onClick={alignLeft}
>
<AlignLeftIcon />
</IconButton>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size="small"
disabled={!hasTwoOrMore}
onClick={alignCenterHorizontal}
>
<AlignCenterHorizontallyIcon />
</IconButton>
<IconButton size="small" disabled={!hasTwoOrMore} onClick={alignRight}>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size="small"
disabled={!hasTwoOrMore}
onClick={alignRight}
>
<AlignRightIcon />
</IconButton>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size="small"
disabled={!hasTwoOrMore}
onClick={stretchHorizontally}
@ -85,26 +97,39 @@ export default function AlignDistribute({
<StretchHorizontallyIcon />
</IconButton>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size="small"
disabled={!hasThreeOrMore}
onClick={distributeHorizontally}
>
<SpaceEvenlyHorizontallyIcon />
</IconButton>
<IconButton size="small" disabled={!hasTwoOrMore} onClick={alignTop}>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size="small"
disabled={!hasTwoOrMore}
onClick={alignTop}
>
<AlignTopIcon />
</IconButton>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size="small"
disabled={!hasTwoOrMore}
onClick={alignCenterVertical}
>
<AlignCenterVerticallyIcon />
</IconButton>
<IconButton size="small" disabled={!hasTwoOrMore} onClick={alignBottom}>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size="small"
disabled={!hasTwoOrMore}
onClick={alignBottom}
>
<AlignBottomIcon />
</IconButton>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size="small"
disabled={!hasTwoOrMore}
onClick={stretchVertically}
@ -112,6 +137,7 @@ export default function AlignDistribute({
<StretchVerticallyIcon />
</IconButton>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size="small"
disabled={!hasThreeOrMore}
onClick={distributeVertically}

View file

@ -12,7 +12,7 @@ export default function ColorContent({
onChange: (color: ColorStyle) => void
}) {
return (
<DropdownContent sideOffset={0} side="bottom">
<DropdownContent sideOffset={8} side="bottom">
{Object.keys(strokes).map((color: ColorStyle) => (
<DropdownMenu.DropdownMenuItem
as={IconButton}

View file

@ -13,7 +13,10 @@ interface Props {
export default function ColorPicker({ color, onChange }: Props) {
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger as={RowButton}>
<DropdownMenu.Trigger
as={RowButton}
bp={{ '@initial': 'mobile', '@sm': 'small' }}
>
<label htmlFor="color">Color</label>
<IconWrapper>
<Square fill={strokes[color]} />

View file

@ -13,6 +13,7 @@ export default function IsFilledPicker({ isFilled, onChange }: Props) {
return (
<Checkbox.Root
as={RowButton}
bp={{ '@initial': 'mobile', '@sm': 'small' }}
checked={isFilled}
onCheckedChange={(e: React.ChangeEvent<HTMLInputElement>) =>
onChange(e.currentTarget.checked)

View file

@ -11,7 +11,10 @@ export default function QuickColorSelect() {
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger as={IconButton}>
<DropdownMenu.Trigger
as={IconButton}
bp={{ '@initial': 'mobile', '@sm': 'small' }}
>
<Tooltip label="Color">
<Square fill={strokes[color]} stroke={strokes[color]} />
</Tooltip>

View file

@ -22,10 +22,13 @@ export default function QuickdashSelect() {
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger as={IconButton}>
<DropdownMenu.Trigger
as={IconButton}
bp={{ '@initial': 'mobile', '@sm': 'small' }}
>
<Tooltip label="Dash">{dashes[dash]}</Tooltip>
</DropdownMenu.Trigger>
<DropdownContent direction="vertical">
<DropdownContent sideOffset={8} direction="vertical">
<DashItem isActive={dash === DashStyle.Solid} dash={DashStyle.Solid} />
<DashItem
isActive={dash === DashStyle.Dashed}

View file

@ -17,12 +17,15 @@ export default function QuickSizeSelect() {
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger as={IconButton}>
<DropdownMenu.Trigger
as={IconButton}
bp={{ '@initial': 'mobile', '@sm': 'small' }}
>
<Tooltip label="Size">
<Circle size={sizes[size]} stroke="none" fill="currentColor" />
</Tooltip>
</DropdownMenu.Trigger>
<DropdownContent direction="vertical">
<DropdownContent sideOffset={8} direction="vertical">
<SizeItem isActive={size === SizeStyle.Small} size={SizeStyle.Small} />
<SizeItem
isActive={size === SizeStyle.Medium}

View file

@ -44,6 +44,7 @@ export default function StylePanel() {
<QuickSizeSelect />
<QuickdashSelect />
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
title="Style"
size="small"
onClick={() => state.send('TOGGLED_STYLE_PANEL_OPEN')}
@ -92,6 +93,7 @@ function SelectedShapeStyles() {
<Panel.Header side="right">
<h3>Style</h3>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size="small"
onClick={() => state.send('TOGGLED_STYLE_PANEL_OPEN')}
>
@ -117,6 +119,7 @@ function SelectedShapeStyles() {
</Row>
<ButtonsRow>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
disabled={!hasSelection}
size="small"
onClick={() => state.send('DUPLICATED')}
@ -137,6 +140,7 @@ function SelectedShapeStyles() {
</IconButton>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
disabled={!hasSelection}
size="small"
onClick={() => state.send('TOGGLED_SHAPE_HIDE')}
@ -147,6 +151,7 @@ function SelectedShapeStyles() {
</IconButton>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
disabled={!hasSelection}
size="small"
onClick={() => state.send('TOGGLED_SHAPE_LOCK')}
@ -157,6 +162,7 @@ function SelectedShapeStyles() {
</IconButton>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
disabled={!hasSelection}
size="small"
onClick={() => state.send('TOGGLED_SHAPE_ASPECT_LOCK')}
@ -168,6 +174,7 @@ function SelectedShapeStyles() {
</ButtonsRow>
<ButtonsRow>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
disabled={!hasSelection}
size="small"
onClick={() => state.send('MOVED', { type: MoveType.ToBack })}
@ -178,6 +185,7 @@ function SelectedShapeStyles() {
</IconButton>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
disabled={!hasSelection}
size="small"
onClick={() => state.send('MOVED', { type: MoveType.Backward })}
@ -188,6 +196,7 @@ function SelectedShapeStyles() {
</IconButton>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
disabled={!hasSelection}
size="small"
onClick={() => state.send('MOVED', { type: MoveType.Forward })}
@ -198,6 +207,7 @@ function SelectedShapeStyles() {
</IconButton>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
disabled={!hasSelection}
size="small"
onClick={() => state.send('MOVED', { type: MoveType.ToFront })}
@ -208,6 +218,7 @@ function SelectedShapeStyles() {
</IconButton>
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
disabled={!hasSelection}
size="small"
onClick={() => state.send('DELETED')}

View file

@ -10,6 +10,7 @@ import {
Pencil2Icon,
SewingPinIcon,
SquareIcon,
TextIcon,
} from '@radix-ui/react-icons'
import { IconButton } from 'components/shared'
import React from 'react'
@ -27,6 +28,7 @@ const selectDrawTool = () => state.send('SELECTED_DRAW_TOOL')
const selectEllipseTool = () => state.send('SELECTED_ELLIPSE_TOOL')
const selectLineTool = () => state.send('SELECTED_LINE_TOOL')
const selectPolylineTool = () => state.send('SELECTED_POLYLINE_TOOL')
const selectTextTool = () => state.send('SELECTED_TEXT_TOOL')
const selectRayTool = () => state.send('SELECTED_RAY_TOOL')
const selectRectangleTool = () => state.send('SELECTED_RECTANGLE_TOOL')
const selectSelectTool = () => state.send('SELECTED_SELECT_TOOL')
@ -47,6 +49,7 @@ export default function ToolsPanel() {
<Tooltip label="Select">
<IconButton
name="select"
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size={{ '@initial': 'small', '@sm': 'small', '@md': 'large' }}
onClick={selectSelectTool}
isActive={activeTool === 'select'}
@ -59,6 +62,7 @@ export default function ToolsPanel() {
<Tooltip label="Draw">
<IconButton
name={ShapeType.Draw}
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size={{ '@initial': 'medium', '@sm': 'small', '@md': 'large' }}
onClick={selectDrawTool}
isActive={activeTool === ShapeType.Draw}
@ -69,6 +73,7 @@ export default function ToolsPanel() {
<Tooltip label="Rectangle">
<IconButton
name={ShapeType.Rectangle}
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size={{ '@initial': 'medium', '@sm': 'small', '@md': 'large' }}
onClick={selectRectangleTool}
isActive={activeTool === ShapeType.Rectangle}
@ -79,6 +84,7 @@ export default function ToolsPanel() {
<Tooltip label="Ellipse">
<IconButton
name={ShapeType.Circle}
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size={{ '@initial': 'medium', '@sm': 'small', '@md': 'large' }}
onClick={selectEllipseTool}
isActive={activeTool === ShapeType.Ellipse}
@ -89,6 +95,7 @@ export default function ToolsPanel() {
<Tooltip label="Arrow">
<IconButton
name={ShapeType.Arrow}
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size={{ '@initial': 'medium', '@sm': 'small', '@md': 'large' }}
onClick={selectArrowTool}
isActive={activeTool === ShapeType.Arrow}
@ -96,6 +103,17 @@ export default function ToolsPanel() {
<ArrowTopRightIcon />
</IconButton>
</Tooltip>
<Tooltip label="Text">
<IconButton
name={ShapeType.Arrow}
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size={{ '@initial': 'medium', '@sm': 'small', '@md': 'large' }}
onClick={selectTextTool}
isActive={activeTool === ShapeType.Text}
>
<TextIcon />
</IconButton>
</Tooltip>
{/* <IconButton
name={ShapeType.Circle}
size={{ '@initial': 'medium', '@sm': 'small', '@md': 'large' }}
@ -132,6 +150,7 @@ export default function ToolsPanel() {
<Container>
<Tooltip label="Lock Tool">
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size={{ '@initial': 'small', '@sm': 'small', '@md': 'large' }}
onClick={selectToolLock}
>
@ -141,6 +160,7 @@ export default function ToolsPanel() {
{isPenLocked && (
<Tooltip label="Unlock Pen">
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size={{ '@initial': 'small', '@sm': 'small', '@md': 'large' }}
onClick={selectToolLock}
>

View file

@ -164,10 +164,10 @@ function renderPath(shape: DrawShape, style: ShapeStyles) {
shape.points,
getSvgPathFromStroke(
getStroke(shape.points, {
size: +styles.strokeWidth * 2,
thinning: 0.9,
end: { taper: 100 },
start: { taper: 40 },
size: 1 + +styles.strokeWidth * 2,
thinning: 0.83,
end: { taper: +styles.strokeWidth * 16 },
start: { taper: +styles.strokeWidth * 16 },
})
)
)

View file

@ -1,6 +1,6 @@
import { AppProps } from "next/app"
import { globalStyles } from "styles"
import "styles/globals.css"
import { AppProps } from 'next/app'
import { globalStyles } from 'styles'
import 'styles/globals.css'
function MyApp({ Component, pageProps }: AppProps) {
globalStyles()

View file

@ -1,5 +1,5 @@
import NextDocument, { Html, Head, Main, NextScript } from "next/document"
import { dark, getCssString } from "styles"
import NextDocument, { Html, Head, Main, NextScript } from 'next/document'
import { dark, getCssString } from 'styles'
class MyDocument extends NextDocument {
static async getInitialProps(ctx) {

View file

@ -1,11 +1,15 @@
// import Editor from "components/editor"
import dynamic from "next/dynamic"
const Editor = dynamic(() => import("components/editor"), { ssr: false })
import Head from 'next/head'
import dynamic from 'next/dynamic'
const Editor = dynamic(() => import('components/editor'), { ssr: false })
export default function Home() {
return (
<div>
<>
<Head>
<title>tldraw</title>
</Head>
<Editor />
</div>
</>
)
}

View file

@ -65,6 +65,7 @@ export enum ShapeType {
Rectangle = 'rectangle',
Draw = 'draw',
Arrow = 'arrow',
Text = 'text',
}
// Consider:
@ -183,6 +184,11 @@ export interface ArrowShape extends BaseShape {
}
}
export interface TextShape extends BaseShape {
type: ShapeType.Text
text: string
}
export type MutableShape =
| DotShape
| CircleShape
@ -193,6 +199,7 @@ export type MutableShape =
| DrawShape
| RectangleShape
| ArrowShape
| TextShape
export type Shape = Readonly<MutableShape>
@ -206,6 +213,7 @@ export interface Shapes {
[ShapeType.Draw]: Readonly<DrawShape>
[ShapeType.Rectangle]: Readonly<RectangleShape>
[ShapeType.Arrow]: Readonly<ArrowShape>
[ShapeType.Text]: Readonly<TextShape>
}
export type ShapeByType<T extends ShapeType> = Shapes[T]