feat: add help panel (#816)

* feat: add help panel

* feat: added all the shortcuts and responsive

* improve help panel

* add modal for the shortcut

* add grid

* fix language menu

* add responsive grid

* Styling keyboard shortcuts / panel

* fix links ts issue

* Improve styling

* Fix translation bug

Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This commit is contained in:
Judicael 2022-07-19 12:04:38 +03:00 committed by GitHub
parent 240202bb81
commit 77337b1281
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 516 additions and 81 deletions

View file

@ -43,8 +43,10 @@
"dependencies": {
"@radix-ui/react-alert-dialog": "^0.1.7",
"@radix-ui/react-context-menu": "^0.1.6",
"@radix-ui/react-dialog": "^0.1.7",
"@radix-ui/react-dropdown-menu": "^0.1.6",
"@radix-ui/react-icons": "^1.1.1",
"@radix-ui/react-popover": "^0.1.6",
"@radix-ui/react-tooltip": "^0.1.7",
"@stitches/react": "^1.2.8",
"@tldraw/core": "^1.14.1",

View file

@ -9,6 +9,7 @@ export interface DMContentProps {
align?: 'start' | 'center' | 'end'
sideOffset?: number
children: React.ReactNode
overflow?: boolean
id?: string
side?: 'top' | 'left' | 'right' | 'bottom' | undefined
}
@ -19,6 +20,7 @@ export function DMContent({
align,
variant,
id,
overflow = false,
side = 'bottom',
}: DMContentProps) {
return (
@ -31,7 +33,9 @@ export function DMContent({
id={id}
side={side}
>
<StyledContent variant={variant}>{children}</StyledContent>
<StyledContent variant={variant} overflow={overflow}>
{children}
</StyledContent>
</DropdownMenu.Content>
)
}
@ -40,11 +44,14 @@ export const StyledContent = styled(MenuContent, {
width: 'fit-content',
height: 'fit-content',
minWidth: 0,
maxHeight: '75vh',
maxHeight: '100vh',
overflowY: 'auto',
'& *': {
boxSizing: 'border-box',
overflowX: 'hidden',
'&::webkit-scrollbar': {
display: 'none',
},
'-ms-overflow-style': 'none' /* for Internet Explorer, Edge */,
scrollbarWidth: 'none',
variants: {
variant: {
horizontal: {
@ -54,5 +61,10 @@ export const StyledContent = styled(MenuContent, {
minWidth: 128,
},
},
overflow: {
true: {
maxHeight: '60vh',
},
},
},
})

View file

@ -8,10 +8,18 @@ export interface DMSubMenuProps {
size?: 'small'
disabled?: boolean
children: React.ReactNode
overflow?: boolean
id?: string
}
export function DMSubMenu({ children, size, disabled = false, label, id }: DMSubMenuProps) {
export function DMSubMenu({
children,
size,
overflow = false,
disabled = false,
label,
id,
}: DMSubMenuProps) {
return (
<span id={id}>
<Root dir="ltr">
@ -21,7 +29,7 @@ export function DMSubMenu({ children, size, disabled = false, label, id }: DMSub
</RowButton>
</TriggerItem>
<Content dir="ltr" asChild sideOffset={2} alignOffset={-2} align="start">
<MenuContent size={size} overflow>
<MenuContent size={size} overflow={overflow}>
{children}
<Arrow offset={13} />
</MenuContent>

View file

@ -14,6 +14,14 @@ export const MenuContent = styled('div', {
padding: '$2 $2',
borderRadius: '$3',
font: '$ui',
maxHeight: '100vh',
overflowY: 'auto',
overflowX: 'hidden',
'&::webkit-scrollbar': {
display: 'none',
},
'-ms-overflow-style': 'none' /* for Internet Explorer, Edge */,
scrollbarWidth: 'none',
variants: {
size: {
small: {
@ -23,14 +31,7 @@ export const MenuContent = styled('div', {
overflow: {
true: {
maxHeight: '60vh',
overflowY: 'auto',
overflowX: 'hidden',
},
},
},
'-ms-overflow-style': 'none' /* for Internet Explorer, Edge */,
scrollbarWidth: 'none',
'&::webkit-scrollbar': {
display: 'none',
},
})

View file

@ -0,0 +1,19 @@
import * as React from 'react'
export function QuestionMarkIcon(props: React.SVGProps<SVGSVGElement>) {
return (
<svg
width="8"
height="16"
viewBox="0 0 4 8"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M1.15948 5.85192C1.05082 5.66856 0.972717 5.50556 0.925178 5.36295C0.884431 5.21354 0.864057 5.06074 0.864057 4.90454C0.864057 4.69401 0.921782 4.50046 1.03723 4.32389C1.15269 4.14731 1.2953 3.98093 1.46508 3.82473C1.63486 3.66174 1.80125 3.50214 1.96424 3.34594C2.13402 3.18295 2.27664 3.01317 2.39209 2.8366C2.50754 2.65324 2.56527 2.45629 2.56527 2.24576C2.56527 1.98769 2.48038 1.78056 2.31059 1.62436C2.1476 1.46137 1.93028 1.37988 1.65863 1.37988C1.48206 1.37988 1.32586 1.41044 1.19004 1.47156C1.05421 1.52589 0.911596 1.55305 0.762188 1.55305C0.572033 1.55305 0.429416 1.50551 0.334339 1.41044C0.239261 1.31536 0.191722 1.21009 0.191722 1.09464C0.191722 0.958818 0.252844 0.843366 0.375086 0.748289C0.50412 0.64642 0.701067 0.595485 0.965926 0.595485C1.30549 0.595485 1.6111 0.64642 1.88275 0.748289C2.1544 0.850157 2.38869 0.996169 2.58564 1.18632C2.78938 1.36969 2.94218 1.5904 3.04405 1.84847C3.15271 2.10654 3.20704 2.39517 3.20704 2.71436C3.20704 2.98601 3.14932 3.22031 3.03386 3.41725C2.91841 3.60741 2.7758 3.77719 2.60601 3.9266C2.44302 4.07601 2.27664 4.21862 2.10686 4.35445C1.93708 4.49027 1.79446 4.63628 1.67901 4.79248C1.56356 4.94189 1.50583 5.12186 1.50583 5.33239C1.50583 5.42067 1.50923 5.50896 1.51602 5.59725C1.5296 5.68553 1.54658 5.77042 1.56695 5.85192H1.15948ZM1.3734 7.8078C1.25116 7.8078 1.1391 7.78064 1.03723 7.72631C0.942156 7.67198 0.867452 7.59727 0.813122 7.50219C0.758792 7.40712 0.731627 7.30185 0.731627 7.1864C0.731627 7.06416 0.758792 6.95889 0.813122 6.87061C0.867452 6.77553 0.942156 6.70083 1.03723 6.6465C1.1391 6.59217 1.25116 6.565 1.3734 6.565C1.55677 6.565 1.70957 6.62612 1.83181 6.74837C1.95405 6.86382 2.01518 7.00983 2.01518 7.1864C2.01518 7.36297 1.95405 7.51238 1.83181 7.63462C1.70957 7.75008 1.55677 7.8078 1.3734 7.8078Z"
fill="currentColor"
/>
</svg>
)
}

View file

@ -15,3 +15,4 @@ export * from './EraserIcon'
export * from './MultiplayerIcon'
export * from './DiscordIcon'
export * from './LineIcon'
export * from './QuestionMarkIcon'

View file

@ -51,9 +51,6 @@ const BackToContentContainer = styled(MenuContent, {
pointerEvents: 'all',
width: 'fit-content',
minWidth: 0,
// gridRow: 1,
// flexGrow: 2,
// display: 'block',
position: 'fixed',
bottom: 0,
})

View file

@ -0,0 +1,184 @@
import * as React from 'react'
import * as Popover from '@radix-ui/react-popover'
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
import { FormattedMessage } from 'react-intl'
import { styled } from '~styles'
import { useTldrawApp } from '~hooks'
import { TDSnapshot } from '~types'
import { breakpoints } from '~components/breakpoints'
import { GitHubLogoIcon, QuestionMarkIcon, TwitterLogoIcon } from '@radix-ui/react-icons'
import { RowButton } from '~components/Primitives/RowButton'
import { MenuContent } from '~components/Primitives/MenuContent'
import { DMContent, DMDivider } from '~components/Primitives/DropdownMenu'
import { SmallIcon } from '~components/Primitives/SmallIcon'
import { DiscordIcon } from '~components/Primitives/icons'
import { LanguageMenu } from '~components/TopPanel/LanguageMenu/LanguageMenu'
import { KeyboardShortcutDialog } from './keyboardShortcutDialog'
const isDebugModeSelector = (s: TDSnapshot) => s.settings.isDebugMode
const dockPositionState = (s: TDSnapshot) => s.settings.dockPosition
export function HelpPanel() {
const app = useTldrawApp()
const isDebugMode = app.useStore(isDebugModeSelector)
const side = app.useStore(dockPositionState)
const [isKeyboardShortcutsOpen, setIsKeyboardShortcutsOpen] = React.useState(false)
return (
<Popover.Root>
<PopoverAnchor dir="ltr">
<Popover.Trigger asChild dir="ltr">
<HelpButton side={side} debug={isDebugMode} bp={breakpoints}>
<QuestionMarkIcon />
</HelpButton>
</Popover.Trigger>
</PopoverAnchor>
<Popover.Content dir="ltr">
<StyledContent style={{ visibility: isKeyboardShortcutsOpen ? 'hidden' : 'visible' }}>
<LanguageMenuDropdown />
<KeyboardShortcutDialog onOpenChange={setIsKeyboardShortcutsOpen} />
<DMDivider />
<Links />
</StyledContent>
</Popover.Content>
</Popover.Root>
)
}
const LanguageMenuDropdown = () => {
return (
<DropdownMenu.Root dir="ltr">
<DropdownMenu.Trigger asChild>
<RowButton variant="wide" hasArrow>
<FormattedMessage id="language" />
</RowButton>
</DropdownMenu.Trigger>
<LanguageMenu />
</DropdownMenu.Root>
)
}
const linksData = [
{ id: 'github', title: 'Github', icon: GitHubLogoIcon, url: 'https://github.com/tldraw/tldraw' },
{ id: 'twitter', title: 'Twitter', icon: TwitterLogoIcon, url: 'https://twitter.com/tldraw' },
{ id: 'discord', title: 'Discord', icon: DiscordIcon, url: 'https://discord.gg/SBBEVCA4PG' },
]
const Links = () => {
return (
<>
{linksData.map((item) => (
<a key={item.id} href={item.url} target="_blank" rel="nofollow">
<RowButton id={`TD-Link-${item.id}`} variant="wide">
{item.title}
<SmallIcon>
<item.icon />
</SmallIcon>
</RowButton>
</a>
))}
</>
)
}
const HelpButton = styled('button', {
width: 28,
height: 28,
borderRadius: '100%',
position: 'fixed',
right: 10,
display: 'grid',
placeItems: 'center',
border: 'none',
backgroundColor: 'white',
cursor: 'pointer',
boxShadow: '$panel',
bottom: 10,
variants: {
debug: {
true: {},
false: {},
},
bp: {
mobile: {},
small: {},
medium: {},
large: {},
},
side: {
top: {},
left: {},
right: {},
bottom: {},
},
},
compoundVariants: [
{
bp: 'mobile',
side: 'bottom',
debug: false,
css: {
bottom: 70,
},
},
{
bp: 'mobile',
debug: true,
css: {
bottom: 50, // 40 + 10
},
},
{
bp: 'mobile',
side: 'bottom',
debug: true,
css: {
bottom: 110,
},
},
{
bp: 'small',
side: 'bottom',
debug: true,
css: {
bottom: 50,
},
},
{
bp: 'small',
debug: false,
css: {
bottom: 10,
},
},
],
})
export const StyledContent = styled(MenuContent, {
width: 'fit-content',
height: 'fit-content',
minWidth: 200,
maxHeight: 380,
overflowY: 'auto',
'& *': {
boxSizing: 'border-box',
},
variants: {
variant: {
horizontal: {
flexDirection: 'row',
},
menu: {
minWidth: 128,
},
},
},
})
const PopoverAnchor = styled(Popover.Anchor, {
position: 'absolute',
right: 10,
zIndex: 999,
bottom: 50,
})

View file

@ -97,7 +97,7 @@ export const ShapesMenu = React.memo(function ShapesMenu({
{shapeShapes.map((shape, i) => (
<Tooltip
key={shape}
label={intl.formatMessage({ id: shape[0].toUpperCase() + shape.slice(1) })}
label={intl.formatMessage({ id: shape })}
kbd={(4 + i).toString()}
id={`TD-PrimaryTools-Shapes-${shape}`}
>

View file

@ -8,6 +8,7 @@ import { PrimaryTools } from './PrimaryTools'
import { ActionButton } from './ActionButton'
import { DeleteButton } from './DeleteButton'
import { breakpoints } from '~components/breakpoints'
import { HelpPanel } from './HelpPanel'
const isDebugModeSelector = (s: TDSnapshot) => s.settings.isDebugMode
const dockPositionState = (s: TDSnapshot) => s.settings.dockPosition
@ -35,6 +36,7 @@ export const ToolsPanel = React.memo(function ToolsPanel({ onBlur }: ToolsPanelP
</StyledPrimaryTools>
</StyledCenterWrap>
</StyledToolsPanelContainer>
<HelpPanel />
{isDebugMode && (
<StyledStatusWrap>
<StatusBar />
@ -84,7 +86,7 @@ const StyledToolsPanelContainer = styled('div', {
width: '100%',
left: 0,
right: 0,
bottom: 0,
bottom: 4,
},
left: { width: 64, height: '100%', left: 0 },
},
@ -101,7 +103,7 @@ const StyledToolsPanelContainer = styled('div', {
side: 'bottom',
debug: true,
css: {
bottom: '40px',
bottom: 44,
},
},
],

View file

@ -0,0 +1,235 @@
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}>
<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>{key}</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,
})

View file

@ -1,8 +1,7 @@
import { ExternalLinkIcon } from '@radix-ui/react-icons'
import * as React from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
import { DMCheckboxItem, DMDivider, DMItem, DMSubMenu } from '~components/Primitives/DropdownMenu'
import { HeartIcon } from '~components/Primitives/icons/HeartIcon'
import { ExternalLinkIcon } from '@radix-ui/react-icons'
import { FormattedMessage } from 'react-intl'
import { DMCheckboxItem, DMContent, DMDivider, DMItem } from '~components/Primitives/DropdownMenu'
import { SmallIcon } from '~components/Primitives/SmallIcon'
import { useTldrawApp } from '~hooks'
import { TDLanguage, TRANSLATIONS } from '~translations'
@ -13,7 +12,6 @@ const languageSelector = (s: TDSnapshot) => s.settings.language
export function LanguageMenu() {
const app = useTldrawApp()
const language = app.useStore(languageSelector)
const intl = useIntl()
const handleChangeLanguage = React.useCallback(
(locale: TDLanguage) => {
@ -23,7 +21,7 @@ export function LanguageMenu() {
)
return (
<DMSubMenu label={intl.formatMessage({ id: 'language' })}>
<DMContent variant="menu" overflow={true} id="language-menu" side="left" sideOffset={8}>
{TRANSLATIONS.map(({ locale, label }) => (
<DMCheckboxItem
key={locale}
@ -47,6 +45,6 @@ export function LanguageMenu() {
</SmallIcon>
</DMItem>
</a>
</DMSubMenu>
</DMContent>
)
}

View file

@ -24,7 +24,6 @@ import { DiscordIcon } from '~components/Primitives/icons'
import { TDExportType, TDSnapshot } from '~types'
import { Divider } from '~components/Primitives/Divider'
import { FormattedMessage, useIntl } from 'react-intl'
import { LanguageMenu } from '../LanguageMenu/LanguageMenu'
interface MenuProps {
sponsor: boolean | undefined
@ -326,53 +325,6 @@ export const Menu = React.memo(function Menu({ sponsor, readOnly }: MenuProps) {
</DMSubMenu>
<DMDivider dir="ltr" />
<PreferencesMenu />
<DMDivider dir="ltr" />
<LanguageMenu />
<DMDivider dir="ltr" />
<a href="https://github.com/Tldraw/Tldraw" target="_blank" rel="nofollow">
<DMItem id="TD-MenuItem-Github">
GitHub
<SmallIcon>
<GitHubLogoIcon />
</SmallIcon>
</DMItem>
</a>
<a href="https://twitter.com/Tldraw" target="_blank" rel="nofollow">
<DMItem id="TD-MenuItem-Twitter">
Twitter
<SmallIcon>
<TwitterLogoIcon />
</SmallIcon>
</DMItem>
</a>
<a href="https://discord.gg/SBBEVCA4PG" target="_blank" rel="nofollow">
<DMItem id="TD-MenuItem-Discord">
Discord
<SmallIcon>
<DiscordIcon />
</SmallIcon>
</DMItem>
</a>
{sponsor === false && (
<a href="https://github.com/sponsors/steveruizok" target="_blank" rel="nofollow">
<DMItem isSponsor id="TD-MenuItem-Become_a_Sponsor">
<FormattedMessage id="become.a.sponsor" />{' '}
<SmallIcon>
<HeartIcon />
</SmallIcon>
</DMItem>
</a>
)}
{sponsor === true && (
<a href="https://github.com/sponsors/steveruizok" target="_blank" rel="nofollow">
<DMItem id="TD-MenuItem-is_a_Sponsor">
<FormattedMessage id="sponsored" />!
<SmallIcon>
<HeartFilledIcon />
</SmallIcon>
</DMItem>
</a>
)}
{showSignInOutMenu && (
<>
<DMDivider dir="ltr" />{' '}

View file

@ -138,7 +138,7 @@ export function PreferencesMenu() {
>
<FormattedMessage id="preferences.clone.handles" />
</DMCheckboxItem>
<DMSubMenu label={intl.formatMessage({ id: 'dock.position' })}>
<DMSubMenu label={intl.formatMessage({ id: 'dock.position' })} overflow={false}>
{DockPosition.map((position) => (
<DMCheckboxItem
key={position}

View file

@ -91,6 +91,7 @@
"language": "Langage",
"dock.position": "Position du dock",
"bottom": "En bas",
"keyboard.shortcuts": "Raccourci clavier",
"loading": "Chargement{dots}",
"left": "À gauche",
"right": "À droite",

View file

@ -64,10 +64,10 @@
"arrow": "Arrow",
"text": "Text",
"sticky": "Sticky",
"Rectangle": "Rectangle",
"Ellipse": "Ellipse",
"Triangle": "Triangle",
"Line": "Line",
"rectangle": "Rectangle",
"ellipse": "Ellipse",
"triangle": "Triangle",
"line": "Line",
"rotate": "Rotate",
"lock.aspect.ratio": "Lock Aspect Ratio",
"unlock.aspect.ratio": "Unlock Aspect Ratio",
@ -96,5 +96,7 @@
"right": "Right",
"top": "Top",
"page": "Page",
"keyboard.shortcuts": "Keyboard shortcuts",
"search": "Search",
"loading": "Loading{dots}"
}

View file

@ -2482,7 +2482,7 @@
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-dialog@0.1.7":
"@radix-ui/react-dialog@0.1.7", "@radix-ui/react-dialog@^0.1.7":
version "0.1.7"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-0.1.7.tgz#285414cf66f5bbf42bc9935314e0381abe01e7d0"
integrity sha512-jXt8srGhHBRvEr9jhEAiwwJzWCWZoGRJ030aC9ja/gkRJbZdy0iD3FwXf+Ff4RtsZyLUMHW7VUwFOlz3Ixe1Vw==
@ -2584,6 +2584,27 @@
aria-hidden "^1.1.1"
react-remove-scroll "^2.4.0"
"@radix-ui/react-popover@^0.1.6":
version "0.1.6"
resolved "https://registry.yarnpkg.com/@radix-ui/react-popover/-/react-popover-0.1.6.tgz#788e969239d9c55239678e615ab591b6b7ba5cdc"
integrity sha512-zQzgUqW4RQDb0ItAL1xNW4K4olUrkfV3jeEPs9rG+nsDQurO+W9TT+YZ9H1mmgAJqlthyv1sBRZGdBm4YjtD6Q==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.1.0"
"@radix-ui/react-compose-refs" "0.1.0"
"@radix-ui/react-context" "0.1.1"
"@radix-ui/react-dismissable-layer" "0.1.5"
"@radix-ui/react-focus-guards" "0.1.0"
"@radix-ui/react-focus-scope" "0.1.4"
"@radix-ui/react-id" "0.1.5"
"@radix-ui/react-popper" "0.1.4"
"@radix-ui/react-portal" "0.1.4"
"@radix-ui/react-presence" "0.1.2"
"@radix-ui/react-primitive" "0.1.4"
"@radix-ui/react-use-controllable-state" "0.1.0"
aria-hidden "^1.1.1"
react-remove-scroll "^2.4.0"
"@radix-ui/react-popper@0.1.4":
version "0.1.4"
resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-0.1.4.tgz#dfc055dcd7dfae6a2eff7a70d333141d15a5d029"