Tldraw/packages/ui/src/lib/components/Menu.tsx

152 wiersze
4.0 KiB
TypeScript
Czysty Zwykły widok Historia

2023-04-25 11:01:25 +00:00
import { App, preventDefault, useApp } from '@tldraw/editor'
import * as React from 'react'
import { MenuChild } from '../hooks/menuHelpers'
import { useBreakpoint } from '../hooks/useBreakpoint'
import { useMenuClipboardEvents } from '../hooks/useClipboardEvents'
import { useMenuSchema } from '../hooks/useMenuSchema'
import { useReadonly } from '../hooks/useReadonly'
import { useTranslation } from '../hooks/useTranslation/useTranslation'
import { LanguageMenu } from './LanguageMenu'
import { Button } from './primitives/Button'
import * as M from './primitives/DropdownMenu'
import { Kbd } from './primitives/Kbd'
export const Menu = React.memo(function Menu() {
const msg = useTranslation()
return (
<M.Root id="main menu">
<M.Trigger>
<Button
className="tlui-menu__trigger"
data-wd="main.menu"
title={msg('menu.title')}
icon="menu"
/>
</M.Trigger>
<M.Content alignOffset={0} sideOffset={6}>
<MenuContent />
</M.Content>
</M.Root>
)
})
function MenuContent() {
const app = useApp()
const msg = useTranslation()
const menuSchema = useMenuSchema()
const breakpoint = useBreakpoint()
const isReadonly = useReadonly()
const { paste } = useMenuClipboardEvents()
function getMenuItem(app: App, item: MenuChild, parent: MenuChild | null, depth: number) {
switch (item.type) {
case 'custom': {
if (isReadonly && !item.readonlyOk) return null
if (item.id === 'LANGUAGE_MENU') {
return <LanguageMenu key="item" />
}
if (item.id === 'MENU_PASTE') {
return (
<M.Item
key={item.id}
data-wd={`menu-item.${item.id}`}
kbd="$v"
label="action.paste"
disabled={item.disabled}
onMouseDown={() => {
if (app.isSafari && navigator.clipboard?.read) {
// NOTE: This must be a onMouseDown for Safari/desktop, onClick doesn't work at the time of writing...
navigator.clipboard.read().then((clipboardItems) => {
paste(clipboardItems)
})
}
}}
onClick={() => {
if (app.isSafari) {
// noop
} else if (navigator.clipboard?.read) {
navigator.clipboard.read().then((clipboardItems) => {
paste(clipboardItems)
})
}
}}
onPointerUp={preventDefault}
/>
)
}
return null
}
case 'group': {
if (isReadonly && !item.readonlyOk) return null
return (
<M.Group
size={
depth <= 1
? 'medium'
: breakpoint < 3 || (parent?.type === 'submenu' && depth > 2)
? 'tiny'
: 'medium'
}
key={item.id}
>
{item.children.map((child) => getMenuItem(app, child, item, depth + 1))}
</M.Group>
)
}
case 'submenu': {
if (isReadonly && !item.readonlyOk) return null
return (
<M.Sub id={`main menu ${parent ? parent.id + ' ' : ''}${item.id}`} key={item.id}>
<M.SubTrigger label={item.label} data-wd={`menu-item.${item.id}`} />
<M.SubContent sideOffset={-4} alignOffset={-1}>
{item.children.map((child) => getMenuItem(app, child, item, depth + 1))}
</M.SubContent>
</M.Sub>
)
}
case 'item': {
if (isReadonly && !item.readonlyOk) return null
const { id, checkbox, menuLabel, label, onSelect, kbd } = item.actionItem
const labelToUse = menuLabel ?? label
const labelStr = labelToUse ? msg(labelToUse) : undefined
if (checkbox) {
// Item is in a checkbox group
return (
<M.CheckboxItem
key={id}
[feature] ui events (#1326) This PR updates the editor events: - adds types to the events emitted by the app (by `app.emit`) - removes a few events emitted by the app (e.g. `move-to-page`, `change-camera`) - adds `onEvent` prop to the <TldrawUi> / <Tldraw> components - call the `onEvent` when actions occur or tools are selected - does some superficial cleanup on editor app APIs ### Release Note - Fix layout bug in error dialog - (ui) Add `TLEventMap` for types emitted from editor app - (editor) Update `crash` event emitted from editor app to include error - (editor) Update `change-history` event emitted from editor app - (editor) Remove `change-camera` event from editor app - (editor) Remove `move-to-page` event from editor app - (ui) Add `onEvent` prop and events to <Tldraw> / <TldrawUi> - (editor) Replace `app.openMenus` plain Set with computed value - (editor) Add `addOpenMenu` method - (editor) Add `removeOpenMenu` method - (editor) Add `setFocusMode` method - (editor) Add `setToolLocked` method - (editor) Add `setSnapMode` method - (editor) Add `isSnapMode` method - (editor) Update `setGridMode` method return type to editor app - (editor) Update `setReadOnly` method return type to editor app - (editor) Update `setPenMode` method return type to editor app - (editor) Update `selectNone` method return type to editor app - (editor) Rename `backToContent` to `zoomToContent` - (editor) Remove `TLReorderOperation` type --------- Co-authored-by: Orange Mug <orangemug@users.noreply.github.com>
2023-05-11 22:14:58 +00:00
onSelect={() => onSelect('menu')}
2023-04-25 11:01:25 +00:00
title={labelStr ? labelStr : ''}
checked={item.checked}
disabled={item.disabled}
>
{labelStr && <span>{labelStr}</span>}
{kbd && <Kbd>{kbd}</Kbd>}
</M.CheckboxItem>
)
}
// Item is a button
return (
<M.Item
key={id}
data-wd={`menu-item.${item.id}`}
kbd={kbd}
label={labelToUse}
[feature] ui events (#1326) This PR updates the editor events: - adds types to the events emitted by the app (by `app.emit`) - removes a few events emitted by the app (e.g. `move-to-page`, `change-camera`) - adds `onEvent` prop to the <TldrawUi> / <Tldraw> components - call the `onEvent` when actions occur or tools are selected - does some superficial cleanup on editor app APIs ### Release Note - Fix layout bug in error dialog - (ui) Add `TLEventMap` for types emitted from editor app - (editor) Update `crash` event emitted from editor app to include error - (editor) Update `change-history` event emitted from editor app - (editor) Remove `change-camera` event from editor app - (editor) Remove `move-to-page` event from editor app - (ui) Add `onEvent` prop and events to <Tldraw> / <TldrawUi> - (editor) Replace `app.openMenus` plain Set with computed value - (editor) Add `addOpenMenu` method - (editor) Add `removeOpenMenu` method - (editor) Add `setFocusMode` method - (editor) Add `setToolLocked` method - (editor) Add `setSnapMode` method - (editor) Add `isSnapMode` method - (editor) Update `setGridMode` method return type to editor app - (editor) Update `setReadOnly` method return type to editor app - (editor) Update `setPenMode` method return type to editor app - (editor) Update `selectNone` method return type to editor app - (editor) Rename `backToContent` to `zoomToContent` - (editor) Remove `TLReorderOperation` type --------- Co-authored-by: Orange Mug <orangemug@users.noreply.github.com>
2023-05-11 22:14:58 +00:00
onClick={() => onSelect('menu')}
2023-04-25 11:01:25 +00:00
disabled={item.disabled}
/>
)
}
}
}
return <>{menuSchema.map((item) => getMenuItem(app, item, null, 0))}</>
}