menu: rework File menu / ensure Export menu is present (#2783)
<img width="428" alt="Screenshot 2024-02-16 at 16 46 28" src="https://github.com/tldraw/tldraw/assets/469604/334cd0db-d9d5-4993-8012-c6985173edfb"> - re-orders to be the normative New / Open / Save order — we shouldn't be messing with this conventional ordering - removes the "Don't ask again" from New/Open dialogs because they're non-undoable and not what _anybody_ should ever select. we shouldn't offer users a loaded footgun! :P - makes File menu be part of the default menu — it's presence is glaringly missing for regular development - along with that, make the pieces of that menu available as lego pieces to use - it can't just be `DefaultMainMenuContent`, all or nothing, forcing downstream users to import everything from scratch - finally, adds the Export menu as initially intended by this PR! @steveruizok let's discuss if you have some notes on this and we can talk about the shape of things here. ### Change Type - [x] `patch` — Bug fix ### Release Notes - Composable UI: makes File items be more granularly accessible / usable - Menu: show Export under the File menu.
This commit is contained in:
parent
f19b12c42e
commit
fb852459db
15 changed files with 441 additions and 175 deletions
|
@ -1,4 +1,5 @@
|
|||
import {
|
||||
ExportFileContentSubMenu,
|
||||
TldrawUiMenuGroup,
|
||||
TldrawUiMenuItem,
|
||||
TldrawUiMenuSubmenu,
|
||||
|
@ -17,9 +18,10 @@ export function LocalFileMenu() {
|
|||
return (
|
||||
<TldrawUiMenuSubmenu id="file" label="menu.file">
|
||||
<TldrawUiMenuGroup id="file-actions">
|
||||
<TldrawUiMenuItem {...actions[SAVE_FILE_COPY_ACTION]} />
|
||||
<TldrawUiMenuItem {...actions[OPEN_FILE_ACTION]} />
|
||||
<TldrawUiMenuItem {...actions[NEW_PROJECT_ACTION]} />
|
||||
<TldrawUiMenuItem {...actions[OPEN_FILE_ACTION]} />
|
||||
<TldrawUiMenuItem {...actions[SAVE_FILE_COPY_ACTION]} />
|
||||
<ExportFileContentSubMenu />
|
||||
</TldrawUiMenuGroup>
|
||||
<TldrawUiMenuGroup id="share">
|
||||
<TldrawUiMenuItem {...actions[SHARE_PROJECT_ACTION]} />
|
||||
|
@ -35,6 +37,7 @@ export function MultiplayerFileMenu() {
|
|||
<TldrawUiMenuSubmenu id="file" label="menu.file">
|
||||
<TldrawUiMenuGroup id="file-actions">
|
||||
<TldrawUiMenuItem {...actions[SAVE_FILE_COPY_ACTION]} />
|
||||
<ExportFileContentSubMenu />
|
||||
</TldrawUiMenuGroup>
|
||||
<TldrawUiMenuGroup id="share">
|
||||
<TldrawUiMenuItem {...actions[FORK_PROJECT_ACTION]} />
|
||||
|
|
|
@ -6,12 +6,16 @@ import {
|
|||
DefaultKeyboardShortcutsDialog,
|
||||
DefaultKeyboardShortcutsDialogContent,
|
||||
DefaultMainMenu,
|
||||
DefaultMainMenuContent,
|
||||
EditSubmenu,
|
||||
Editor,
|
||||
ExtrasGroup,
|
||||
ObjectSubmenu,
|
||||
PreferencesGroup,
|
||||
TLComponents,
|
||||
Tldraw,
|
||||
TldrawUiMenuGroup,
|
||||
TldrawUiMenuItem,
|
||||
ViewSubmenu,
|
||||
useActions,
|
||||
} from '@tldraw/tldraw'
|
||||
import { useCallback } from 'react'
|
||||
|
@ -44,7 +48,11 @@ const components: TLComponents = {
|
|||
MainMenu: () => (
|
||||
<DefaultMainMenu>
|
||||
<LocalFileMenu />
|
||||
<DefaultMainMenuContent />
|
||||
<EditSubmenu />
|
||||
<ObjectSubmenu />
|
||||
<ViewSubmenu />
|
||||
<ExtrasGroup />
|
||||
<PreferencesGroup />
|
||||
<Links />
|
||||
</DefaultMainMenu>
|
||||
),
|
||||
|
|
|
@ -6,13 +6,17 @@ import {
|
|||
DefaultKeyboardShortcutsDialog,
|
||||
DefaultKeyboardShortcutsDialogContent,
|
||||
DefaultMainMenu,
|
||||
DefaultMainMenuContent,
|
||||
EditSubmenu,
|
||||
Editor,
|
||||
ExtrasGroup,
|
||||
ObjectSubmenu,
|
||||
OfflineIndicator,
|
||||
PreferencesGroup,
|
||||
TLComponents,
|
||||
Tldraw,
|
||||
TldrawUiMenuGroup,
|
||||
TldrawUiMenuItem,
|
||||
ViewSubmenu,
|
||||
atom,
|
||||
debugFlags,
|
||||
lns,
|
||||
|
@ -66,7 +70,11 @@ const components: TLComponents = {
|
|||
MainMenu: () => (
|
||||
<DefaultMainMenu>
|
||||
<MultiplayerFileMenu />
|
||||
<DefaultMainMenuContent />
|
||||
<EditSubmenu />
|
||||
<ObjectSubmenu />
|
||||
<ViewSubmenu />
|
||||
<ExtrasGroup />
|
||||
<PreferencesGroup />
|
||||
<Links />
|
||||
</DefaultMainMenu>
|
||||
),
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import {
|
||||
TLUiDialogsContextType,
|
||||
TldrawUiButton,
|
||||
TldrawUiButtonCheck,
|
||||
TldrawUiButtonLabel,
|
||||
TldrawUiDialogBody,
|
||||
TldrawUiDialogCloseButton,
|
||||
|
@ -10,34 +9,29 @@ import {
|
|||
TldrawUiDialogTitle,
|
||||
useTranslation,
|
||||
} from '@tldraw/tldraw'
|
||||
import { useState } from 'react'
|
||||
import { userPreferences } from './userPreferences'
|
||||
|
||||
export async function shouldClearDocument(addDialog: TLUiDialogsContextType['addDialog']) {
|
||||
if (userPreferences.showFileClearWarning.get()) {
|
||||
const shouldContinue = await new Promise<boolean>((resolve) => {
|
||||
addDialog({
|
||||
component: ({ onClose }) => (
|
||||
<ConfirmClearDialog
|
||||
onCancel={() => {
|
||||
resolve(false)
|
||||
onClose()
|
||||
}}
|
||||
onContinue={() => {
|
||||
resolve(true)
|
||||
onClose()
|
||||
}}
|
||||
/>
|
||||
),
|
||||
onClose: () => {
|
||||
resolve(false)
|
||||
},
|
||||
})
|
||||
const shouldContinue = await new Promise<boolean>((resolve) => {
|
||||
addDialog({
|
||||
component: ({ onClose }) => (
|
||||
<ConfirmClearDialog
|
||||
onCancel={() => {
|
||||
resolve(false)
|
||||
onClose()
|
||||
}}
|
||||
onContinue={() => {
|
||||
resolve(true)
|
||||
onClose()
|
||||
}}
|
||||
/>
|
||||
),
|
||||
onClose: () => {
|
||||
resolve(false)
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
return shouldContinue
|
||||
}
|
||||
return true
|
||||
return shouldContinue
|
||||
}
|
||||
|
||||
function ConfirmClearDialog({
|
||||
|
@ -48,7 +42,6 @@ function ConfirmClearDialog({
|
|||
onContinue: () => void
|
||||
}) {
|
||||
const msg = useTranslation()
|
||||
const [dontShowAgain, setDontShowAgain] = useState(false)
|
||||
return (
|
||||
<>
|
||||
<TldrawUiDialogHeader>
|
||||
|
@ -59,28 +52,10 @@ function ConfirmClearDialog({
|
|||
{msg('file-system.confirm-clear.description')}
|
||||
</TldrawUiDialogBody>
|
||||
<TldrawUiDialogFooter className="tlui-dialog__footer__actions">
|
||||
<TldrawUiButton
|
||||
type="normal"
|
||||
onClick={() => setDontShowAgain(!dontShowAgain)}
|
||||
style={{ marginRight: 'auto' }}
|
||||
>
|
||||
<TldrawUiButtonCheck checked={dontShowAgain} />
|
||||
<TldrawUiButtonLabel>
|
||||
{msg('file-system.confirm-clear.dont-show-again')}
|
||||
</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
<TldrawUiButton type="normal" onClick={onCancel}>
|
||||
<TldrawUiButtonLabel>{msg('file-system.confirm-clear.cancel')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
<TldrawUiButton
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
if (dontShowAgain) {
|
||||
userPreferences.showFileClearWarning.set(false)
|
||||
}
|
||||
onContinue()
|
||||
}}
|
||||
>
|
||||
<TldrawUiButton type="primary" onClick={async () => onContinue()}>
|
||||
<TldrawUiButtonLabel>{msg('file-system.confirm-clear.continue')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDialogFooter>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import {
|
||||
TLUiDialogsContextType,
|
||||
TldrawUiButton,
|
||||
TldrawUiButtonCheck,
|
||||
TldrawUiButtonLabel,
|
||||
TldrawUiDialogBody,
|
||||
TldrawUiDialogCloseButton,
|
||||
|
@ -10,34 +9,30 @@ import {
|
|||
TldrawUiDialogTitle,
|
||||
useTranslation,
|
||||
} from '@tldraw/tldraw'
|
||||
import { useState } from 'react'
|
||||
import { userPreferences } from './userPreferences'
|
||||
|
||||
/** @public */
|
||||
export async function shouldOverrideDocument(addDialog: TLUiDialogsContextType['addDialog']) {
|
||||
if (userPreferences.showFileOpenWarning.get()) {
|
||||
const shouldContinue = await new Promise<boolean>((resolve) => {
|
||||
addDialog({
|
||||
component: ({ onClose }) => (
|
||||
<ConfirmOpenDialog
|
||||
onCancel={() => {
|
||||
resolve(false)
|
||||
onClose()
|
||||
}}
|
||||
onContinue={() => {
|
||||
resolve(true)
|
||||
onClose()
|
||||
}}
|
||||
/>
|
||||
),
|
||||
onClose: () => {
|
||||
resolve(false)
|
||||
},
|
||||
})
|
||||
const shouldContinue = await new Promise<boolean>((resolve) => {
|
||||
addDialog({
|
||||
component: ({ onClose }) => (
|
||||
<ConfirmOpenDialog
|
||||
onCancel={() => {
|
||||
resolve(false)
|
||||
onClose()
|
||||
}}
|
||||
onContinue={() => {
|
||||
resolve(true)
|
||||
onClose()
|
||||
}}
|
||||
/>
|
||||
),
|
||||
onClose: () => {
|
||||
resolve(false)
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
return shouldContinue
|
||||
}
|
||||
return true
|
||||
return shouldContinue
|
||||
}
|
||||
|
||||
function ConfirmOpenDialog({
|
||||
|
@ -48,7 +43,6 @@ function ConfirmOpenDialog({
|
|||
onContinue: () => void
|
||||
}) {
|
||||
const msg = useTranslation()
|
||||
const [dontShowAgain, setDontShowAgain] = useState(false)
|
||||
return (
|
||||
<>
|
||||
<TldrawUiDialogHeader>
|
||||
|
@ -59,28 +53,10 @@ function ConfirmOpenDialog({
|
|||
{msg('file-system.confirm-open.description')}
|
||||
</TldrawUiDialogBody>
|
||||
<TldrawUiDialogFooter className="tlui-dialog__footer__actions">
|
||||
<TldrawUiButton
|
||||
type="normal"
|
||||
onClick={() => setDontShowAgain(!dontShowAgain)}
|
||||
style={{ marginRight: 'auto' }}
|
||||
>
|
||||
<TldrawUiButtonCheck checked={dontShowAgain} />
|
||||
<TldrawUiButtonLabel>
|
||||
{msg('file-system.confirm-open.dont-show-again')}
|
||||
</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
<TldrawUiButton type="normal" onClick={onCancel}>
|
||||
<TldrawUiButtonLabel>{msg('file-system.confirm-open.cancel')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
<TldrawUiButton
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
if (dontShowAgain) {
|
||||
userPreferences.showFileOpenWarning.set(false)
|
||||
}
|
||||
onContinue()
|
||||
}}
|
||||
>
|
||||
<TldrawUiButton type="primary" onClick={async () => onContinue()}>
|
||||
<TldrawUiButtonLabel>{msg('file-system.confirm-open.open')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDialogFooter>
|
||||
|
|
|
@ -215,6 +215,7 @@
|
|||
"menu.title": "Menu",
|
||||
"menu.copy-as": "Copy as",
|
||||
"menu.edit": "Edit",
|
||||
"menu.object": "Object",
|
||||
"menu.export-as": "Export as",
|
||||
"menu.file": "File",
|
||||
"menu.language": "Language",
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -180,7 +180,15 @@ export {
|
|||
DefaultMainMenu,
|
||||
type TLUiMainMenuProps,
|
||||
} from './lib/ui/components/MainMenu/DefaultMainMenu'
|
||||
export { DefaultMainMenuContent } from './lib/ui/components/MainMenu/DefaultMainMenuContent'
|
||||
export {
|
||||
DefaultMainMenuContent,
|
||||
EditSubmenu,
|
||||
ExportFileContentSubMenu,
|
||||
ExtrasGroup,
|
||||
ObjectSubmenu,
|
||||
PreferencesGroup,
|
||||
ViewSubmenu,
|
||||
} from './lib/ui/components/MainMenu/DefaultMainMenuContent'
|
||||
|
||||
export {
|
||||
DefaultQuickActions,
|
||||
|
|
|
@ -3,8 +3,6 @@ import {
|
|||
ArrangeMenuSubmenu,
|
||||
ClipboardMenuGroup,
|
||||
ConversionsMenuGroup,
|
||||
DeleteGroup,
|
||||
DuplicateMenuItem,
|
||||
EditLinkMenuItem,
|
||||
EmbedsGroup,
|
||||
FitFrameToContentMenuItem,
|
||||
|
@ -36,7 +34,6 @@ export function DefaultContextMenuContent() {
|
|||
<TldrawUiMenuGroup id="selection">
|
||||
<ToggleAutoSizeMenuItem />
|
||||
<EditLinkMenuItem />
|
||||
<DuplicateMenuItem />
|
||||
<GroupMenuItem />
|
||||
<UngroupMenuItem />
|
||||
<RemoveFrameMenuItem />
|
||||
|
@ -52,7 +49,6 @@ export function DefaultContextMenuContent() {
|
|||
<ClipboardMenuGroup />
|
||||
<ConversionsMenuGroup />
|
||||
<SetSelectionGroup />
|
||||
<DeleteGroup />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,8 +5,6 @@ import { LanguageMenu } from '../LanguageMenu'
|
|||
import {
|
||||
ClipboardMenuGroup,
|
||||
ConversionsMenuGroup,
|
||||
DeleteGroup,
|
||||
DuplicateMenuItem,
|
||||
EditLinkMenuItem,
|
||||
EmbedsGroup,
|
||||
FitFrameToContentMenuItem,
|
||||
|
@ -23,6 +21,7 @@ import {
|
|||
ToggleReduceMotionItem,
|
||||
ToggleSnapModeItem,
|
||||
ToggleToolLockItem,
|
||||
ToggleTransparentBgMenuItem,
|
||||
UngroupMenuItem,
|
||||
UnlockAllMenuItem,
|
||||
ZoomTo100MenuItem,
|
||||
|
@ -38,6 +37,7 @@ export function DefaultMainMenuContent() {
|
|||
return (
|
||||
<>
|
||||
<EditSubmenu />
|
||||
<ObjectSubmenu />
|
||||
<ViewSubmenu />
|
||||
<ExtrasGroup />
|
||||
<PreferencesGroup />
|
||||
|
@ -45,7 +45,26 @@ export function DefaultMainMenuContent() {
|
|||
)
|
||||
}
|
||||
|
||||
function EditSubmenu() {
|
||||
/** @public */
|
||||
export function ExportFileContentSubMenu() {
|
||||
const actions = useActions()
|
||||
|
||||
return (
|
||||
<TldrawUiMenuSubmenu id="export-as" label="context-menu.export-as" size="small">
|
||||
<TldrawUiMenuGroup id="export-as-group">
|
||||
<TldrawUiMenuItem {...actions['export-as-svg']} />
|
||||
<TldrawUiMenuItem {...actions['export-as-png']} />
|
||||
<TldrawUiMenuItem {...actions['export-as-json']} />
|
||||
</TldrawUiMenuGroup>
|
||||
<TldrawUiMenuGroup id="export-as-bg">
|
||||
<ToggleTransparentBgMenuItem />
|
||||
</TldrawUiMenuGroup>
|
||||
</TldrawUiMenuSubmenu>
|
||||
)
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function EditSubmenu() {
|
||||
const editor = useEditor()
|
||||
|
||||
const selectToolActive = useValue(
|
||||
|
@ -54,38 +73,70 @@ function EditSubmenu() {
|
|||
[editor]
|
||||
)
|
||||
|
||||
if (!selectToolActive) return null
|
||||
|
||||
return (
|
||||
<TldrawUiMenuSubmenu id="edit" label="menu.edit">
|
||||
<TldrawUiMenuSubmenu id="edit" label="menu.edit" disabled={!selectToolActive}>
|
||||
<UndoRedoGroup />
|
||||
<ClipboardMenuGroup />
|
||||
<ConversionsMenuGroup />
|
||||
<SetSelectionGroup />
|
||||
<SelectionMenuGroup />
|
||||
<EmbedsGroup />
|
||||
<DeleteGroup />
|
||||
</TldrawUiMenuSubmenu>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectionMenuGroup() {
|
||||
/** @public */
|
||||
export function ObjectSubmenu() {
|
||||
const editor = useEditor()
|
||||
|
||||
const selectToolActive = useValue(
|
||||
'isSelectToolActive',
|
||||
() => editor.getCurrentToolId() === 'select',
|
||||
[editor]
|
||||
)
|
||||
|
||||
return (
|
||||
<TldrawUiMenuGroup id="selection">
|
||||
<TldrawUiMenuSubmenu id="object" label="menu.object" disabled={!selectToolActive}>
|
||||
<ConversionsMenuGroup />
|
||||
<MultiShapeMenuGroup />
|
||||
<MiscMenuGroup />
|
||||
<EmbedsGroup />
|
||||
<LockGroup />
|
||||
</TldrawUiMenuSubmenu>
|
||||
)
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function MiscMenuGroup() {
|
||||
return (
|
||||
<TldrawUiMenuGroup id="misc">
|
||||
<ToggleAutoSizeMenuItem />
|
||||
<EditLinkMenuItem />
|
||||
<DuplicateMenuItem />
|
||||
<GroupMenuItem />
|
||||
<UngroupMenuItem />
|
||||
<RemoveFrameMenuItem />
|
||||
<FitFrameToContentMenuItem />
|
||||
</TldrawUiMenuGroup>
|
||||
)
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function LockGroup() {
|
||||
return (
|
||||
<TldrawUiMenuGroup id="lock">
|
||||
<ToggleLockMenuItem />
|
||||
<UnlockAllMenuItem />
|
||||
</TldrawUiMenuGroup>
|
||||
)
|
||||
}
|
||||
|
||||
function UndoRedoGroup() {
|
||||
/** @public */
|
||||
export function MultiShapeMenuGroup() {
|
||||
return (
|
||||
<TldrawUiMenuGroup id="multi-shape">
|
||||
<GroupMenuItem />
|
||||
<UngroupMenuItem />
|
||||
<RemoveFrameMenuItem />
|
||||
<FitFrameToContentMenuItem />
|
||||
</TldrawUiMenuGroup>
|
||||
)
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function UndoRedoGroup() {
|
||||
const actions = useActions()
|
||||
const canUndo = useCanUndo()
|
||||
const canRedo = useCanRedo()
|
||||
|
@ -97,7 +148,8 @@ function UndoRedoGroup() {
|
|||
)
|
||||
}
|
||||
|
||||
function ViewSubmenu() {
|
||||
/** @public */
|
||||
export function ViewSubmenu() {
|
||||
const actions = useActions()
|
||||
return (
|
||||
<TldrawUiMenuSubmenu id="view" label="menu.view">
|
||||
|
@ -112,7 +164,8 @@ function ViewSubmenu() {
|
|||
)
|
||||
}
|
||||
|
||||
function ExtrasGroup() {
|
||||
/** @public */
|
||||
export function ExtrasGroup() {
|
||||
const actions = useActions()
|
||||
return (
|
||||
<TldrawUiMenuGroup id="extras">
|
||||
|
@ -124,7 +177,8 @@ function ExtrasGroup() {
|
|||
|
||||
/* ------------------- Preferences ------------------ */
|
||||
|
||||
function PreferencesGroup() {
|
||||
/** @public */
|
||||
export function PreferencesGroup() {
|
||||
return (
|
||||
<TldrawUiMenuGroup id="preferences">
|
||||
<TldrawUiMenuSubmenu id="preferences" label="menu.preferences">
|
||||
|
|
|
@ -31,36 +31,36 @@ import { TldrawUiMenuSubmenu } from './primitives/menus/TldrawUiMenuSubmenu'
|
|||
export function ToggleAutoSizeMenuItem() {
|
||||
const actions = useActions()
|
||||
const shouldDisplay = useShowAutoSizeToggle()
|
||||
if (!shouldDisplay) return null
|
||||
return <TldrawUiMenuItem {...actions['toggle-auto-size']} />
|
||||
|
||||
return <TldrawUiMenuItem {...actions['toggle-auto-size']} disabled={!shouldDisplay} />
|
||||
}
|
||||
|
||||
export function EditLinkMenuItem() {
|
||||
const actions = useActions()
|
||||
const shouldDisplay = useHasLinkShapeSelected()
|
||||
if (!shouldDisplay) return null
|
||||
return <TldrawUiMenuItem {...actions['edit-link']} />
|
||||
|
||||
return <TldrawUiMenuItem {...actions['edit-link']} disabled={!shouldDisplay} />
|
||||
}
|
||||
|
||||
export function DuplicateMenuItem() {
|
||||
const actions = useActions()
|
||||
const shouldDisplay = useUnlockedSelectedShapesCount(1)
|
||||
if (!shouldDisplay) return null
|
||||
return <TldrawUiMenuItem {...actions['duplicate']} />
|
||||
|
||||
return <TldrawUiMenuItem {...actions['duplicate']} disabled={!shouldDisplay} />
|
||||
}
|
||||
|
||||
export function GroupMenuItem() {
|
||||
const actions = useActions()
|
||||
const shouldDisplay = useAllowGroup()
|
||||
if (!shouldDisplay) return null
|
||||
return <TldrawUiMenuItem {...actions['group']} />
|
||||
|
||||
return <TldrawUiMenuItem {...actions['group']} disabled={!shouldDisplay} />
|
||||
}
|
||||
|
||||
export function UngroupMenuItem() {
|
||||
const actions = useActions()
|
||||
const shouldDisplay = useAllowUngroup()
|
||||
if (!shouldDisplay) return null
|
||||
return <TldrawUiMenuItem {...actions['ungroup']} />
|
||||
|
||||
return <TldrawUiMenuItem {...actions['ungroup']} disabled={!shouldDisplay} />
|
||||
}
|
||||
|
||||
export function RemoveFrameMenuItem() {
|
||||
|
@ -75,8 +75,8 @@ export function RemoveFrameMenuItem() {
|
|||
},
|
||||
[editor]
|
||||
)
|
||||
if (!shouldDisplay) return null
|
||||
return <TldrawUiMenuItem {...actions['remove-frame']} />
|
||||
|
||||
return <TldrawUiMenuItem {...actions['remove-frame']} disabled={!shouldDisplay} />
|
||||
}
|
||||
|
||||
export function FitFrameToContentMenuItem() {
|
||||
|
@ -94,8 +94,8 @@ export function FitFrameToContentMenuItem() {
|
|||
},
|
||||
[editor]
|
||||
)
|
||||
if (!shouldDisplay) return null
|
||||
return <TldrawUiMenuItem {...actions['fit-frame-to-content']} />
|
||||
|
||||
return <TldrawUiMenuItem {...actions['fit-frame-to-content']} disabled={!shouldDisplay} />
|
||||
}
|
||||
|
||||
export function ToggleLockMenuItem() {
|
||||
|
@ -104,8 +104,8 @@ export function ToggleLockMenuItem() {
|
|||
const shouldDisplay = useValue('selected shapes', () => editor.getSelectedShapes().length > 0, [
|
||||
editor,
|
||||
])
|
||||
if (!shouldDisplay) return null
|
||||
return <TldrawUiMenuItem {...actions['toggle-lock']} />
|
||||
|
||||
return <TldrawUiMenuItem {...actions['toggle-lock']} disabled={!shouldDisplay} />
|
||||
}
|
||||
|
||||
export function ToggleTransparentBgMenuItem() {
|
||||
|
@ -125,8 +125,8 @@ export function UnlockAllMenuItem() {
|
|||
const shouldDisplay = useValue('any shapes', () => editor.getCurrentPageShapeIds().size > 0, [
|
||||
editor,
|
||||
])
|
||||
if (!shouldDisplay) return null
|
||||
return <TldrawUiMenuItem {...actions['unlock-all']} />
|
||||
|
||||
return <TldrawUiMenuItem {...actions['unlock-all']} disabled={!shouldDisplay} />
|
||||
}
|
||||
|
||||
/* ---------------------- Zoom ---------------------- */
|
||||
|
@ -174,11 +174,38 @@ export function ZoomToSelectionMenuItem() {
|
|||
/* -------------------- Clipboard ------------------- */
|
||||
|
||||
export function ClipboardMenuGroup() {
|
||||
const editor = useEditor()
|
||||
const actions = useActions()
|
||||
const atLeastOneShapeOnPage = useValue(
|
||||
'atLeastOneShapeOnPage',
|
||||
() => editor.getCurrentPageShapeIds().size > 0,
|
||||
[]
|
||||
)
|
||||
|
||||
return (
|
||||
<TldrawUiMenuGroup id="clipboard">
|
||||
<CutMenuItem />
|
||||
<CopyMenuItem />
|
||||
<TldrawUiMenuSubmenu
|
||||
id="copy-as"
|
||||
label="context-menu.copy-as"
|
||||
size="small"
|
||||
disabled={!atLeastOneShapeOnPage}
|
||||
>
|
||||
<TldrawUiMenuGroup id="copy-as-group">
|
||||
<TldrawUiMenuItem {...actions['copy-as-svg']} />
|
||||
{Boolean(window.navigator.clipboard?.write) && (
|
||||
<TldrawUiMenuItem {...actions['copy-as-png']} />
|
||||
)}
|
||||
<TldrawUiMenuItem {...actions['copy-as-json']} />
|
||||
</TldrawUiMenuGroup>
|
||||
<TldrawUiMenuGroup id="copy-as-bg">
|
||||
<ToggleTransparentBgMenuItem />
|
||||
</TldrawUiMenuGroup>
|
||||
</TldrawUiMenuSubmenu>
|
||||
<DuplicateMenuItem />
|
||||
<PasteMenuItem />
|
||||
<DeleteMenuItem />
|
||||
</TldrawUiMenuGroup>
|
||||
)
|
||||
}
|
||||
|
@ -186,22 +213,22 @@ export function ClipboardMenuGroup() {
|
|||
export function CutMenuItem() {
|
||||
const actions = useActions()
|
||||
const shouldDisplay = useUnlockedSelectedShapesCount(1)
|
||||
if (!shouldDisplay) return null
|
||||
return <TldrawUiMenuItem {...actions['cut']} />
|
||||
|
||||
return <TldrawUiMenuItem {...actions['cut']} disabled={!shouldDisplay} />
|
||||
}
|
||||
|
||||
export function CopyMenuItem() {
|
||||
const actions = useActions()
|
||||
const shouldDisplay = useAnySelectedShapesCount(1)
|
||||
if (!shouldDisplay) return null
|
||||
return <TldrawUiMenuItem {...actions['copy']} />
|
||||
|
||||
return <TldrawUiMenuItem {...actions['copy']} disabled={!shouldDisplay} />
|
||||
}
|
||||
|
||||
export function PasteMenuItem() {
|
||||
const actions = useActions()
|
||||
const shouldDisplay = showMenuPaste
|
||||
if (!shouldDisplay) return null
|
||||
return <TldrawUiMenuItem {...actions['paste']} />
|
||||
|
||||
return <TldrawUiMenuItem {...actions['paste']} disabled={!shouldDisplay} />
|
||||
}
|
||||
|
||||
/* ------------------- Conversions ------------------ */
|
||||
|
@ -214,23 +241,15 @@ export function ConversionsMenuGroup() {
|
|||
() => editor.getCurrentPageShapeIds().size > 0,
|
||||
[]
|
||||
)
|
||||
if (!atLeastOneShapeOnPage) return null
|
||||
|
||||
return (
|
||||
<TldrawUiMenuGroup id="conversions">
|
||||
<TldrawUiMenuSubmenu id="copy-as" label="context-menu.copy-as" size="small">
|
||||
<TldrawUiMenuGroup id="copy-as-group">
|
||||
<TldrawUiMenuItem {...actions['copy-as-svg']} />
|
||||
{Boolean(window.navigator.clipboard?.write) && (
|
||||
<TldrawUiMenuItem {...actions['copy-as-png']} />
|
||||
)}
|
||||
<TldrawUiMenuItem {...actions['copy-as-json']} />
|
||||
</TldrawUiMenuGroup>
|
||||
<TldrawUiMenuGroup id="copy-as-bg">
|
||||
<ToggleTransparentBgMenuItem />
|
||||
</TldrawUiMenuGroup>
|
||||
</TldrawUiMenuSubmenu>
|
||||
<TldrawUiMenuSubmenu id="export-as" label="context-menu.export-as" size="small">
|
||||
<TldrawUiMenuSubmenu
|
||||
id="export-as"
|
||||
label="context-menu.export-as"
|
||||
size="small"
|
||||
disabled={!atLeastOneShapeOnPage}
|
||||
>
|
||||
<TldrawUiMenuGroup id="export-as-group">
|
||||
<TldrawUiMenuItem {...actions['export-as-svg']} />
|
||||
<TldrawUiMenuItem {...actions['export-as-png']} />
|
||||
|
@ -254,25 +273,21 @@ export function SetSelectionGroup() {
|
|||
() => editor.getCurrentPageShapeIds().size > 0,
|
||||
[editor]
|
||||
)
|
||||
if (!atLeastOneShapeOnPage) return null
|
||||
|
||||
return (
|
||||
<TldrawUiMenuGroup id="set-selection-group">
|
||||
<TldrawUiMenuItem {...actions['select-all']} />
|
||||
<TldrawUiMenuItem {...actions['select-all']} disabled={!atLeastOneShapeOnPage} />
|
||||
</TldrawUiMenuGroup>
|
||||
)
|
||||
}
|
||||
|
||||
/* ------------------ Delete Group ------------------ */
|
||||
|
||||
export function DeleteGroup() {
|
||||
export function DeleteMenuItem() {
|
||||
const actions = useActions()
|
||||
const oneSelected = useUnlockedSelectedShapesCount(1)
|
||||
if (!oneSelected) return null
|
||||
return (
|
||||
<TldrawUiMenuGroup id="delete-group">
|
||||
<TldrawUiMenuItem {...actions['delete']} />
|
||||
</TldrawUiMenuGroup>
|
||||
)
|
||||
|
||||
return <TldrawUiMenuItem {...actions['delete']} disabled={!oneSelected} />
|
||||
}
|
||||
|
||||
/* --------------------- Modify --------------------- */
|
||||
|
@ -449,9 +464,13 @@ export function EmbedsGroup() {
|
|||
|
||||
return (
|
||||
<TldrawUiMenuGroup id="embeds">
|
||||
{oneEmbedSelected && <TldrawUiMenuItem {...actions['edit-embed']} />}
|
||||
{oneEmbedSelected && <TldrawUiMenuItem {...actions['convert-to-bookmark']} />}
|
||||
{oneEmbeddableBookmarkSelected && <TldrawUiMenuItem {...actions['convert-to-embed']} />}
|
||||
{/* XXX this doesn't exist?? */}
|
||||
{/* <TldrawUiMenuItem {...actions['edit-embed']} disabled={!oneEmbedSelected} /> */}
|
||||
<TldrawUiMenuItem {...actions['convert-to-bookmark']} disabled={!oneEmbedSelected} />
|
||||
<TldrawUiMenuItem
|
||||
{...actions['convert-to-embed']}
|
||||
disabled={!oneEmbeddableBookmarkSelected}
|
||||
/>
|
||||
</TldrawUiMenuGroup>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ export function TldrawUiDropdownMenuSubTrigger({
|
|||
disabled,
|
||||
}: TLUiDropdownMenuSubTriggerProps) {
|
||||
return (
|
||||
<_DropdownMenu.SubTrigger dir="ltr" asChild>
|
||||
<_DropdownMenu.SubTrigger dir="ltr" asChild disabled={disabled}>
|
||||
<TldrawUiButton
|
||||
type="menu"
|
||||
className="tlui-menu__submenu__trigger"
|
||||
|
|
|
@ -219,6 +219,7 @@ export type TLUiTranslationKey =
|
|||
| 'menu.title'
|
||||
| 'menu.copy-as'
|
||||
| 'menu.edit'
|
||||
| 'menu.object'
|
||||
| 'menu.export-as'
|
||||
| 'menu.file'
|
||||
| 'menu.language'
|
||||
|
|
|
@ -219,6 +219,7 @@ export const DEFAULT_TRANSLATION = {
|
|||
'menu.title': 'Menu',
|
||||
'menu.copy-as': 'Copy as',
|
||||
'menu.edit': 'Edit',
|
||||
'menu.object': 'Object',
|
||||
'menu.export-as': 'Export as',
|
||||
'menu.file': 'File',
|
||||
'menu.language': 'Language',
|
||||
|
|
Loading…
Reference in a new issue