UNPKG

tldraw

Version:

A tiny little drawing editor.

530 lines (529 loc) • 21.1 kB
import { jsx, jsxs } from "react/jsx-runtime"; import { useEditor, useValue } from "@tldraw/editor"; import { useUiEvents } from "../context/events.mjs"; import { useToasts } from "../context/toasts.mjs"; import { showMenuPaste, useAllowGroup, useAllowUngroup, useAnySelectedShapesCount, useHasLinkShapeSelected, useOnlyFlippableShape, useShowAutoSizeToggle, useThreeStackableItems, useUnlockedSelectedShapesCount } from "../hooks/menu-hooks.mjs"; import { useGetEmbedDefinition } from "../hooks/useGetEmbedDefinition.mjs"; import { useReadonly } from "../hooks/useReadonly.mjs"; import { TldrawUiMenuActionCheckboxItem } from "./primitives/menus/TldrawUiMenuActionCheckboxItem.mjs"; import { TldrawUiMenuActionItem } from "./primitives/menus/TldrawUiMenuActionItem.mjs"; import { TldrawUiMenuGroup } from "./primitives/menus/TldrawUiMenuGroup.mjs"; import { TldrawUiMenuItem } from "./primitives/menus/TldrawUiMenuItem.mjs"; import { TldrawUiMenuSubmenu } from "./primitives/menus/TldrawUiMenuSubmenu.mjs"; function ToggleAutoSizeMenuItem() { const shouldDisplay = useShowAutoSizeToggle(); if (!shouldDisplay) return null; return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "toggle-auto-size" }); } function EditLinkMenuItem() { const shouldDisplay = useHasLinkShapeSelected(); if (!shouldDisplay) return null; return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "edit-link" }); } function DuplicateMenuItem() { const shouldDisplay = useUnlockedSelectedShapesCount(1); if (!shouldDisplay) return null; return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "duplicate" }); } function FlattenMenuItem() { const editor = useEditor(); const shouldDisplay = useValue( "should display flatten option", () => { const selectedShapeIds = editor.getSelectedShapeIds(); if (selectedShapeIds.length === 0) return false; const onlySelectedShape = editor.getOnlySelectedShape(); if (onlySelectedShape && editor.isShapeOfType(onlySelectedShape, "image")) { return false; } return true; }, [editor] ); if (!shouldDisplay) return null; return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "flatten-to-image" }); } function GroupMenuItem() { const shouldDisplay = useAllowGroup(); if (!shouldDisplay) return null; return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "group" }); } function UngroupMenuItem() { const shouldDisplay = useAllowUngroup(); if (!shouldDisplay) return null; return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "ungroup" }); } function RemoveFrameMenuItem() { const editor = useEditor(); const shouldDisplay = useValue( "allow unframe", () => { const selectedShapes = editor.getSelectedShapes(); if (selectedShapes.length === 0) return false; return selectedShapes.every((shape) => editor.isShapeOfType(shape, "frame")); }, [editor] ); if (!shouldDisplay) return null; return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "remove-frame" }); } function FitFrameToContentMenuItem() { const editor = useEditor(); const shouldDisplay = useValue( "allow fit frame to content", () => { const onlySelectedShape = editor.getOnlySelectedShape(); if (!onlySelectedShape) return false; return editor.isShapeOfType(onlySelectedShape, "frame") && editor.getSortedChildIdsForParent(onlySelectedShape).length > 0; }, [editor] ); if (!shouldDisplay) return null; return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "fit-frame-to-content" }); } function ToggleLockMenuItem() { const editor = useEditor(); const shouldDisplay = useValue("selected shapes", () => editor.getSelectedShapes().length > 0, [ editor ]); if (!shouldDisplay) return null; return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "toggle-lock" }); } function ToggleTransparentBgMenuItem() { const editor = useEditor(); const isTransparentBg = useValue( "isTransparentBg", () => !editor.getInstanceState().exportBackground, [editor] ); return /* @__PURE__ */ jsx( TldrawUiMenuActionCheckboxItem, { actionId: "toggle-transparent", checked: isTransparentBg, toggle: true } ); } function UnlockAllMenuItem() { const editor = useEditor(); const shouldDisplay = useValue("any shapes", () => editor.getCurrentPageShapeIds().size > 0, [ editor ]); return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "unlock-all", disabled: !shouldDisplay }); } function ZoomTo100MenuItem() { const editor = useEditor(); const isZoomedTo100 = useValue("zoomed to 100", () => editor.getZoomLevel() === 1, [editor]); return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "zoom-to-100", noClose: true, disabled: isZoomedTo100 }); } function ZoomToFitMenuItem() { const editor = useEditor(); const hasShapes = useValue("has shapes", () => editor.getCurrentPageShapeIds().size > 0, [editor]); return /* @__PURE__ */ jsx( TldrawUiMenuActionItem, { actionId: "zoom-to-fit", disabled: !hasShapes, "data-testid": "minimap.zoom-menu.zoom-to-fit", noClose: true } ); } function ZoomToSelectionMenuItem() { const editor = useEditor(); const hasSelected = useValue("has shapes", () => editor.getSelectedShapeIds().length > 0, [ editor ]); return /* @__PURE__ */ jsx( TldrawUiMenuActionItem, { actionId: "zoom-to-selection", disabled: !hasSelected, "data-testid": "minimap.zoom-menu.zoom-to-selection", noClose: true } ); } function ClipboardMenuGroup() { return /* @__PURE__ */ jsxs(TldrawUiMenuGroup, { id: "clipboard", children: [ /* @__PURE__ */ jsx(CutMenuItem, {}), /* @__PURE__ */ jsx(CopyMenuItem, {}), /* @__PURE__ */ jsx(PasteMenuItem, {}), /* @__PURE__ */ jsx(DuplicateMenuItem, {}), /* @__PURE__ */ jsx(DeleteMenuItem, {}) ] }); } function CopyAsMenuGroup() { const editor = useEditor(); const atLeastOneShapeOnPage = useValue( "atLeastOneShapeOnPage", () => editor.getCurrentPageShapeIds().size > 0, [editor] ); return /* @__PURE__ */ jsxs( TldrawUiMenuSubmenu, { id: "copy-as", label: "context-menu.copy-as", size: "small", disabled: !atLeastOneShapeOnPage, children: [ /* @__PURE__ */ jsxs(TldrawUiMenuGroup, { id: "copy-as-group", children: [ /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "copy-as-svg" }), Boolean(window.navigator.clipboard?.write) && /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "copy-as-png" }), /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "copy-as-json" }) ] }), /* @__PURE__ */ jsx(TldrawUiMenuGroup, { id: "copy-as-bg", children: /* @__PURE__ */ jsx(ToggleTransparentBgMenuItem, {}) }) ] } ); } function CutMenuItem() { const shouldDisplay = useUnlockedSelectedShapesCount(1); return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "cut", disabled: !shouldDisplay }); } function CopyMenuItem() { const shouldDisplay = useAnySelectedShapesCount(1); return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "copy", disabled: !shouldDisplay }); } function PasteMenuItem() { const shouldDisplay = showMenuPaste; return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "paste", disabled: !shouldDisplay }); } function ConversionsMenuGroup() { const editor = useEditor(); const atLeastOneShapeOnPage = useValue( "atLeastOneShapeOnPage", () => editor.getCurrentPageShapeIds().size > 0, [editor] ); if (!atLeastOneShapeOnPage) return null; return /* @__PURE__ */ jsxs(TldrawUiMenuGroup, { id: "conversions", children: [ /* @__PURE__ */ jsx(CopyAsMenuGroup, {}), /* @__PURE__ */ jsxs(TldrawUiMenuSubmenu, { id: "export-as", label: "context-menu.export-as", size: "small", children: [ /* @__PURE__ */ jsxs(TldrawUiMenuGroup, { id: "export-as-group", children: [ /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "export-as-svg" }), /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "export-as-png" }), /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "export-as-json" }) ] }), /* @__PURE__ */ jsx(TldrawUiMenuGroup, { id: "export-as-bg", children: /* @__PURE__ */ jsx(ToggleTransparentBgMenuItem, {}) }) ] }) ] }); } function SelectAllMenuItem() { const editor = useEditor(); const atLeastOneShapeOnPage = useValue( "atLeastOneShapeOnPage", () => editor.getCurrentPageShapeIds().size > 0, [editor] ); return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "select-all", disabled: !atLeastOneShapeOnPage }); } function DeleteMenuItem() { const oneSelected = useUnlockedSelectedShapesCount(1); return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "delete", disabled: !oneSelected }); } function EditMenuSubmenu() { const isReadonlyMode = useReadonly(); if (!useAnySelectedShapesCount(1)) return null; if (isReadonlyMode) return null; return /* @__PURE__ */ jsxs(TldrawUiMenuSubmenu, { id: "edit", label: "context-menu.edit", size: "small", children: [ /* @__PURE__ */ jsx(GroupMenuItem, {}), /* @__PURE__ */ jsx(UngroupMenuItem, {}), /* @__PURE__ */ jsx(FlattenMenuItem, {}), /* @__PURE__ */ jsx(EditLinkMenuItem, {}), /* @__PURE__ */ jsx(FitFrameToContentMenuItem, {}), /* @__PURE__ */ jsx(RemoveFrameMenuItem, {}), /* @__PURE__ */ jsx(ConvertToEmbedMenuItem, {}), /* @__PURE__ */ jsx(ConvertToBookmarkMenuItem, {}), /* @__PURE__ */ jsx(ToggleAutoSizeMenuItem, {}), /* @__PURE__ */ jsx(ToggleLockMenuItem, {}) ] }); } function ArrangeMenuSubmenu() { const twoSelected = useUnlockedSelectedShapesCount(2); const onlyFlippableShapeSelected = useOnlyFlippableShape(); const isReadonlyMode = useReadonly(); if (isReadonlyMode) return null; if (!(twoSelected || onlyFlippableShapeSelected)) return null; return /* @__PURE__ */ jsxs(TldrawUiMenuSubmenu, { id: "arrange", label: "context-menu.arrange", size: "small", children: [ twoSelected && /* @__PURE__ */ jsxs(TldrawUiMenuGroup, { id: "align", children: [ /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "align-left" }), /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "align-center-horizontal" }), /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "align-right" }), /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "align-top" }), /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "align-center-vertical" }), /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "align-bottom" }) ] }), /* @__PURE__ */ jsx(DistributeMenuGroup, {}), twoSelected && /* @__PURE__ */ jsxs(TldrawUiMenuGroup, { id: "stretch", children: [ /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "stretch-horizontal" }), /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "stretch-vertical" }) ] }), (twoSelected || onlyFlippableShapeSelected) && /* @__PURE__ */ jsxs(TldrawUiMenuGroup, { id: "flip", children: [ /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "flip-horizontal" }), /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "flip-vertical" }) ] }), /* @__PURE__ */ jsx(OrderMenuGroup, {}) ] }); } function DistributeMenuGroup() { const threeSelected = useUnlockedSelectedShapesCount(3); if (!threeSelected) return null; return /* @__PURE__ */ jsxs(TldrawUiMenuGroup, { id: "distribute", children: [ /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "distribute-horizontal" }), /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "distribute-vertical" }) ] }); } function OrderMenuGroup() { const twoSelected = useUnlockedSelectedShapesCount(2); const threeStackableItems = useThreeStackableItems(); if (!twoSelected) return null; return /* @__PURE__ */ jsxs(TldrawUiMenuGroup, { id: "order", children: [ /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "pack" }), threeStackableItems && /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "stack-horizontal" }), threeStackableItems && /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "stack-vertical" }) ] }); } function ReorderMenuSubmenu() { const isReadonlyMode = useReadonly(); const oneSelected = useUnlockedSelectedShapesCount(1); if (isReadonlyMode) return null; if (!oneSelected) return null; return /* @__PURE__ */ jsx(TldrawUiMenuSubmenu, { id: "reorder", label: "context-menu.reorder", size: "small", children: /* @__PURE__ */ jsxs(TldrawUiMenuGroup, { id: "reorder", children: [ /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "bring-to-front" }), /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "bring-forward" }), /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "send-backward" }), /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "send-to-back" }) ] }) }); } function MoveToPageMenu() { const editor = useEditor(); const pages = useValue("pages", () => editor.getPages(), [editor]); const currentPageId = useValue("current page id", () => editor.getCurrentPageId(), [editor]); const { addToast } = useToasts(); const trackEvent = useUiEvents(); const isReadonlyMode = useReadonly(); const oneSelected = useUnlockedSelectedShapesCount(1); if (!oneSelected) return null; if (isReadonlyMode) return null; return /* @__PURE__ */ jsxs(TldrawUiMenuSubmenu, { id: "move-to-page", label: "context-menu.move-to-page", size: "small", children: [ /* @__PURE__ */ jsx(TldrawUiMenuGroup, { id: "pages", children: pages.map((page) => /* @__PURE__ */ jsx( TldrawUiMenuItem, { id: page.id, disabled: currentPageId === page.id, label: page.name.length > 30 ? `${page.name.slice(0, 30)}\u2026` : page.name, onSelect: () => { editor.markHistoryStoppingPoint("move_shapes_to_page"); editor.moveShapesToPage(editor.getSelectedShapeIds(), page.id); const toPage = editor.getPage(page.id); if (toPage) { addToast({ title: "Changed Page", description: `Moved to ${toPage.name}.`, actions: [ { label: "Go Back", type: "primary", onClick: () => { editor.markHistoryStoppingPoint("change-page"); editor.setCurrentPage(currentPageId); } } ] }); } trackEvent("move-to-page", { source: "context-menu" }); } }, page.id )) }), /* @__PURE__ */ jsx(TldrawUiMenuGroup, { id: "new-page", children: /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "move-to-new-page" }) }) ] }); } function ConvertToBookmarkMenuItem() { const editor = useEditor(); const oneEmbedSelected = useValue( "oneEmbedSelected", () => { const onlySelectedShape = editor.getOnlySelectedShape(); if (!onlySelectedShape) return false; return !!(editor.isShapeOfType(onlySelectedShape, "embed") && onlySelectedShape.props.url && !editor.isShapeOrAncestorLocked(onlySelectedShape)); }, [editor] ); if (!oneEmbedSelected) return null; return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "convert-to-bookmark" }); } function ConvertToEmbedMenuItem() { const editor = useEditor(); const getEmbedDefinition = useGetEmbedDefinition(); const oneEmbeddableBookmarkSelected = useValue( "oneEmbeddableBookmarkSelected", () => { const onlySelectedShape = editor.getOnlySelectedShape(); if (!onlySelectedShape) return false; return !!(editor.isShapeOfType(onlySelectedShape, "bookmark") && onlySelectedShape.props.url && getEmbedDefinition(onlySelectedShape.props.url) && !editor.isShapeOrAncestorLocked(onlySelectedShape)); }, [editor] ); if (!oneEmbeddableBookmarkSelected) return null; return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "convert-to-embed" }); } function ToggleSnapModeItem() { const editor = useEditor(); const isSnapMode = useValue("isSnapMode", () => editor.user.getIsSnapMode(), [editor]); return /* @__PURE__ */ jsx(TldrawUiMenuActionCheckboxItem, { actionId: "toggle-snap-mode", checked: isSnapMode }); } function ToggleToolLockItem() { const editor = useEditor(); const isToolLock = useValue("isToolLock", () => editor.getInstanceState().isToolLocked, [editor]); return /* @__PURE__ */ jsx(TldrawUiMenuActionCheckboxItem, { actionId: "toggle-tool-lock", checked: isToolLock }); } function ToggleGridItem() { const editor = useEditor(); const isGridMode = useValue("isGridMode", () => editor.getInstanceState().isGridMode, [editor]); return /* @__PURE__ */ jsx(TldrawUiMenuActionCheckboxItem, { actionId: "toggle-grid", checked: isGridMode }); } function ToggleWrapModeItem() { const editor = useEditor(); const isWrapMode = useValue("isWrapMode", () => editor.user.getIsWrapMode(), [editor]); return /* @__PURE__ */ jsx(TldrawUiMenuActionCheckboxItem, { actionId: "toggle-wrap-mode", checked: isWrapMode }); } function ToggleDarkModeItem() { const editor = useEditor(); const isDarkMode = useValue("isDarkMode", () => editor.user.getIsDarkMode(), [editor]); return /* @__PURE__ */ jsx(TldrawUiMenuActionCheckboxItem, { actionId: "toggle-dark-mode", checked: isDarkMode }); } function ToggleFocusModeItem() { const editor = useEditor(); const isFocusMode = useValue("isFocusMode", () => editor.getInstanceState().isFocusMode, [editor]); return /* @__PURE__ */ jsx(TldrawUiMenuActionCheckboxItem, { actionId: "toggle-focus-mode", checked: isFocusMode }); } function ToggleEdgeScrollingItem() { const editor = useEditor(); const edgeScrollSpeed = useValue("edgeScrollSpeed", () => editor.user.getEdgeScrollSpeed(), [ editor ]); return /* @__PURE__ */ jsx( TldrawUiMenuActionCheckboxItem, { actionId: "toggle-edge-scrolling", checked: edgeScrollSpeed === 1 } ); } function ToggleReduceMotionItem() { const editor = useEditor(); const animationSpeed = useValue("animationSpeed", () => editor.user.getAnimationSpeed(), [editor]); return /* @__PURE__ */ jsx( TldrawUiMenuActionCheckboxItem, { actionId: "toggle-reduce-motion", checked: animationSpeed === 0 } ); } function ToggleDebugModeItem() { const editor = useEditor(); const isDebugMode = useValue("isDebugMode", () => editor.getInstanceState().isDebugMode, [editor]); return /* @__PURE__ */ jsx(TldrawUiMenuActionCheckboxItem, { actionId: "toggle-debug-mode", checked: isDebugMode }); } function ToggleDynamicSizeModeItem() { const editor = useEditor(); const isDynamicResizeMode = useValue( "dynamic resize", () => editor.user.getIsDynamicResizeMode(), [editor] ); return /* @__PURE__ */ jsx( TldrawUiMenuActionCheckboxItem, { actionId: "toggle-dynamic-size-mode", checked: isDynamicResizeMode } ); } function TogglePasteAtCursorItem() { const editor = useEditor(); const pasteAtCursor = useValue("paste at cursor", () => editor.user.getIsPasteAtCursorMode(), [ editor ]); return /* @__PURE__ */ jsx(TldrawUiMenuActionCheckboxItem, { actionId: "toggle-paste-at-cursor", checked: pasteAtCursor }); } function PrintItem() { const editor = useEditor(); const emptyPage = useValue("emptyPage", () => editor.getCurrentPageShapeIds().size === 0, [ editor ]); return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "print", disabled: emptyPage }); } function CursorChatItem() { const editor = useEditor(); const shouldShow = useValue( "show cursor chat", () => editor.getCurrentToolId() === "select" && !editor.getInstanceState().isCoarsePointer, [editor] ); if (!shouldShow) return null; return /* @__PURE__ */ jsx(TldrawUiMenuActionItem, { actionId: "open-cursor-chat" }); } export { ArrangeMenuSubmenu, ClipboardMenuGroup, ConversionsMenuGroup, ConvertToBookmarkMenuItem, ConvertToEmbedMenuItem, CopyAsMenuGroup, CopyMenuItem, CursorChatItem, CutMenuItem, DeleteMenuItem, DuplicateMenuItem, EditLinkMenuItem, EditMenuSubmenu, FitFrameToContentMenuItem, FlattenMenuItem, GroupMenuItem, MoveToPageMenu, PasteMenuItem, PrintItem, RemoveFrameMenuItem, ReorderMenuSubmenu, SelectAllMenuItem, ToggleAutoSizeMenuItem, ToggleDarkModeItem, ToggleDebugModeItem, ToggleDynamicSizeModeItem, ToggleEdgeScrollingItem, ToggleFocusModeItem, ToggleGridItem, ToggleLockMenuItem, TogglePasteAtCursorItem, ToggleReduceMotionItem, ToggleSnapModeItem, ToggleToolLockItem, ToggleTransparentBgMenuItem, ToggleWrapModeItem, UngroupMenuItem, UnlockAllMenuItem, ZoomTo100MenuItem, ZoomToFitMenuItem, ZoomToSelectionMenuItem }; //# sourceMappingURL=menu-items.mjs.map