UNPKG

tldraw

Version:

A tiny little drawing editor.

174 lines (173 loc) • 6.66 kB
import { Fragment, jsx, jsxs } from "react/jsx-runtime"; import { activeElementShouldCaptureKeys, preventDefault, tlmenus, useEditor, useEvent, useUniqueSafeId } from "@tldraw/editor"; import classNames from "classnames"; import { createContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import { PORTRAIT_BREAKPOINT } from "../../constants.mjs"; import { useBreakpoint } from "../../context/breakpoints.mjs"; import { areShortcutsDisabled } from "../../hooks/useKeyboardShortcuts.mjs"; import { useTranslation } from "../../hooks/useTranslation/useTranslation.mjs"; import { TldrawUiButtonIcon } from "../primitives/Button/TldrawUiButtonIcon.mjs"; import { TldrawUiPopover, TldrawUiPopoverContent, TldrawUiPopoverTrigger } from "../primitives/TldrawUiPopover.mjs"; import { TldrawUiToolbar, TldrawUiToolbarButton } from "../primitives/TldrawUiToolbar.mjs"; import { TldrawUiMenuContextProvider } from "../primitives/menus/TldrawUiMenuContext.mjs"; const IsInOverflowContext = createContext(false); const NUMBERED_SHORTCUT_KEYS = { "1": 0, "2": 1, "3": 2, "4": 3, "5": 4, "6": 5, "7": 6, "8": 7, "9": 8, "0": 9 }; function OverflowingToolbar({ children }) { const editor = useEditor(); const id = useUniqueSafeId(); const breakpoint = useBreakpoint(); const msg = useTranslation(); const rButtons = useRef([]); const [isOpen, setIsOpen] = useState(false); const overflowIndex = Math.min(8, 5 + breakpoint); const [totalItems, setTotalItems] = useState(0); const mainToolsRef = useRef(null); const [lastActiveOverflowItem, setLastActiveOverflowItem] = useState(null); const css = useMemo(() => { const activeCss = lastActiveOverflowItem ? `:not([data-value="${lastActiveOverflowItem}"])` : ""; return ` #${id}_main > *:nth-child(n + ${overflowIndex + (lastActiveOverflowItem ? 1 : 2)})${activeCss} { display: none; } #${id}_more > *:nth-child(-n + ${overflowIndex}) { display: none; } #${id}_more > *:nth-child(-n + ${overflowIndex + 4}) { margin-top: 0; } `; }, [lastActiveOverflowItem, id, overflowIndex]); const onDomUpdate = useEvent(() => { if (!mainToolsRef.current) return; const children2 = Array.from(mainToolsRef.current.children); setTotalItems(children2.length); const lastActiveElementIdx = children2.findIndex( (el) => el.getAttribute("data-value") === lastActiveOverflowItem ); if (lastActiveElementIdx <= overflowIndex) { setLastActiveOverflowItem(null); } const activeElementIdx = Array.from(mainToolsRef.current.children).findIndex( (el) => el.getAttribute("aria-pressed") === "true" ); if (activeElementIdx === -1) return; if (activeElementIdx >= overflowIndex) { setLastActiveOverflowItem(children2[activeElementIdx].getAttribute("data-value")); } rButtons.current = Array.from(mainToolsRef.current?.children ?? []).filter( (el) => { if (!(el instanceof HTMLElement)) return false; if (el.tagName.toLowerCase() !== "button") return false; return !!(el.offsetWidth || el.offsetHeight); } ); }); useLayoutEffect(() => { onDomUpdate(); }); useLayoutEffect(() => { if (!mainToolsRef.current) return; const mutationObserver = new MutationObserver(onDomUpdate); mutationObserver.observe(mainToolsRef.current, { childList: true, subtree: true, attributeFilter: ["data-value", "aria-pressed"] }); return () => { mutationObserver.disconnect(); }; }, [onDomUpdate]); useEffect(() => { if (!editor.options.enableToolbarKeyboardShortcuts) return; function handleKeyDown(event) { if (areShortcutsDisabled(editor) || activeElementShouldCaptureKeys( true /* allow buttons */ )) return; if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) return; const index = NUMBERED_SHORTCUT_KEYS[event.key]; if (typeof index === "number") { preventDefault(event); rButtons.current[index]?.click(); } } document.addEventListener("keydown", handleKeyDown); return () => { document.removeEventListener("keydown", handleKeyDown); }; }, [editor]); const popoverId = "toolbar overflow"; return /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx("style", { nonce: editor.options.nonce, children: css }), /* @__PURE__ */ jsxs( TldrawUiToolbar, { className: classNames("tlui-toolbar__tools", { "tlui-toolbar__tools__mobile": breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM }), label: msg("tool-panel.title"), children: [ /* @__PURE__ */ jsx("div", { id: `${id}_main`, ref: mainToolsRef, className: "tlui-toolbar__tools__list", children: /* @__PURE__ */ jsx(TldrawUiMenuContextProvider, { type: "toolbar", sourceId: "toolbar", children }) }), totalItems > overflowIndex + 1 && /* @__PURE__ */ jsx(IsInOverflowContext.Provider, { value: true, children: /* @__PURE__ */ jsxs(TldrawUiPopover, { id: popoverId, open: isOpen, onOpenChange: setIsOpen, children: [ /* @__PURE__ */ jsx(TldrawUiPopoverTrigger, { children: /* @__PURE__ */ jsx( TldrawUiToolbarButton, { title: msg("tool-panel.more"), type: "tool", className: "tlui-toolbar__overflow", "data-testid": "tools.more-button", children: /* @__PURE__ */ jsx(TldrawUiButtonIcon, { icon: "chevron-up" }) } ) }), /* @__PURE__ */ jsx(TldrawUiPopoverContent, { side: "top", align: "center", children: /* @__PURE__ */ jsx( TldrawUiToolbar, { className: "tlui-buttons__grid", "data-testid": "tools.more-content", label: msg("tool-panel.more"), id: `${id}_more`, onClick: () => { tlmenus.deleteOpenMenu(popoverId, editor.contextId); setIsOpen(false); }, children: /* @__PURE__ */ jsx(TldrawUiMenuContextProvider, { type: "toolbar-overflow", sourceId: "toolbar", children }) } ) }) ] }) }) ] } ) ] }); } const isActiveTLUiToolItem = (item, activeToolId, geoState) => { return item.meta?.geo ? activeToolId === "geo" && geoState === item.meta?.geo : activeToolId === item.id; }; export { IsInOverflowContext, OverflowingToolbar, isActiveTLUiToolItem }; //# sourceMappingURL=OverflowingToolbar.mjs.map