UNPKG

tldraw

Version:

A tiny little drawing editor.

150 lines (149 loc) 5.92 kB
import { Fragment, jsx, jsxs } from "react/jsx-runtime"; import { preventDefault, useEditor, useEvent, useUniqueSafeId } from "@tldraw/editor"; import classNames from "classnames"; import hotkeys from "hotkeys-js"; 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 { TldrawUiButton } from "../primitives/Button/TldrawUiButton.mjs"; import { TldrawUiButtonIcon } from "../primitives/Button/TldrawUiButtonIcon.mjs"; import { TldrawUiDropdownMenuContent, TldrawUiDropdownMenuRoot, TldrawUiDropdownMenuTrigger } from "../primitives/TldrawUiDropdownMenu.mjs"; import { TldrawUiMenuContextProvider } from "../primitives/menus/TldrawUiMenuContext.mjs"; const IsInOverflowContext = createContext(false); function OverflowingToolbar({ children }) { const editor = useEditor(); const id = useUniqueSafeId(); const breakpoint = useBreakpoint(); const msg = useTranslation(); 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; } `; }, [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-checked") === "true" ); if (activeElementIdx === -1) return; if (activeElementIdx >= overflowIndex) { setLastActiveOverflowItem(children2[activeElementIdx].getAttribute("data-value")); } }); useLayoutEffect(() => { onDomUpdate(); }); useLayoutEffect(() => { if (!mainToolsRef.current) return; const mutationObserver = new MutationObserver(onDomUpdate); mutationObserver.observe(mainToolsRef.current, { childList: true, subtree: true, attributeFilter: ["data-value", "aria-checked"] }); return () => { mutationObserver.disconnect(); }; }, [onDomUpdate]); useEffect(() => { const keys = [ ["1", 0], ["2", 1], ["3", 2], ["4", 3], ["5", 4], ["6", 5], ["7", 6], ["8", 7], ["9", 8], ["0", 9] ]; for (const [key, index] of keys) { hotkeys(key, (event) => { if (areShortcutsDisabled(editor)) return; preventDefault(event); const relevantEls = Array.from(mainToolsRef.current?.children ?? []).filter( (el2) => { if (!(el2 instanceof HTMLElement)) return false; if (el2.tagName.toLowerCase() !== "button") return false; return !!(el2.offsetWidth || el2.offsetHeight); } ); const el = relevantEls[index]; if (el) el.click(); }); } return () => { hotkeys.unbind("1,2,3,4,5,6,7,8,9,0"); }; }, [editor]); return /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx("style", { children: css }), /* @__PURE__ */ jsxs( "div", { className: classNames("tlui-toolbar__tools", { "tlui-toolbar__tools__mobile": breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM }), role: "radiogroup", 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(TldrawUiDropdownMenuRoot, { id: "toolbar overflow", modal: false, children: [ /* @__PURE__ */ jsx(TldrawUiDropdownMenuTrigger, { children: /* @__PURE__ */ jsx( TldrawUiButton, { 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(TldrawUiDropdownMenuContent, { side: "top", align: "center", children: /* @__PURE__ */ jsx( "div", { className: "tlui-buttons__grid", "data-testid": "tools.more-content", id: `${id}_more`, 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