Refactors tool panel

This commit is contained in:
Steve Ruiz 2021-07-10 13:01:59 +01:00
parent f9c688056e
commit 4efa1092f1
19 changed files with 580 additions and 261 deletions

View file

@ -0,0 +1,273 @@
import Tooltip from 'components/tooltip'
import styled from 'styles'
// small: {
// height: 32,
// width: 32,
// '& svg:nth-of-type(1)': {
// height: '16px',
// width: '16px',
// },
// },
// medium: {
// height: 44,
// width: 44,
// '& svg:nth-of-type(1)': {
// height: '18px',
// width: '18px',
// },
// },
// large: {
// height: 44,
// width: 44,
// '& svg:nth-of-type(1)': {
// height: '20px',
// width: '20px',
// },
// },
// tertiary: {
// height: 32,
// width: 44,
// '& svg:nth-of-type(1)': {
// height: '16px',
// width: '16px',
// },
// },
export const ToolButton = styled('button', {
position: 'relative',
height: '32px',
width: '32px',
backgroundColor: '$panel',
borderRadius: '4px',
padding: '0',
margin: '0',
display: 'grid',
alignItems: 'center',
justifyContent: 'center',
outline: 'none',
border: 'none',
pointerEvents: 'all',
fontSize: '$0',
cursor: 'pointer',
'& > *': {
gridRow: 1,
gridColumn: 1,
},
'&:disabled': {
opacity: '0.5',
},
'& > span': {
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
},
})
export const PrimaryToolButton = styled(ToolButton, {
variants: {
bp: {
mobile: {
height: 44,
width: 44,
'& svg:nth-of-type(1)': {
height: '20px',
width: '20px',
},
},
small: {
'&:hover:not(:disabled)': {
backgroundColor: '$hover',
},
},
medium: {},
large: {},
},
isActive: {
true: {
color: '$selected',
},
},
},
})
export const SecondaryToolButton = styled(ToolButton, {
variants: {
bp: {
mobile: {
height: 44,
width: 44,
'& svg:nth-of-type(1)': {
height: '18px',
width: '18px',
},
},
small: {
'&:hover:not(:disabled)': {
backgroundColor: '$hover',
},
},
medium: {},
large: {},
},
isActive: {
true: {
color: '$selected',
},
},
},
})
export const TertiaryToolButton = styled(ToolButton, {
variants: {
bp: {
mobile: {
height: 32,
width: 44,
'& svg:nth-of-type(1)': {
height: '16px',
width: '16px',
},
},
small: {
height: 40,
width: 40,
'& svg:nth-of-type(1)': {
height: '18px',
width: '18px',
},
'&:hover:not(:disabled)': {
backgroundColor: '$hover',
},
},
medium: {},
large: {},
},
},
})
interface PrimaryToolButtonProps {
label: string
onClick: () => void
onDoubleClick?: () => void
isActive: boolean
children: React.ReactNode
}
export function PrimaryButton({
label,
onClick,
onDoubleClick,
isActive,
children,
}: PrimaryToolButtonProps): JSX.Element {
return (
<Tooltip label={label}>
<PrimaryToolButton
name={label}
bp={{
'@initial': 'mobile',
'@sm': 'small',
'@md': 'medium',
'@lg': 'large',
}}
onClick={onClick}
onDoubleClick={onDoubleClick}
isActive={isActive}
>
{children}
</PrimaryToolButton>
</Tooltip>
)
}
interface SecondaryToolButtonProps {
label: string
onClick: () => void
onDoubleClick?: () => void
isActive: boolean
children: React.ReactNode
}
export function SecondaryButton({
label,
onClick,
onDoubleClick,
isActive,
children,
}: SecondaryToolButtonProps): JSX.Element {
return (
<Tooltip label={label}>
<SecondaryToolButton
name={label}
bp={{
'@initial': 'mobile',
'@sm': 'small',
'@md': 'medium',
'@lg': 'large',
}}
onClick={onClick}
onDoubleClick={onDoubleClick}
isActive={isActive}
>
{children}
</SecondaryToolButton>
</Tooltip>
)
}
interface TertiaryToolProps {
label: string
onClick: () => void
onDoubleClick?: () => void
children: React.ReactNode
}
export function TertiaryButton({
label,
onClick,
onDoubleClick,
children,
}: TertiaryToolProps): JSX.Element {
return (
<Tooltip label={label}>
<TertiaryToolButton
name={label}
bp={{
'@initial': 'mobile',
'@sm': 'small',
'@md': 'medium',
'@lg': 'large',
}}
onClick={onClick}
onDoubleClick={onDoubleClick}
>
{children}
</TertiaryToolButton>
</Tooltip>
)
}
export const TertiaryButtonsContainer = styled('div', {
backgroundColor: '$panel',
borderRadius: '4px',
overflow: 'hidden',
pointerEvents: 'all',
userSelect: 'none',
zIndex: 200,
minWidth: 36,
border: '1px solid $panel',
boxShadow: '0px 2px 4px rgba(0,0,0,.10)',
padding: 2,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
'& svg': {
strokeWidth: 0,
},
})

