tldraw
Version:
A tiny little drawing editor.
530 lines (529 loc) • 21.1 kB
JavaScript
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