tldraw/packages/tldraw/src/components/ToolsPanel/keyboardShortcutDialog.tsx
Steve Ruiz f0f545806a
[improvement] Quality and UI cleanup (#846)
* remove sponsors, ui cleanup

* fix radius

* improve panel

* remove cursor spline animations

* migrate options

* Switch hrs to divider

* fix text color on menu dark mode

* Remove option for clone handles

* fix wheel

* remove unused translations
2022-07-23 15:05:48 +01:00

238 lines
7.1 KiB
TypeScript

import * as React from 'react'
import * as Dialog from '@radix-ui/react-dialog'
import { Cross2Icon } from '@radix-ui/react-icons'
import { FormattedMessage, useIntl } from 'react-intl'
import { IconButton } from '~components/Primitives/IconButton'
import { RowButton } from '~components/Primitives/RowButton'
import { styled } from '~styles'
import { breakpoints } from '~components/breakpoints'
import { Kbd } from '~components/Primitives/Kbd'
export function KeyboardShortcutDialog({
onOpenChange,
}: {
onOpenChange?: (open: boolean) => void
}) {
const intl = useIntl()
const shortcuts = {
Tools: [
{ label: intl.formatMessage({ id: 'select' }), kbd: '1' },
{ label: intl.formatMessage({ id: 'draw' }), kbd: '2' },
{ label: intl.formatMessage({ id: 'eraser' }), kbd: '3' },
{ label: intl.formatMessage({ id: 'rectangle' }), kbd: '4' },
{ label: intl.formatMessage({ id: 'ellipse' }), kbd: '5' },
{ label: intl.formatMessage({ id: 'triangle' }), kbd: '6' },
{ label: intl.formatMessage({ id: 'line' }), kbd: '7' },
{ label: intl.formatMessage({ id: 'arrow' }), kbd: '8' },
{ label: intl.formatMessage({ id: 'text' }), kbd: '9' },
{ label: intl.formatMessage({ id: 'sticky' }), kbd: '0' },
],
View: [
{ label: intl.formatMessage({ id: 'zoom.in' }), kbd: '#+' },
{ label: intl.formatMessage({ id: 'zoom.out' }), kbd: '#-' },
{ label: `${intl.formatMessage({ id: 'zoom.to' })} 100%`, kbd: '⇧+0' },
{ label: intl.formatMessage({ id: 'zoom.to.fit' }), kbd: '⇧+1' },
{ label: intl.formatMessage({ id: 'zoom.to.selection' }), kbd: '⇧+2' },
{ label: intl.formatMessage({ id: 'preferences.dark.mode' }), kbd: '#⇧D' },
{ label: intl.formatMessage({ id: 'preferences.focus.mode' }), kbd: '#.' },
{ label: intl.formatMessage({ id: 'preferences.show.grid' }), kbd: '#⇧G' },
],
Transform: [
{ label: intl.formatMessage({ id: 'flip.horizontal' }), kbd: '⇧H' },
{ label: intl.formatMessage({ id: 'flip.vertical' }), kbd: '⇧V' },
{
label: `${intl.formatMessage({ id: 'lock' })} / ${intl.formatMessage({ id: 'unlock' })}`,
kbd: '#⇧L',
},
{
label: `${intl.formatMessage({ id: 'move' })} ${intl.formatMessage({ id: 'to.front' })}`,
kbd: '⇧]',
},
{
label: `${intl.formatMessage({ id: 'move' })} ${intl.formatMessage({ id: 'forward' })}`,
kbd: ']',
},
{
label: `${intl.formatMessage({ id: 'move' })} ${intl.formatMessage({ id: 'backward' })}`,
kbd: '[',
},
{
label: `${intl.formatMessage({ id: 'move' })} ${intl.formatMessage({ id: 'back' })}`,
kbd: '⇧[',
},
],
File: [
{ label: intl.formatMessage({ id: 'new.project' }), kbd: '#N' },
{ label: intl.formatMessage({ id: 'open' }), kbd: '#O' },
{ label: intl.formatMessage({ id: 'save' }), kbd: '#S' },
{ label: intl.formatMessage({ id: 'save.as' }), kbd: '#⇧S' },
{ label: intl.formatMessage({ id: 'upload.media' }), kbd: '#U' },
],
Edit: [
{ label: intl.formatMessage({ id: 'undo' }), kbd: '#Z' },
{ label: intl.formatMessage({ id: 'redo' }), kbd: '#⇧Z' },
{ label: intl.formatMessage({ id: 'cut' }), kbd: '#X' },
{ label: intl.formatMessage({ id: 'copy' }), kbd: '#C' },
{ label: intl.formatMessage({ id: 'paste' }), kbd: '#V' },
{ label: intl.formatMessage({ id: 'select.all' }), kbd: '#A' },
{ label: intl.formatMessage({ id: 'delete' }), kbd: '⌫' },
{ label: intl.formatMessage({ id: 'duplicate' }), kbd: '#D' },
],
}
return (
<Dialog.Root onOpenChange={onOpenChange}>
{/* // todo: hide if no keyboard is attached */}
<Dialog.Trigger asChild>
<RowButton id="TD-HelpItem-Keyboard" variant="wide">
<FormattedMessage id="keyboard.shortcuts" />
</RowButton>
</Dialog.Trigger>
<Dialog.Portal>
<DialogOverlay />
<DialogContent>
<DialogTitle>
<FormattedMessage id="keyboard.shortcuts" />
<Dialog.Close asChild>
<DialogIconButton>
<Cross2Icon />
</DialogIconButton>
</Dialog.Close>
</DialogTitle>
<StyledColumns bp={breakpoints}>
{Object.entries(shortcuts).map(([key, value]) => (
<StyledSection key={key}>
<Label>
<FormattedMessage id={`menu.${key.toLocaleLowerCase()}`} />
</Label>
<ContentItem>
{value.map((shortcut) => (
<StyledItem key={shortcut.label}>
{shortcut.label}
<Kbd variant="menu">{shortcut.kbd}</Kbd>
</StyledItem>
))}
</ContentItem>
</StyledSection>
))}
</StyledColumns>
</DialogContent>
</Dialog.Portal>
</Dialog.Root>
)
}
const Label = styled('h3', {
fontSize: '$2',
color: '$text',
fontFamily: '$ui',
margin: 0,
paddingBottom: '$5',
})
const StyledSection = styled('div', {
breakInside: 'avoid',
paddingBottom: 24,
})
const ContentItem = styled('ul', {
listStyleType: 'none',
width: '100%',
padding: 0,
margin: 0,
})
const StyledItem = styled('li', {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
height: 32,
minHeight: 32,
width: '100%',
outline: 'none',
color: '$text',
fontFamily: '$ui',
fontWeight: 400,
fontSize: '$1',
borderRadius: 4,
userSelect: 'none',
margin: 0,
padding: '0 0',
})
const DialogContent = styled(Dialog.Content, {
backgroundColor: 'white',
borderRadius: 6,
boxShadow: 'hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px',
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 'fit-content',
maxWidth: '90vw',
maxHeight: '74vh',
overflowY: 'auto',
padding: 25,
'&:focus': { outline: 'none' },
})
const StyledColumns = styled('div', {
maxWidth: '100%',
width: 'fit-content',
height: 'fit-content',
overflowY: 'auto',
columnGap: 64,
variants: {
bp: {
mobile: {
columns: 1,
[`& ${StyledSection}`]: {
minWidth: '0px',
},
},
small: {
columns: 2,
[`& ${StyledSection}`]: {
minWidth: '200px',
},
},
medium: {
columns: 3,
},
large: {
columns: 3,
},
},
},
})
const DialogOverlay = styled(Dialog.Overlay, {
backgroundColor: '$overlay',
position: 'fixed',
inset: 0,
})
const DialogIconButton = styled(IconButton, {
fontFamily: 'inherit',
borderRadius: '100%',
height: 25,
width: 25,
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
color: '$text',
cursor: 'pointer',
'&:hover': { backgroundColor: '$hover' },
})
const DialogTitle = styled(Dialog.Title, {
fontFamily: '$body',
fontSize: '$3',
color: '$text',
paddingBottom: 32,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
margin: 0,
})