View file

@ -5,18 +5,16 @@ import {
LockClosedIcon,
LockOpen1Icon,
Pencil1Icon,
Pencil2Icon,
SquareIcon,
TextIcon,
} from '@radix-ui/react-icons'
import { IconButton } from 'components/shared'
import { PrimaryButton, SecondaryButton } from './shared'
import React from 'react'
import state, { useSelector } from 'state'
import styled from 'styles'
import { ShapeType } from 'types'
import UndoRedo from './undo-redo'
import Zoom from './zoom'
import Tooltip from '../tooltip'
const selectArrowTool = () => state.send('SELECTED_ARROW_TOOL')
const selectDrawTool = () => state.send('SELECTED_DRAW_TOOL')
@ -24,167 +22,167 @@ const selectEllipseTool = () => state.send('SELECTED_ELLIPSE_TOOL')
const selectTextTool = () => state.send('SELECTED_TEXT_TOOL')
const selectRectangleTool = () => state.send('SELECTED_RECTANGLE_TOOL')
const selectSelectTool = () => state.send('SELECTED_SELECT_TOOL')
const selectToolLock = () => state.send('TOGGLED_TOOL_LOCK')
const toggleToolLock = () => state.send('TOGGLED_TOOL_LOCK')
export default function ToolsPanel(): JSX.Element {
const activeTool = useSelector((s) => s.data.activeTool)
const isToolLocked = useSelector((s) => s.data.settings.isToolLocked)
const isPenLocked = useSelector((s) => s.data.settings.isPenLocked)
return (
<OuterContainer>
<Zoom />
<Flex size={{ '@sm': 'small' }}>
<ToolsPanelContainer>
<LeftWrap size={{ '@initial': 'mobile', '@sm': 'small' }}>
<Zoom />
<Container>
<Tooltip label="Select">
<IconButton
name="select"
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size={{ '@initial': 'small', '@sm': 'small', '@md': 'medium' }}
onClick={selectSelectTool}
isActive={activeTool === 'select'}
>
<CursorArrowIcon />
</IconButton>
</Tooltip>
<SecondaryButton
label={'Select'}
onClick={selectSelectTool}
isActive={activeTool === 'select'}
>
<CursorArrowIcon />
</SecondaryButton>
</Container>
</LeftWrap>
<CenterWrap>
<Container>
<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}
>
<Pencil1Icon />
</IconButton>
</Tooltip>
<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}
>
<SquareIcon />
</IconButton>
</Tooltip>
<Tooltip label="Ellipse">
<IconButton
name={ShapeType.Ellipse}
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size={{ '@initial': 'medium', '@sm': 'small', '@md': 'large' }}
onClick={selectEllipseTool}
isActive={activeTool === ShapeType.Ellipse}
>
<CircleIcon />
</IconButton>
</Tooltip>
<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}
>
<ArrowTopRightIcon />
</IconButton>
</Tooltip>
<Tooltip label="Text">
<IconButton
name={ShapeType.Text}
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size={{ '@initial': 'medium', '@sm': 'small', '@md': 'large' }}
onClick={selectTextTool}
isActive={activeTool === ShapeType.Text}
>
<TextIcon />
</IconButton>
</Tooltip>
<PrimaryButton
label={ShapeType.Draw}
onClick={selectDrawTool}
isActive={activeTool === ShapeType.Draw}
>
<Pencil1Icon />
</PrimaryButton>
<PrimaryButton
label={ShapeType.Rectangle}
onClick={selectRectangleTool}
isActive={activeTool === ShapeType.Rectangle}
>
<SquareIcon />
</PrimaryButton>
<PrimaryButton
label={ShapeType.Ellipse}
onClick={selectEllipseTool}
isActive={activeTool === ShapeType.Ellipse}
>
<CircleIcon />
</PrimaryButton>
<PrimaryButton
label={ShapeType.Arrow}
onClick={selectArrowTool}
isActive={activeTool === ShapeType.Arrow}
>
<ArrowTopRightIcon />
</PrimaryButton>
<PrimaryButton
label={ShapeType.Text}
onClick={selectTextTool}
isActive={activeTool === ShapeType.Text}
>
<TextIcon />
</PrimaryButton>
</Container>
</CenterWrap>
<RightWrap size={{ '@initial': 'mobile', '@sm': 'small' }}>
<Container>
<Tooltip label="Lock Tool">
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size={{ '@initial': 'small', '@sm': 'small', '@md': 'medium' }}
onClick={selectToolLock}
>
{isToolLocked ? <LockClosedIcon /> : <LockOpen1Icon />}
</IconButton>
</Tooltip>
{isPenLocked && (
<Tooltip label="Unlock Pen">
<IconButton
bp={{ '@initial': 'mobile', '@sm': 'small' }}
size={{ '@initial': 'small', '@sm': 'small', '@md': 'medium' }}
onClick={selectToolLock}
>
<Pencil2Icon />
</IconButton>
</Tooltip>
)}
<SecondaryButton
label={'Lock Tool'}
onClick={toggleToolLock}
isActive={isToolLocked}
>
{isToolLocked ? <LockClosedIcon /> : <LockOpen1Icon />}
</SecondaryButton>
</Container>
</Flex>
<UndoRedo />
</OuterContainer>
<UndoRedo />
</RightWrap>
</ToolsPanelContainer>
)
}
const OuterContainer = styled('div', {
const ToolsPanelContainer = styled('div', {
position: 'fixed',
bottom: 44,
left: 0,
right: 0,
width: '100%',
minWidth: 0,
maxWidth: '100%',
display: 'grid',
gridTemplateColumns: '1fr auto 1fr',
padding: '0 8px 12px 8px',
width: '100%',
display: 'flex',
alignItems: 'flex-end',
justifyContent: 'center',
flexWrap: 'wrap',
gap: 16,
zIndex: 200,
})
const Flex = styled('div', {
display: 'flex',
width: '100%',
padding: '0 4px',
justifyContent: 'space-between',
alignItems: 'flex-end',
variants: {
size: {
small: {
width: 'auto',
padding: '0',
justifyContent: 'center',
'& > *:nth-child(n+2)': {
marginLeft: 16,
},
},
},
},
gap: 8,
})
const Container = styled('div', {
position: 'relative',
backgroundColor: '$panel',
borderRadius: '4px',
overflow: 'hidden',
border: '1px solid $panel',
pointerEvents: 'all',
userSelect: 'none',
height: '100%',
height: 'fit-content',
display: 'flex',
padding: 4,
boxShadow: '0px 2px 4px rgba(0,0,0,.12)',
padding: 2,
boxShadow: '0px 2px 4px rgba(0,0,0,.16)',
})
'& svg': {
strokeWidth: 0,
const CenterWrap = styled('div', {
gridRow: 1,
gridColumn: 2,
display: 'flex',
width: 'fit-content',
justifyContent: 'center',
})
const LeftWrap = styled('div', {
gridRow: 1,
gridColumn: 1,
display: 'flex',
variants: {
size: {
mobile: {
flexDirection: 'column',
justifyContent: 'flex-end',
alignItems: 'flex-start',
'& > *:nth-of-type(1)': {
marginBottom: '8px',
},
},
small: {
flexDirection: 'row',
alignItems: 'flex-end',
justifyContent: 'space-between',
'& > *:nth-of-type(1)': {
marginBottom: '0px',
},
},
},
},
})
const RightWrap = styled('div', {
gridRow: 1,
gridColumn: 3,
display: 'flex',
variants: {
size: {
mobile: {
flexDirection: 'column-reverse',
justifyContent: 'flex-end',
alignItems: 'flex-end',
'& > *:nth-of-type(2)': {
marginBottom: '8px',
},
},
small: {
flexDirection: 'row',
alignItems: 'flex-end',
justifyContent: 'space-between',
'& > *:nth-of-type(2)': {
marginBottom: '0px',
},
},
},
},
})

View file

@ -1,8 +1,6 @@
import { IconButton } from 'components/shared'
import { RotateCcw, RotateCw, Trash2 } from 'react-feather'
import { TertiaryButton, TertiaryButtonsContainer } from './shared'
import { Undo, Redo, Trash } from 'components/icons'
import state from 'state'
import styled from 'styles'
import Tooltip from '../tooltip'
const undo = () => state.send('UNDO')
const redo = () => state.send('REDO')
@ -10,55 +8,16 @@ const clear = () => state.send('CLEARED_PAGE')
export default function UndoRedo(): JSX.Element {
return (
<Container size={{ '@sm': 'small' }}>
<Tooltip label="Undo">
<IconButton onClick={undo}>
<RotateCcw />
</IconButton>
</Tooltip>
<Tooltip label="Redo">
<IconButton onClick={redo}>
<RotateCw />
</IconButton>
</Tooltip>
<Tooltip label="Clear Canvas">
<IconButton onClick={clear}>
<Trash2 />
</IconButton>
</Tooltip>
</Container>
<TertiaryButtonsContainer>
<TertiaryButton label="Undo" onClick={undo}>
<Undo />
</TertiaryButton>
<TertiaryButton label="Redo" onClick={redo}>
<Redo />
</TertiaryButton>
<TertiaryButton label="Delete" onClick={clear}>
<Trash />
</TertiaryButton>
</TertiaryButtonsContainer>
)
}
const Container = styled('div', {
position: 'absolute',
bottom: 64,
right: 12,
backgroundColor: '$panel',
borderRadius: '4px',
overflow: 'hidden',
alignSelf: 'flex-end',
pointerEvents: 'all',
userSelect: 'none',
zIndex: 200,
border: '1px solid $panel',
boxShadow: '0px 2px 4px rgba(0,0,0,.12)',
display: 'flex',
padding: 4,
flexDirection: 'column',
'& svg': {
height: 13,
width: 13,
},
variants: {
size: {
small: {
bottom: 12,
flexDirection: 'row',
alignItems: 'center',
},
},
},
})

View file

@ -1,8 +1,6 @@
import { ZoomInIcon, ZoomOutIcon } from '@radix-ui/react-icons'
import { IconButton } from 'components/shared'
import { TertiaryButton, TertiaryButtonsContainer } from './shared'
import state, { useSelector } from 'state'
import styled from 'styles'
import Tooltip from '../tooltip'
import tld from 'utils/tld'
const zoomIn = () => state.send('ZOOMED_IN')
@ -12,21 +10,15 @@ const zoomToActual = () => state.send('ZOOMED_TO_ACTUAL')
export default function Zoom(): JSX.Element {
return (
<Container size={{ '@sm': 'small' }}>
<Tooltip label="Zoom Out">
<IconButton onClick={zoomOut}>
<ZoomOutIcon />
</IconButton>
</Tooltip>
<Tooltip label="Zoom In">
<IconButton onClick={zoomIn}>
<ZoomInIcon />
</IconButton>
</Tooltip>
<Tooltip label="Reset Zoom">
<ZoomCounter />
</Tooltip>
</Container>
<TertiaryButtonsContainer>
<TertiaryButton label="Zoom Out" onClick={zoomOut}>
<ZoomOutIcon />
</TertiaryButton>
<TertiaryButton label="Zoom In" onClick={zoomIn}>
<ZoomInIcon />
</TertiaryButton>
<ZoomCounter />
</TertiaryButtonsContainer>
)
}
@ -34,50 +26,12 @@ function ZoomCounter() {
const zoom = useSelector((s) => tld.getCurrentCamera(s.data).zoom)
return (
<ZoomButton
<TertiaryButton
label="Reset Zoom"
onClick={zoomToActual}
onDoubleClick={zoomToFit}
style={{ width: '44px' }}
>
{Math.round(zoom * 100)}%
</ZoomButton>
</TertiaryButton>
)
}
const ZoomButton = styled(IconButton, {
fontSize: '$0',
padding: 8,
})
const Container = styled('div', {
position: 'absolute',
left: 12,
bottom: 64,
backgroundColor: '$panel',
borderRadius: '4px',
overflow: 'hidden',
alignSelf: 'flex-end',
pointerEvents: 'all',
userSelect: 'none',
zIndex: 200,
border: '1px solid $panel',
boxShadow: '0px 2px 4px rgba(0,0,0,.12)',
display: 'flex',
padding: 4,
flexDirection: 'column',
alignItems: 'center',
'& svg': {
strokeWidth: 0,
},
variants: {
size: {
small: {
bottom: 12,
flexDirection: 'row',
alignItems: 'center',
},
},
},
})