tldraw 4.3.0-canary.b8f81b08d169 → 4.3.0-canary.b9cf1eed518f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/index.d.ts +12 -0
- package/dist-cjs/index.js +2 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/canvas/TldrawSelectionForeground.js +2 -2
- package/dist-cjs/lib/canvas/TldrawSelectionForeground.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js +9 -12
- package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/arrow-types.js.map +1 -1
- package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js +3 -3
- package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +1 -1
- package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js +10 -6
- package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js +1 -1
- package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +5 -5
- package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/HyperlinkButton.js +2 -1
- package/dist-cjs/lib/shapes/shared/HyperlinkButton.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +14 -2
- package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +3 -3
- package/dist-cjs/lib/shapes/shared/RichTextLabel.js +11 -3
- package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +3 -3
- package/dist-cjs/lib/shapes/shared/ShapeFill.js +2 -2
- package/dist-cjs/lib/shapes/shared/ShapeFill.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/{useForceSolid.js → useEfficientZoomThreshold.js} +10 -7
- package/dist-cjs/lib/shapes/shared/useEfficientZoomThreshold.js.map +7 -0
- package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js +1 -1
- package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js.map +2 -2
- package/dist-cjs/lib/shapes/text/TextShapeUtil.js +5 -2
- package/dist-cjs/lib/shapes/text/TextShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/video/VideoShapeUtil.js +1 -1
- package/dist-cjs/lib/shapes/video/VideoShapeUtil.js.map +2 -2
- package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.js +3 -9
- package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/ZoomMenu/DefaultZoomMenu.js +1 -1
- package/dist-cjs/lib/ui/components/ZoomMenu/DefaultZoomMenu.js.map +2 -2
- package/dist-cjs/lib/ui/components/menu-items.js +3 -1
- package/dist-cjs/lib/ui/components/menu-items.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js +1 -1
- package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +143 -88
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +1 -1
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
- package/dist-cjs/lib/ui/version.js +3 -3
- package/dist-cjs/lib/ui/version.js.map +1 -1
- package/dist-cjs/lib/utils/text/richText.js +7 -17
- package/dist-cjs/lib/utils/text/richText.js.map +3 -3
- package/dist-esm/index.d.mts +12 -0
- package/dist-esm/index.mjs +3 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs +2 -2
- package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs +10 -14
- package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs +3 -3
- package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +1 -1
- package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +10 -6
- package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs +1 -1
- package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +5 -5
- package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/HyperlinkButton.mjs +3 -2
- package/dist-esm/lib/shapes/shared/HyperlinkButton.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +14 -2
- package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/RichTextLabel.mjs +11 -3
- package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/ShapeFill.mjs +2 -2
- package/dist-esm/lib/shapes/shared/ShapeFill.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/useEfficientZoomThreshold.mjs +12 -0
- package/dist-esm/lib/shapes/shared/useEfficientZoomThreshold.mjs.map +7 -0
- package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs +1 -1
- package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs.map +2 -2
- package/dist-esm/lib/shapes/text/TextShapeUtil.mjs +5 -2
- package/dist-esm/lib/shapes/text/TextShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs +1 -1
- package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.mjs +2 -8
- package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/ZoomMenu/DefaultZoomMenu.mjs +1 -1
- package/dist-esm/lib/ui/components/ZoomMenu/DefaultZoomMenu.mjs.map +2 -2
- package/dist-esm/lib/ui/components/menu-items.mjs +3 -1
- package/dist-esm/lib/ui/components/menu-items.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +151 -90
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
- package/dist-esm/lib/ui/version.mjs +3 -3
- package/dist-esm/lib/ui/version.mjs.map +1 -1
- package/dist-esm/lib/utils/text/richText.mjs +3 -3
- package/dist-esm/lib/utils/text/richText.mjs.map +2 -2
- package/package.json +3 -3
- package/src/index.ts +1 -0
- package/src/lib/canvas/TldrawSelectionForeground.tsx +2 -2
- package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +10 -12
- package/src/lib/shapes/arrow/arrow-types.ts +2 -0
- package/src/lib/shapes/draw/DrawShapeUtil.tsx +3 -3
- package/src/lib/shapes/frame/FrameShapeUtil.tsx +1 -1
- package/src/lib/shapes/geo/GeoShapeUtil.tsx +9 -4
- package/src/lib/shapes/highlight/HighlightShapeUtil.tsx +1 -1
- package/src/lib/shapes/note/NoteShapeUtil.tsx +7 -8
- package/src/lib/shapes/shared/HyperlinkButton.tsx +3 -2
- package/src/lib/shapes/shared/PlainTextLabel.tsx +10 -1
- package/src/lib/shapes/shared/RichTextLabel.tsx +11 -2
- package/src/lib/shapes/shared/ShapeFill.tsx +2 -2
- package/src/lib/shapes/shared/useEfficientZoomThreshold.ts +10 -0
- package/src/lib/shapes/shared/useImageOrVideoAsset.ts +1 -1
- package/src/lib/shapes/text/TextShapeUtil.tsx +5 -0
- package/src/lib/shapes/video/VideoShapeUtil.tsx +2 -1
- package/src/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.tsx +1 -9
- package/src/lib/ui/components/ZoomMenu/DefaultZoomMenu.tsx +1 -1
- package/src/lib/ui/components/menu-items.tsx +3 -1
- package/src/lib/ui/components/primitives/TldrawUiSlider.tsx +2 -2
- package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +196 -108
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +2 -2
- package/src/lib/ui/version.ts +3 -3
- package/src/lib/utils/text/richText.ts +3 -3
- package/src/test/TldrawEditor.test.tsx +3 -2
- package/src/test/commands/__snapshots__/getSvgString.test.ts.snap +2 -2
- package/src/test/commands/cameraState.test.ts +299 -0
- package/src/test/commands/putContent.test.ts +79 -1
- package/tldraw.css +8 -4
- package/dist-cjs/lib/shapes/shared/useForceSolid.js.map +0 -7
- package/dist-esm/lib/shapes/shared/useForceSolid.mjs +0 -9
- package/dist-esm/lib/shapes/shared/useForceSolid.mjs.map +0 -7
- package/src/lib/shapes/shared/useForceSolid.ts +0 -6
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/ui/components/primitives/TldrawUiTooltip.tsx"],
|
|
4
|
-
"sourcesContent": ["import { assert, Atom, atom, Editor, uniqueId, useMaybeEditor, useValue } from '@tldraw/editor'\nimport { Tooltip as _Tooltip } from 'radix-ui'\nimport React, {\n\tcreateContext,\n\tforwardRef,\n\tReactNode,\n\tuseContext,\n\tuseEffect,\n\tuseLayoutEffect,\n\tuseRef,\n\tuseState,\n} from 'react'\nimport { useTldrawUiOrientation } from './layout'\n\nconst DEFAULT_TOOLTIP_DELAY_MS = 700\n\n/** @public */\nexport interface TldrawUiTooltipProps {\n\tchildren: React.ReactNode\n\tcontent?: string | React.ReactNode\n\tside?: 'top' | 'right' | 'bottom' | 'left'\n\tsideOffset?: number\n\tdisabled?: boolean\n\tshowOnMobile?: boolean\n\tdelayDuration?: number\n}\n\ninterface CurrentTooltip {\n\tid: string\n\tcontent: ReactNode\n\tside: 'top' | 'right' | 'bottom' | 'left'\n\tsideOffset: number\n\tshowOnMobile: boolean\n\ttargetElement: HTMLElement\n\tdelayDuration: number\n}\n\n// Singleton tooltip manager\nclass TooltipManager {\n\tprivate static instance: TooltipManager | null = null\n\tprivate currentTooltip = atom<CurrentTooltip | null>('current tooltip', null)\n\tprivate destroyTimeoutId: number | null = null\n\n\tstatic getInstance(): TooltipManager {\n\t\tif (!TooltipManager.instance) {\n\t\t\tTooltipManager.instance = new TooltipManager()\n\t\t}\n\t\treturn TooltipManager.instance\n\t}\n\n\tshowTooltip(\n\t\ttooltipId: string,\n\t\tcontent: string | React.ReactNode,\n\t\ttargetElement: HTMLElement,\n\t\tside: 'top' | 'right' | 'bottom' | 'left',\n\t\tsideOffset: number,\n\t\tshowOnMobile: boolean,\n\t\tdelayDuration: number\n\t) {\n\t\t// Clear any existing destroy timeout\n\t\tif (this.destroyTimeoutId) {\n\t\t\tclearTimeout(this.destroyTimeoutId)\n\t\t\tthis.destroyTimeoutId = null\n\t\t}\n\n\t\t// Update current tooltip\n\t\tthis.currentTooltip.set({\n\t\t\tid: tooltipId,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset,\n\t\t\tshowOnMobile,\n\t\t\ttargetElement,\n\t\t\tdelayDuration,\n\t\t})\n\t}\n\n\tupdateCurrentTooltip(tooltipId: string, update: (tooltip: CurrentTooltip) => CurrentTooltip) {\n\t\tthis.currentTooltip.update((tooltip) => {\n\t\t\tif (tooltip?.id === tooltipId) {\n\t\t\t\treturn update(tooltip)\n\t\t\t}\n\t\t\treturn tooltip\n\t\t})\n\t}\n\n\thideTooltip(editor: Editor | null, tooltipId: string, instant: boolean = false) {\n\t\tconst hide = () => {\n\t\t\t// Only hide if this is the current tooltip\n\t\t\tif (this.currentTooltip.get()?.id === tooltipId) {\n\t\t\t\tthis.currentTooltip.set(null)\n\t\t\t\tthis.destroyTimeoutId = null\n\t\t\t}\n\t\t}\n\n\t\tif (editor && !instant) {\n\t\t\t// Start destroy timeout (1 second)\n\t\t\tthis.destroyTimeoutId = editor.timers.setTimeout(hide, 300)\n\t\t} else {\n\t\t\thide()\n\t\t}\n\t}\n\n\thideAllTooltips() {\n\t\tthis.currentTooltip.set(null)\n\t\tthis.destroyTimeoutId = null\n\t}\n\n\tgetCurrentTooltipData() {\n\t\tconst currentTooltip = this.currentTooltip.get()\n\t\tif (!currentTooltip) return null\n\t\tif (!this.supportsHover() && !currentTooltip.showOnMobile) return null\n\t\treturn currentTooltip\n\t}\n\n\tprivate supportsHoverAtom: Atom<boolean> | null = null\n\tsupportsHover() {\n\t\tif (!this.supportsHoverAtom) {\n\t\t\tconst mediaQuery = window.matchMedia('(hover: hover)')\n\t\t\tconst supportsHover = atom('has hover', mediaQuery.matches)\n\t\t\tthis.supportsHoverAtom = supportsHover\n\t\t\tmediaQuery.addEventListener('change', (e) => {\n\t\t\t\tsupportsHover.set(e.matches)\n\t\t\t})\n\t\t}\n\t\treturn this.supportsHoverAtom.get()\n\t}\n}\n\nexport const tooltipManager = TooltipManager.getInstance()\n\n// Context for the tooltip singleton\nconst TooltipSingletonContext = createContext<boolean>(false)\n\n/** @public */\nexport interface TldrawUiTooltipProviderProps {\n\tchildren: React.ReactNode\n}\n\n/** @public @react */\nexport function TldrawUiTooltipProvider({ children }: TldrawUiTooltipProviderProps) {\n\treturn (\n\t\t<_Tooltip.Provider skipDelayDuration={700}>\n\t\t\t<TooltipSingletonContext.Provider value={true}>\n\t\t\t\t{children}\n\t\t\t\t<TooltipSingleton />\n\t\t\t</TooltipSingletonContext.Provider>\n\t\t</_Tooltip.Provider>\n\t)\n}\n\n// The singleton tooltip component that renders once\nfunction TooltipSingleton() {\n\tconst [isOpen, setIsOpen] = useState(false)\n\tconst triggerRef = useRef<HTMLDivElement>(null)\n\tconst isFirstShowRef = useRef(true)\n\tconst editor = useMaybeEditor()\n\n\tconst currentTooltip = useValue(\n\t\t'current tooltip',\n\t\t() => tooltipManager.getCurrentTooltipData(),\n\t\t[]\n\t)\n\n\tconst cameraState = useValue('camera state', () => editor?.getCameraState(), [editor])\n\n\t// Hide tooltip when camera is moving (panning/zooming)\n\tuseEffect(() => {\n\t\tif (cameraState === 'moving' && isOpen && currentTooltip) {\n\t\t\ttooltipManager.hideTooltip(editor, currentTooltip.id, true)\n\t\t}\n\t}, [cameraState, isOpen, currentTooltip, editor])\n\n\tuseEffect(() => {\n\t\tfunction handleKeyDown(event: KeyboardEvent) {\n\t\t\tif (event.key === 'Escape' && currentTooltip && isOpen) {\n\t\t\t\ttooltipManager.hideTooltip(editor, currentTooltip.id)\n\t\t\t\tevent.stopPropagation()\n\t\t\t}\n\t\t}\n\n\t\tdocument.addEventListener('keydown', handleKeyDown, { capture: true })\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('keydown', handleKeyDown, { capture: true })\n\t\t}\n\t}, [editor, currentTooltip, isOpen])\n\n\t// Update open state and trigger position\n\tuseEffect(() => {\n\t\tlet timer: ReturnType<typeof setTimeout> | null = null\n\t\tif (currentTooltip && triggerRef.current) {\n\t\t\t// Position the invisible trigger element over the active element\n\t\t\tconst activeRect = currentTooltip.targetElement.getBoundingClientRect()\n\t\t\tconst trigger = triggerRef.current\n\n\t\t\ttrigger.style.position = 'fixed'\n\t\t\ttrigger.style.left = `${activeRect.left}px`\n\t\t\ttrigger.style.top = `${activeRect.top}px`\n\t\t\ttrigger.style.width = `${activeRect.width}px`\n\t\t\ttrigger.style.height = `${activeRect.height}px`\n\t\t\ttrigger.style.pointerEvents = 'none'\n\t\t\ttrigger.style.zIndex = '9999'\n\n\t\t\t// Handle delay for first show\n\t\t\tif (isFirstShowRef.current) {\n\t\t\t\t// eslint-disable-next-line no-restricted-globals\n\t\t\t\ttimer = setTimeout(() => {\n\t\t\t\t\tsetIsOpen(true)\n\t\t\t\t\tisFirstShowRef.current = false\n\t\t\t\t}, currentTooltip.delayDuration)\n\t\t\t} else {\n\t\t\t\t// Subsequent tooltips show immediately\n\t\t\t\tsetIsOpen(true)\n\t\t\t}\n\t\t} else {\n\t\t\t// Hide tooltip immediately\n\t\t\tsetIsOpen(false)\n\t\t\t// Reset first show state after tooltip is hidden\n\t\t\tisFirstShowRef.current = true\n\t\t}\n\n\t\treturn () => {\n\t\t\tif (timer !== null) {\n\t\t\t\tclearTimeout(timer)\n\t\t\t}\n\t\t}\n\t}, [currentTooltip])\n\n\tif (!currentTooltip) {\n\t\treturn null\n\t}\n\n\treturn (\n\t\t<_Tooltip.Root open={isOpen} delayDuration={0}>\n\t\t\t<_Tooltip.Trigger asChild>\n\t\t\t\t<div ref={triggerRef} />\n\t\t\t</_Tooltip.Trigger>\n\t\t\t<_Tooltip.Content\n\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\tside={currentTooltip.side}\n\t\t\t\tsideOffset={currentTooltip.sideOffset}\n\t\t\t\tavoidCollisions\n\t\t\t\tcollisionPadding={8}\n\t\t\t\tdir=\"ltr\"\n\t\t\t>\n\t\t\t\t{currentTooltip.content}\n\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t</_Tooltip.Content>\n\t\t</_Tooltip.Root>\n\t)\n}\n\n/** @public @react */\nexport const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProps>(\n\t(\n\t\t{\n\t\t\tchildren,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset = 5,\n\t\t\tdisabled = false,\n\t\t\tshowOnMobile = false,\n\t\t\tdelayDuration,\n\t\t},\n\t\tref\n\t) => {\n\t\tconst editor = useMaybeEditor()\n\t\tconst tooltipId = useRef<string>(uniqueId())\n\t\tconst hasProvider = useContext(TooltipSingletonContext)\n\t\tconst enhancedA11yMode = useValue(\n\t\t\t'enhancedA11yMode',\n\t\t\t() => editor?.user.getEnhancedA11yMode(),\n\t\t\t[editor]\n\t\t)\n\n\t\tconst orientationCtx = useTldrawUiOrientation()\n\t\tconst sideToUse = side ?? orientationCtx.tooltipSide\n\n\t\tuseEffect(() => {\n\t\t\tconst currentTooltipId = tooltipId.current\n\t\t\treturn () => {\n\t\t\t\tif (hasProvider) {\n\t\t\t\t\ttooltipManager.hideTooltip(editor, currentTooltipId, true)\n\t\t\t\t}\n\t\t\t}\n\t\t}, [editor, hasProvider])\n\n\t\tuseLayoutEffect(() => {\n\t\t\tif (hasProvider && tooltipManager.getCurrentTooltipData()?.id === tooltipId.current) {\n\t\t\t\ttooltipManager.updateCurrentTooltip(tooltipId.current, (tooltip) => ({\n\t\t\t\t\t...tooltip,\n\t\t\t\t\tcontent,\n\t\t\t\t\tside: sideToUse,\n\t\t\t\t\tsideOffset,\n\t\t\t\t\tshowOnMobile,\n\t\t\t\t}))\n\t\t\t}\n\t\t}, [content, sideToUse, sideOffset, showOnMobile, hasProvider])\n\n\t\t// Don't show tooltip if disabled, no content, or enhanced accessibility mode is disabled\n\t\tif (disabled || !content) {\n\t\t\treturn <>{children}</>\n\t\t}\n\n\t\tlet delayDurationToUse\n\t\tif (enhancedA11yMode) {\n\t\t\tdelayDurationToUse = 0\n\t\t} else {\n\t\t\tdelayDurationToUse =\n\t\t\t\tdelayDuration ?? (editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS)\n\t\t}\n\n\t\t// Fallback to old behavior if no provider\n\t\tif (!hasProvider || enhancedA11yMode) {\n\t\t\treturn (\n\t\t\t\t<_Tooltip.Root\n\t\t\t\t\tdelayDuration={delayDurationToUse}\n\t\t\t\t\tdisableHoverableContent={!enhancedA11yMode}\n\t\t\t\t>\n\t\t\t\t\t<_Tooltip.Trigger asChild ref={ref}>\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</_Tooltip.Trigger>\n\t\t\t\t\t<_Tooltip.Content\n\t\t\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\t\t\tside={sideToUse}\n\t\t\t\t\t\tsideOffset={sideOffset}\n\t\t\t\t\t\tavoidCollisions\n\t\t\t\t\t\tcollisionPadding={8}\n\t\t\t\t\t\tdir=\"ltr\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{content}\n\t\t\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t\t\t</_Tooltip.Content>\n\t\t\t\t</_Tooltip.Root>\n\t\t\t)\n\t\t}\n\n\t\tconst child = React.Children.only(children)\n\t\tassert(React.isValidElement(child), 'TldrawUiTooltip children must be a single element')\n\n\t\tconst handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseEnter?.(event)\n\t\t\ttooltipManager.showTooltip(\n\t\t\t\ttooltipId.current,\n\t\t\t\tcontent,\n\t\t\t\tevent.currentTarget as HTMLElement,\n\t\t\t\tsideToUse,\n\t\t\t\tsideOffset,\n\t\t\t\tshowOnMobile,\n\t\t\t\tdelayDurationToUse\n\t\t\t)\n\t\t}\n\n\t\tconst handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseLeave?.(event)\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current)\n\t\t}\n\n\t\tconst handleFocus = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onFocus?.(event)\n\t\t\ttooltipManager.showTooltip(\n\t\t\t\ttooltipId.current,\n\t\t\t\tcontent,\n\t\t\t\tevent.currentTarget as HTMLElement,\n\t\t\t\tsideToUse,\n\t\t\t\tsideOffset,\n\t\t\t\tshowOnMobile,\n\t\t\t\tdelayDurationToUse\n\t\t\t)\n\t\t}\n\n\t\tconst handleBlur = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onBlur?.(event)\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current)\n\t\t}\n\n\t\tconst childrenWithHandlers = React.cloneElement(children as React.ReactElement, {\n\t\t\tonMouseEnter: handleMouseEnter,\n\t\t\tonMouseLeave: handleMouseLeave,\n\t\t\tonFocus: handleFocus,\n\t\t\tonBlur: handleBlur,\n\t\t})\n\n\t\treturn childrenWithHandlers\n\t}\n)\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import {\n\tassert,\n\tatom,\n\tEditor,\n\ttlenvReactive,\n\tuniqueId,\n\tuseMaybeEditor,\n\tuseValue,\n} from '@tldraw/editor'\nimport { Tooltip as _Tooltip } from 'radix-ui'\nimport React, {\n\tcreateContext,\n\tforwardRef,\n\tReactNode,\n\tuseContext,\n\tuseEffect,\n\tuseRef,\n\tuseState,\n} from 'react'\nimport { useTldrawUiOrientation } from './layout'\n\nconst DEFAULT_TOOLTIP_DELAY_MS = 700\n\n/** @public */\nexport interface TldrawUiTooltipProps {\n\tchildren: React.ReactNode\n\tcontent?: string | React.ReactNode\n\tside?: 'top' | 'right' | 'bottom' | 'left'\n\tsideOffset?: number\n\tdisabled?: boolean\n\tshowOnMobile?: boolean\n\tdelayDuration?: number\n}\n\ninterface TooltipData {\n\tid: string\n\tcontent: ReactNode\n\tside: 'top' | 'right' | 'bottom' | 'left'\n\tsideOffset: number\n\tshowOnMobile: boolean\n\ttargetElement: HTMLElement\n\tdelayDuration: number\n}\n\n// State machine states\ntype TooltipState =\n\t| { name: 'idle' }\n\t| { name: 'pointer_down' }\n\t| { name: 'showing'; tooltip: TooltipData }\n\t| { name: 'waiting_to_hide'; tooltip: TooltipData; timeoutId: number }\n\n// State machine events\ntype TooltipEvent =\n\t| { type: 'pointer_down' }\n\t| { type: 'pointer_up' }\n\t| { type: 'show'; tooltip: TooltipData }\n\t| { type: 'hide'; tooltipId: string; editor: Editor | null; instant: boolean }\n\t| { type: 'hide_all' }\n\n// Singleton tooltip manager using explicit state machine\nclass TooltipManager {\n\tprivate static instance: TooltipManager | null = null\n\tprivate state = atom<TooltipState>('tooltip state', { name: 'idle' })\n\n\tstatic getInstance(): TooltipManager {\n\t\tif (!TooltipManager.instance) {\n\t\t\tTooltipManager.instance = new TooltipManager()\n\t\t}\n\t\treturn TooltipManager.instance\n\t}\n\n\thideAllTooltips() {\n\t\tthis.handleEvent({ type: 'hide_all' })\n\t}\n\n\thandleEvent(event: TooltipEvent) {\n\t\tconst currentState = this.state.get()\n\n\t\tswitch (event.type) {\n\t\t\tcase 'pointer_down': {\n\t\t\t\t// Transition to pointer_down from any state\n\t\t\t\tif (currentState.name === 'waiting_to_hide') {\n\t\t\t\t\tclearTimeout(currentState.timeoutId)\n\t\t\t\t}\n\t\t\t\tthis.state.set({ name: 'pointer_down' })\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase 'pointer_up': {\n\t\t\t\t// Only transition from pointer_down to idle\n\t\t\t\tif (currentState.name === 'pointer_down') {\n\t\t\t\t\tthis.state.set({ name: 'idle' })\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase 'show': {\n\t\t\t\t// Don't show tooltips while pointer is down\n\t\t\t\tif (currentState.name === 'pointer_down') {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// Clear any existing timeout if transitioning from waiting_to_hide\n\t\t\t\tif (currentState.name === 'waiting_to_hide') {\n\t\t\t\t\tclearTimeout(currentState.timeoutId)\n\t\t\t\t}\n\n\t\t\t\t// Transition to showing state\n\t\t\t\tthis.state.set({ name: 'showing', tooltip: event.tooltip })\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase 'hide': {\n\t\t\t\tconst { tooltipId, editor, instant } = event\n\n\t\t\t\t// Only hide if the tooltip matches\n\t\t\t\tif (currentState.name === 'showing' && currentState.tooltip.id === tooltipId) {\n\t\t\t\t\tif (editor && !instant) {\n\t\t\t\t\t\t// Transition to waiting_to_hide state\n\t\t\t\t\t\tconst timeoutId = editor.timers.setTimeout(() => {\n\t\t\t\t\t\t\tconst state = this.state.get()\n\t\t\t\t\t\t\tif (state.name === 'waiting_to_hide' && state.tooltip.id === tooltipId) {\n\t\t\t\t\t\t\t\tthis.state.set({ name: 'idle' })\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}, 300)\n\t\t\t\t\t\tthis.state.set({\n\t\t\t\t\t\t\tname: 'waiting_to_hide',\n\t\t\t\t\t\t\ttooltip: currentState.tooltip,\n\t\t\t\t\t\t\ttimeoutId,\n\t\t\t\t\t\t})\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.state.set({ name: 'idle' })\n\t\t\t\t\t}\n\t\t\t\t} else if (\n\t\t\t\t\tcurrentState.name === 'waiting_to_hide' &&\n\t\t\t\t\tcurrentState.tooltip.id === tooltipId\n\t\t\t\t) {\n\t\t\t\t\t// Already waiting to hide, make it instant if requested\n\t\t\t\t\tif (instant) {\n\t\t\t\t\t\tclearTimeout(currentState.timeoutId)\n\t\t\t\t\t\tthis.state.set({ name: 'idle' })\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase 'hide_all': {\n\t\t\t\tif (currentState.name === 'waiting_to_hide') {\n\t\t\t\t\tclearTimeout(currentState.timeoutId)\n\t\t\t\t}\n\t\t\t\t// Preserve pointer_down state if that's the current state\n\t\t\t\tif (currentState.name === 'pointer_down') {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.state.set({ name: 'idle' })\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tgetCurrentTooltipData(): TooltipData | null {\n\t\tconst currentState = this.state.get()\n\t\tlet tooltip: TooltipData | null = null\n\n\t\tif (currentState.name === 'showing') {\n\t\t\ttooltip = currentState.tooltip\n\t\t} else if (currentState.name === 'waiting_to_hide') {\n\t\t\ttooltip = currentState.tooltip\n\t\t}\n\n\t\tif (!tooltip) return null\n\t\tif (tlenvReactive.get().isCoarsePointer && !tooltip.showOnMobile) return null\n\t\treturn tooltip\n\t}\n}\n\nconst tooltipManager = TooltipManager.getInstance()\n\n/** @public */\nexport function hideAllTooltips() {\n\ttooltipManager.hideAllTooltips()\n}\n\n// Context for the tooltip singleton\nconst TooltipSingletonContext = createContext<boolean>(false)\n\n/** @public */\nexport interface TldrawUiTooltipProviderProps {\n\tchildren: React.ReactNode\n}\n\n/** @public @react */\nexport function TldrawUiTooltipProvider({ children }: TldrawUiTooltipProviderProps) {\n\treturn (\n\t\t<_Tooltip.Provider skipDelayDuration={700}>\n\t\t\t<TooltipSingletonContext.Provider value={true}>\n\t\t\t\t{children}\n\t\t\t\t<TooltipSingleton />\n\t\t\t</TooltipSingletonContext.Provider>\n\t\t</_Tooltip.Provider>\n\t)\n}\n\n// The singleton tooltip component that renders once\nfunction TooltipSingleton() {\n\tconst [isOpen, setIsOpen] = useState(false)\n\tconst triggerRef = useRef<HTMLDivElement>(null)\n\tconst isFirstShowRef = useRef(true)\n\tconst editor = useMaybeEditor()\n\n\tconst currentTooltip = useValue(\n\t\t'current tooltip',\n\t\t() => tooltipManager.getCurrentTooltipData(),\n\t\t[]\n\t)\n\n\tconst cameraState = useValue('camera state', () => editor?.getCameraState(), [editor])\n\n\t// Hide tooltip when camera is moving (panning/zooming)\n\tuseEffect(() => {\n\t\tif (cameraState === 'moving' && isOpen && currentTooltip) {\n\t\t\ttooltipManager.handleEvent({\n\t\t\t\ttype: 'hide',\n\t\t\t\ttooltipId: currentTooltip.id,\n\t\t\t\teditor,\n\t\t\t\tinstant: true,\n\t\t\t})\n\t\t}\n\t}, [cameraState, isOpen, currentTooltip, editor])\n\n\tuseEffect(() => {\n\t\tfunction handleKeyDown(event: KeyboardEvent) {\n\t\t\tif (event.key === 'Escape' && currentTooltip && isOpen) {\n\t\t\t\thideAllTooltips()\n\t\t\t\tevent.stopPropagation()\n\t\t\t}\n\t\t}\n\n\t\tdocument.addEventListener('keydown', handleKeyDown, { capture: true })\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('keydown', handleKeyDown, { capture: true })\n\t\t}\n\t}, [currentTooltip, isOpen])\n\n\t// Hide tooltip and prevent new ones from opening while pointer is down\n\tuseEffect(() => {\n\t\tfunction handlePointerDown() {\n\t\t\ttooltipManager.handleEvent({ type: 'pointer_down' })\n\t\t}\n\n\t\tfunction handlePointerUp() {\n\t\t\ttooltipManager.handleEvent({ type: 'pointer_up' })\n\t\t}\n\n\t\tdocument.addEventListener('pointerdown', handlePointerDown, { capture: true })\n\t\tdocument.addEventListener('pointerup', handlePointerUp, { capture: true })\n\t\tdocument.addEventListener('pointercancel', handlePointerUp, { capture: true })\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('pointerdown', handlePointerDown, { capture: true })\n\t\t\tdocument.removeEventListener('pointerup', handlePointerUp, { capture: true })\n\t\t\tdocument.removeEventListener('pointercancel', handlePointerUp, { capture: true })\n\t\t\t// Reset pointer state on unmount to prevent stuck state\n\t\t\ttooltipManager.handleEvent({ type: 'pointer_up' })\n\t\t}\n\t}, [])\n\n\t// Update open state and trigger position\n\tuseEffect(() => {\n\t\tlet timer: ReturnType<typeof setTimeout> | null = null\n\t\tif (currentTooltip && triggerRef.current) {\n\t\t\t// Position the invisible trigger element over the active element\n\t\t\tconst activeRect = currentTooltip.targetElement.getBoundingClientRect()\n\t\t\tconst trigger = triggerRef.current\n\n\t\t\ttrigger.style.position = 'fixed'\n\t\t\ttrigger.style.left = `${activeRect.left}px`\n\t\t\ttrigger.style.top = `${activeRect.top}px`\n\t\t\ttrigger.style.width = `${activeRect.width}px`\n\t\t\ttrigger.style.height = `${activeRect.height}px`\n\t\t\ttrigger.style.pointerEvents = 'none'\n\t\t\ttrigger.style.zIndex = '9999'\n\n\t\t\t// Handle delay for first show\n\t\t\tif (isFirstShowRef.current) {\n\t\t\t\t// eslint-disable-next-line no-restricted-globals\n\t\t\t\ttimer = setTimeout(() => {\n\t\t\t\t\tsetIsOpen(true)\n\t\t\t\t\tisFirstShowRef.current = false\n\t\t\t\t}, currentTooltip.delayDuration)\n\t\t\t} else {\n\t\t\t\t// Subsequent tooltips show immediately\n\t\t\t\tsetIsOpen(true)\n\t\t\t}\n\t\t} else {\n\t\t\t// Hide tooltip immediately\n\t\t\tsetIsOpen(false)\n\t\t\t// Reset first show state after tooltip is hidden\n\t\t\tisFirstShowRef.current = true\n\t\t}\n\n\t\treturn () => {\n\t\t\tif (timer !== null) {\n\t\t\t\tclearTimeout(timer)\n\t\t\t}\n\t\t}\n\t}, [currentTooltip])\n\n\tif (!currentTooltip) {\n\t\treturn null\n\t}\n\n\treturn (\n\t\t<_Tooltip.Root open={isOpen} delayDuration={0}>\n\t\t\t<_Tooltip.Trigger asChild>\n\t\t\t\t<div ref={triggerRef} />\n\t\t\t</_Tooltip.Trigger>\n\t\t\t<_Tooltip.Content\n\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\tside={currentTooltip.side}\n\t\t\t\tsideOffset={currentTooltip.sideOffset}\n\t\t\t\tavoidCollisions\n\t\t\t\tcollisionPadding={8}\n\t\t\t\tdir=\"ltr\"\n\t\t\t>\n\t\t\t\t{currentTooltip.content}\n\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t</_Tooltip.Content>\n\t\t</_Tooltip.Root>\n\t)\n}\n\n/** @public @react */\nexport const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProps>(\n\t(\n\t\t{\n\t\t\tchildren,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset = 5,\n\t\t\tdisabled = false,\n\t\t\tshowOnMobile = false,\n\t\t\tdelayDuration,\n\t\t},\n\t\tref\n\t) => {\n\t\tconst editor = useMaybeEditor()\n\t\tconst tooltipId = useRef<string>(uniqueId())\n\t\tconst hasProvider = useContext(TooltipSingletonContext)\n\t\tconst enhancedA11yMode = useValue(\n\t\t\t'enhancedA11yMode',\n\t\t\t() => editor?.user.getEnhancedA11yMode(),\n\t\t\t[editor]\n\t\t)\n\n\t\tconst orientationCtx = useTldrawUiOrientation()\n\t\tconst sideToUse = side ?? orientationCtx.tooltipSide\n\n\t\tuseEffect(() => {\n\t\t\tconst currentTooltipId = tooltipId.current\n\t\t\treturn () => {\n\t\t\t\tif (hasProvider) {\n\t\t\t\t\ttooltipManager.handleEvent({\n\t\t\t\t\t\ttype: 'hide',\n\t\t\t\t\t\ttooltipId: currentTooltipId,\n\t\t\t\t\t\teditor,\n\t\t\t\t\t\tinstant: true,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}, [editor, hasProvider])\n\n\t\t// Don't show tooltip if disabled, no content, or enhanced accessibility mode is disabled\n\t\tif (disabled || !content) {\n\t\t\treturn <>{children}</>\n\t\t}\n\n\t\tlet delayDurationToUse\n\t\tif (enhancedA11yMode) {\n\t\t\tdelayDurationToUse = 0\n\t\t} else {\n\t\t\tdelayDurationToUse =\n\t\t\t\tdelayDuration ?? (editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS)\n\t\t}\n\n\t\t// Fallback to old behavior if no provider\n\t\tif (!hasProvider || enhancedA11yMode) {\n\t\t\treturn (\n\t\t\t\t<_Tooltip.Root\n\t\t\t\t\tdelayDuration={delayDurationToUse}\n\t\t\t\t\tdisableHoverableContent={!enhancedA11yMode}\n\t\t\t\t>\n\t\t\t\t\t<_Tooltip.Trigger asChild ref={ref}>\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</_Tooltip.Trigger>\n\t\t\t\t\t<_Tooltip.Content\n\t\t\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\t\t\tside={sideToUse}\n\t\t\t\t\t\tsideOffset={sideOffset}\n\t\t\t\t\t\tavoidCollisions\n\t\t\t\t\t\tcollisionPadding={8}\n\t\t\t\t\t\tdir=\"ltr\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{content}\n\t\t\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t\t\t</_Tooltip.Content>\n\t\t\t\t</_Tooltip.Root>\n\t\t\t)\n\t\t}\n\n\t\tconst child = React.Children.only(children)\n\t\tassert(React.isValidElement(child), 'TldrawUiTooltip children must be a single element')\n\n\t\tconst handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseEnter?.(event)\n\t\t\ttooltipManager.handleEvent({\n\t\t\t\ttype: 'show',\n\t\t\t\ttooltip: {\n\t\t\t\t\tid: tooltipId.current,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttargetElement: event.currentTarget as HTMLElement,\n\t\t\t\t\tside: sideToUse,\n\t\t\t\t\tsideOffset,\n\t\t\t\t\tshowOnMobile,\n\t\t\t\t\tdelayDuration: delayDurationToUse,\n\t\t\t\t},\n\t\t\t})\n\t\t}\n\n\t\tconst handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseLeave?.(event)\n\t\t\ttooltipManager.handleEvent({\n\t\t\t\ttype: 'hide',\n\t\t\t\ttooltipId: tooltipId.current,\n\t\t\t\teditor,\n\t\t\t\tinstant: false,\n\t\t\t})\n\t\t}\n\n\t\tconst handleFocus = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onFocus?.(event)\n\t\t\ttooltipManager.handleEvent({\n\t\t\t\ttype: 'show',\n\t\t\t\ttooltip: {\n\t\t\t\t\tid: tooltipId.current,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttargetElement: event.currentTarget as HTMLElement,\n\t\t\t\t\tside: sideToUse,\n\t\t\t\t\tsideOffset,\n\t\t\t\t\tshowOnMobile,\n\t\t\t\t\tdelayDuration: delayDurationToUse,\n\t\t\t\t},\n\t\t\t})\n\t\t}\n\n\t\tconst handleBlur = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onBlur?.(event)\n\t\t\ttooltipManager.handleEvent({\n\t\t\t\ttype: 'hide',\n\t\t\t\ttooltipId: tooltipId.current,\n\t\t\t\teditor,\n\t\t\t\tinstant: false,\n\t\t\t})\n\t\t}\n\n\t\tconst childrenWithHandlers = React.cloneElement(children as React.ReactElement, {\n\t\t\tonMouseEnter: handleMouseEnter,\n\t\t\tonMouseLeave: handleMouseLeave,\n\t\t\tonFocus: handleFocus,\n\t\t\tonBlur: handleBlur,\n\t\t})\n\n\t\treturn childrenWithHandlers\n\t}\n)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -19,7 +19,7 @@ import { TldrawUiButtonLabel } from "../Button/TldrawUiButtonLabel.mjs";
|
|
|
19
19
|
import { TldrawUiDropdownMenuItem } from "../TldrawUiDropdownMenu.mjs";
|
|
20
20
|
import { TldrawUiKbd } from "../TldrawUiKbd.mjs";
|
|
21
21
|
import { TldrawUiToolbarButton } from "../TldrawUiToolbar.mjs";
|
|
22
|
-
import {
|
|
22
|
+
import { hideAllTooltips } from "../TldrawUiTooltip.mjs";
|
|
23
23
|
import { useTldrawUiMenuContext } from "./TldrawUiMenuContext.mjs";
|
|
24
24
|
function TldrawUiMenuItem({
|
|
25
25
|
disabled = false,
|
|
@@ -240,7 +240,7 @@ function useDraggableEvents(onDragStart, onSelect) {
|
|
|
240
240
|
...getPointerInfo(editor, e),
|
|
241
241
|
point: screenSpaceStart
|
|
242
242
|
});
|
|
243
|
-
|
|
243
|
+
hideAllTooltips();
|
|
244
244
|
editor.getContainer().focus();
|
|
245
245
|
});
|
|
246
246
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx"],
|
|
4
|
-
"sourcesContent": ["import {\n\texhaustiveSwitchError,\n\tgetPointerInfo,\n\tpreventDefault,\n\tTLPointerEventInfo,\n\tuseEditor,\n\tVec,\n\tVecModel,\n} from '@tldraw/editor'\nimport { ContextMenu as _ContextMenu } from 'radix-ui'\nimport { useMemo, useState } from 'react'\nimport { unwrapLabel } from '../../../context/actions'\nimport { TLUiEventSource } from '../../../context/events'\nimport { useReadonly } from '../../../hooks/useReadonly'\nimport { TLUiToolItem } from '../../../hooks/useTools'\nimport { TLUiTranslationKey } from '../../../hooks/useTranslation/TLUiTranslationKey'\nimport { useTranslation } from '../../../hooks/useTranslation/useTranslation'\nimport { kbdStr } from '../../../kbd-utils'\nimport { Spinner } from '../../Spinner'\nimport { TldrawUiButton } from '../Button/TldrawUiButton'\nimport { TldrawUiButtonIcon } from '../Button/TldrawUiButtonIcon'\nimport { TldrawUiButtonLabel } from '../Button/TldrawUiButtonLabel'\nimport { TldrawUiDropdownMenuItem } from '../TldrawUiDropdownMenu'\nimport { TLUiIconJsx } from '../TldrawUiIcon'\nimport { TldrawUiKbd } from '../TldrawUiKbd'\nimport { TldrawUiToolbarButton } from '../TldrawUiToolbar'\nimport { tooltipManager } from '../TldrawUiTooltip'\nimport { useTldrawUiMenuContext } from './TldrawUiMenuContext'\n\n/** @public */\nexport interface TLUiMenuItemProps<\n\tTranslationKey extends string = string,\n\tIconType extends string = string,\n> {\n\tid: string\n\t/**\n\t * The icon to display on the item. Icons are only shown in certain menu types.\n\t */\n\ticon?: IconType | TLUiIconJsx\n\t/**\n\t * An icon to display to the left of the menu item.\n\t */\n\ticonLeft?: IconType | TLUiIconJsx\n\t/**\n\t * The keyboard shortcut to display on the item.\n\t */\n\tkbd?: string\n\t/**\n\t * The label to display on the item. If it's a string, it will be translated. If it's an object, the keys will be used as the language keys and the values will be translated.\n\t */\n\tlabel?: TranslationKey | { [key: string]: TranslationKey }\n\t/**\n\t * If the editor is in readonly mode and the item is not marked as readonlyok, it will not be rendered.\n\t */\n\treadonlyOk?: boolean\n\t/**\n\t * The function to call when the item is clicked.\n\t */\n\tonSelect(source: TLUiEventSource): Promise<void> | void\n\t/**\n\t * Whether this item should be disabled.\n\t */\n\tdisabled?: boolean\n\t/**\n\t * Prevent the menu from closing when the item is clicked\n\t */\n\tnoClose?: boolean\n\t/**\n\t * Whether to show a spinner on the item.\n\t */\n\tspinner?: boolean\n\t/**\n\t * Whether the item is selected.\n\t */\n\tisSelected?: boolean\n\t/**\n\t * The function to call when the item is dragged. If this is provided, the item will be draggable.\n\t */\n\tonDragStart?(source: TLUiEventSource, info: TLPointerEventInfo): void\n}\n\n/** @public @react */\nexport function TldrawUiMenuItem<\n\tTranslationKey extends string = string,\n\tIconType extends string = string,\n>({\n\tdisabled = false,\n\tspinner = false,\n\treadonlyOk = false,\n\tid,\n\tkbd,\n\tlabel,\n\ticon,\n\ticonLeft,\n\tonSelect,\n\tnoClose,\n\tisSelected,\n\tonDragStart,\n}: TLUiMenuItemProps<TranslationKey, IconType>) {\n\tconst { type: menuType, sourceId } = useTldrawUiMenuContext()\n\n\tconst msg = useTranslation()\n\n\tconst [disableClicks, setDisableClicks] = useState(false)\n\n\tconst isReadonlyMode = useReadonly()\n\tif (isReadonlyMode && !readonlyOk) return null\n\n\tconst labelToUse = unwrapLabel(label, menuType)\n\tconst kbdToUse = kbd ? kbdStr(kbd) : undefined\n\n\tconst labelStr = labelToUse ? msg(labelToUse as TLUiTranslationKey) : undefined\n\tconst titleStr = labelStr && kbdToUse ? `${labelStr} ${kbdToUse}` : labelStr\n\n\tswitch (menuType) {\n\t\tcase 'menu': {\n\t\t\treturn (\n\t\t\t\t<TldrawUiDropdownMenuItem>\n\t\t\t\t\t<TldrawUiButton\n\t\t\t\t\t\ttype=\"menu\"\n\t\t\t\t\t\tdata-testid={`${sourceId}.${id}`}\n\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\tonClick={(e) => {\n\t\t\t\t\t\t\tif (noClose) {\n\t\t\t\t\t\t\t\tpreventDefault(e)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (disableClicks) {\n\t\t\t\t\t\t\t\tsetDisableClicks(false)\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tonSelect(sourceId)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{iconLeft && <TldrawUiButtonIcon icon={iconLeft} small />}\n\t\t\t\t\t\t<TldrawUiButtonLabel>{labelStr}</TldrawUiButtonLabel>\n\t\t\t\t\t\t{kbd && <TldrawUiKbd>{kbd}</TldrawUiKbd>}\n\t\t\t\t\t\t{icon && <TldrawUiButtonIcon icon={icon} small />}\n\t\t\t\t\t</TldrawUiButton>\n\t\t\t\t</TldrawUiDropdownMenuItem>\n\t\t\t)\n\t\t}\n\t\tcase 'context-menu': {\n\t\t\t// Hide disabled context menu items\n\t\t\tif (disabled) return null\n\n\t\t\treturn (\n\t\t\t\t<_ContextMenu.Item\n\t\t\t\t\tdir=\"ltr\"\n\t\t\t\t\tdraggable={false}\n\t\t\t\t\tclassName=\"tlui-button tlui-button__menu\"\n\t\t\t\t\tdata-testid={`${sourceId}.${id}`}\n\t\t\t\t\tonSelect={(e) => {\n\t\t\t\t\t\tif (noClose) preventDefault(e)\n\t\t\t\t\t\tif (disableClicks) {\n\t\t\t\t\t\t\tsetDisableClicks(false)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tonSelect(sourceId)\n\t\t\t\t\t\t}\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<span className=\"tlui-button__label\" draggable={false}>\n\t\t\t\t\t\t{labelStr}\n\t\t\t\t\t</span>\n\t\t\t\t\t{iconLeft && <TldrawUiButtonIcon icon={iconLeft} small />}\n\t\t\t\t\t{kbd && <TldrawUiKbd>{kbd}</TldrawUiKbd>}\n\t\t\t\t\t{spinner && <Spinner />}\n\t\t\t\t</_ContextMenu.Item>\n\t\t\t)\n\t\t}\n\t\tcase 'small-icons':\n\t\tcase 'icons': {\n\t\t\treturn (\n\t\t\t\t<TldrawUiToolbarButton\n\t\t\t\t\tdata-testid={`${sourceId}.${id}`}\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\ttitle={titleStr}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\tonClick={() => onSelect(sourceId)}\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiButtonIcon icon={icon!} small />\n\t\t\t\t</TldrawUiToolbarButton>\n\t\t\t)\n\t\t}\n\t\tcase 'keyboard-shortcuts': {\n\t\t\tif (!kbd) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Menu item '${label}' isn't shown in the keyboard shortcuts dialog because it doesn't have a keyboard shortcut.`\n\t\t\t\t)\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\treturn (\n\t\t\t\t<div className=\"tlui-shortcuts-dialog__key-pair\" data-testid={`${sourceId}.${id}`}>\n\t\t\t\t\t<div className=\"tlui-shortcuts-dialog__key-pair__key\">{labelStr}</div>\n\t\t\t\t\t<div className=\"tlui-shortcuts-dialog__key-pair__value\">\n\t\t\t\t\t\t<TldrawUiKbd visibleOnMobileLayout>{kbd}</TldrawUiKbd>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)\n\t\t}\n\t\tcase 'helper-buttons': {\n\t\t\treturn (\n\t\t\t\t<TldrawUiButton type=\"low\" onClick={() => onSelect(sourceId)}>\n\t\t\t\t\t<TldrawUiButtonIcon icon={icon!} />\n\t\t\t\t\t<TldrawUiButtonLabel>{labelStr}</TldrawUiButtonLabel>\n\t\t\t\t</TldrawUiButton>\n\t\t\t)\n\t\t}\n\t\tcase 'toolbar': {\n\t\t\tif (onDragStart) {\n\t\t\t\treturn (\n\t\t\t\t\t<DraggableToolbarButton\n\t\t\t\t\t\tid={id}\n\t\t\t\t\t\ticon={icon}\n\t\t\t\t\t\tonSelect={onSelect}\n\t\t\t\t\t\tonDragStart={onDragStart}\n\t\t\t\t\t\tlabelStr={labelStr}\n\t\t\t\t\t\ttitleStr={titleStr}\n\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\tisSelected={isSelected}\n\t\t\t\t\t/>\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn (\n\t\t\t\t<TldrawUiToolbarButton\n\t\t\t\t\taria-label={labelStr}\n\t\t\t\t\taria-pressed={isSelected ? 'true' : 'false'}\n\t\t\t\t\tdata-testid={`tools.${id}`}\n\t\t\t\t\tdata-value={id}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\tonClick={() => onSelect('toolbar')}\n\t\t\t\t\tonTouchStart={(e) => {\n\t\t\t\t\t\tpreventDefault(e)\n\t\t\t\t\t\tonSelect('toolbar')\n\t\t\t\t\t}}\n\t\t\t\t\ttitle={titleStr}\n\t\t\t\t\ttype=\"tool\"\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiButtonIcon icon={icon!} />\n\t\t\t\t</TldrawUiToolbarButton>\n\t\t\t)\n\t\t}\n\t\tcase 'toolbar-overflow': {\n\t\t\tif (onDragStart) {\n\t\t\t\treturn (\n\t\t\t\t\t<DraggableToolbarButton\n\t\t\t\t\t\tid={id}\n\t\t\t\t\t\ticon={icon}\n\t\t\t\t\t\tonSelect={onSelect}\n\t\t\t\t\t\tonDragStart={onDragStart}\n\t\t\t\t\t\tlabelStr={labelStr}\n\t\t\t\t\t\ttitleStr={titleStr}\n\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\tisSelected={isSelected}\n\t\t\t\t\t\toverflow\n\t\t\t\t\t/>\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn (\n\t\t\t\t<TldrawUiToolbarButton\n\t\t\t\t\taria-label={labelStr}\n\t\t\t\t\taria-pressed={isSelected ? 'true' : 'false'}\n\t\t\t\t\tisActive={isSelected}\n\t\t\t\t\tdata-testid={`tools.more.${id}`}\n\t\t\t\t\tdata-value={id}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\tonClick={() => onSelect('toolbar')}\n\t\t\t\t\ttitle={titleStr}\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiButtonIcon icon={icon!} />\n\t\t\t\t</TldrawUiToolbarButton>\n\t\t\t)\n\t\t}\n\t\tdefault: {\n\t\t\tthrow exhaustiveSwitchError(menuType)\n\t\t}\n\t}\n}\n\nfunction useDraggableEvents(\n\tonDragStart: TLUiToolItem['onDragStart'],\n\tonSelect: TLUiToolItem['onSelect']\n) {\n\tconst editor = useEditor()\n\tconst events = useMemo(() => {\n\t\tlet state = { name: 'idle' } as\n\t\t\t| {\n\t\t\t\t\tname: 'idle'\n\t\t\t }\n\t\t\t| {\n\t\t\t\t\tname: 'pointing'\n\t\t\t\t\tscreenSpaceStart: VecModel\n\t\t\t }\n\t\t\t| {\n\t\t\t\t\tname: 'dragging'\n\t\t\t\t\tscreenSpaceStart: VecModel\n\t\t\t }\n\t\t\t| {\n\t\t\t\t\tname: 'dragged'\n\t\t\t }\n\n\t\tfunction handlePointerDown(e: React.PointerEvent<HTMLButtonElement>) {\n\t\t\tstate = {\n\t\t\t\tname: 'pointing',\n\t\t\t\tscreenSpaceStart: { x: e.clientX, y: e.clientY },\n\t\t\t}\n\n\t\t\te.currentTarget.setPointerCapture(e.pointerId)\n\t\t}\n\n\t\tfunction handlePointerMove(e: React.PointerEvent<HTMLButtonElement>) {\n\t\t\tif ((e as any).isSpecialRedispatchedEvent) return\n\n\t\t\tif (state.name === 'pointing') {\n\t\t\t\tconst distanceSq = Vec.Dist2(state.screenSpaceStart, { x: e.clientX, y: e.clientY })\n\t\t\t\tif (\n\t\t\t\t\tdistanceSq >\n\t\t\t\t\t(editor.getInstanceState().isCoarsePointer\n\t\t\t\t\t\t? editor.options.uiCoarseDragDistanceSquared\n\t\t\t\t\t\t: editor.options.uiDragDistanceSquared)\n\t\t\t\t) {\n\t\t\t\t\tconst screenSpaceStart = state.screenSpaceStart\n\t\t\t\t\tstate = {\n\t\t\t\t\t\tname: 'dragging',\n\t\t\t\t\t\tscreenSpaceStart,\n\t\t\t\t\t}\n\n\t\t\t\t\teditor.run(() => {\n\t\t\t\t\t\teditor.setCurrentTool('select')\n\n\t\t\t\t\t\t// Set origin point\n\t\t\t\t\t\teditor.dispatch({\n\t\t\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\t\t\tname: 'pointer_down',\n\t\t\t\t\t\t\t...getPointerInfo(editor, e),\n\t\t\t\t\t\t\tpoint: screenSpaceStart,\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\t// Pointer down potentially selects shapes, so we need to deselect them.\n\t\t\t\t\t\teditor.selectNone()\n\n\t\t\t\t\t\t// start drag\n\t\t\t\t\t\tonDragStart?.('toolbar', {\n\t\t\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\t\t\tname: 'pointer_move',\n\t\t\t\t\t\t\t...getPointerInfo(editor, e),\n\t\t\t\t\t\t\tpoint: screenSpaceStart,\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\ttooltipManager.hideAllTooltips()\n\t\t\t\t\t\teditor.getContainer().focus()\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfunction handlePointerUp(e: React.PointerEvent<HTMLButtonElement>) {\n\t\t\tif ((e as any).isSpecialRedispatchedEvent) return\n\n\t\t\te.currentTarget.releasePointerCapture(e.pointerId)\n\n\t\t\teditor.dispatch({\n\t\t\t\ttype: 'pointer',\n\t\t\t\ttarget: 'canvas',\n\t\t\t\tname: 'pointer_up',\n\t\t\t\t...getPointerInfo(editor, e),\n\t\t\t})\n\t\t}\n\n\t\tfunction handleClick() {\n\t\t\tif (state.name === 'dragging' || state.name === 'dragged') {\n\t\t\t\tstate = { name: 'idle' }\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\tstate = { name: 'idle' }\n\t\t\tonSelect?.('toolbar')\n\t\t}\n\n\t\treturn {\n\t\t\tonPointerDown: handlePointerDown,\n\t\t\tonPointerMove: handlePointerMove,\n\t\t\tonPointerUp: handlePointerUp,\n\t\t\tonClick: handleClick,\n\t\t}\n\t}, [onDragStart, editor, onSelect])\n\n\treturn events\n}\n\nfunction DraggableToolbarButton({\n\tid,\n\tlabelStr,\n\ttitleStr,\n\tdisabled,\n\tisSelected,\n\ticon,\n\tonSelect,\n\tonDragStart,\n\toverflow,\n}: {\n\tid: string\n\tdisabled: boolean\n\tlabelStr?: string\n\ttitleStr?: string\n\tisSelected?: boolean\n\ticon: TLUiMenuItemProps['icon']\n\tonSelect: TLUiMenuItemProps['onSelect']\n\tonDragStart: TLUiMenuItemProps['onDragStart']\n\toverflow?: boolean\n}) {\n\tconst events = useDraggableEvents(onDragStart, onSelect)\n\n\tif (overflow) {\n\t\treturn (\n\t\t\t<TldrawUiToolbarButton\n\t\t\t\taria-label={labelStr}\n\t\t\t\taria-pressed={isSelected ? 'true' : 'false'}\n\t\t\t\tisActive={isSelected}\n\t\t\t\tclassName=\"tlui-button-grid__button\"\n\t\t\t\tdata-testid={`tools.more.${id}`}\n\t\t\t\tdata-value={id}\n\t\t\t\tdisabled={disabled}\n\t\t\t\ttitle={titleStr}\n\t\t\t\ttype=\"icon\"\n\t\t\t\t{...events}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon icon={icon!} />\n\t\t\t</TldrawUiToolbarButton>\n\t\t)\n\t}\n\n\treturn (\n\t\t<TldrawUiToolbarButton\n\t\t\taria-label={labelStr}\n\t\t\taria-pressed={isSelected ? 'true' : 'false'}\n\t\t\tdata-testid={`tools.${id}`}\n\t\t\tdata-value={id}\n\t\t\tdisabled={disabled}\n\t\t\tonTouchStart={(e) => {\n\t\t\t\tpreventDefault(e)\n\t\t\t\tonSelect('toolbar')\n\t\t\t}}\n\t\t\ttitle={titleStr}\n\t\t\ttype=\"tool\"\n\t\t\t{...events}\n\t\t>\n\t\t\t<TldrawUiButtonIcon icon={icon!} />\n\t\t</TldrawUiToolbarButton>\n\t)\n}\n"],
|
|
5
|
-
"mappings": "AAsHK,SAec,KAfd;AAtHL;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,OAEM;AACP,SAAS,eAAe,oBAAoB;AAC5C,SAAS,SAAS,gBAAgB;AAClC,SAAS,mBAAmB;AAE5B,SAAS,mBAAmB;AAG5B,SAAS,sBAAsB;AAC/B,SAAS,cAAc;AACvB,SAAS,eAAe;AACxB,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AACnC,SAAS,2BAA2B;AACpC,SAAS,gCAAgC;AAEzC,SAAS,mBAAmB;AAC5B,SAAS,6BAA6B;AACtC,SAAS,
|
|
4
|
+
"sourcesContent": ["import {\n\texhaustiveSwitchError,\n\tgetPointerInfo,\n\tpreventDefault,\n\tTLPointerEventInfo,\n\tuseEditor,\n\tVec,\n\tVecModel,\n} from '@tldraw/editor'\nimport { ContextMenu as _ContextMenu } from 'radix-ui'\nimport { useMemo, useState } from 'react'\nimport { unwrapLabel } from '../../../context/actions'\nimport { TLUiEventSource } from '../../../context/events'\nimport { useReadonly } from '../../../hooks/useReadonly'\nimport { TLUiToolItem } from '../../../hooks/useTools'\nimport { TLUiTranslationKey } from '../../../hooks/useTranslation/TLUiTranslationKey'\nimport { useTranslation } from '../../../hooks/useTranslation/useTranslation'\nimport { kbdStr } from '../../../kbd-utils'\nimport { Spinner } from '../../Spinner'\nimport { TldrawUiButton } from '../Button/TldrawUiButton'\nimport { TldrawUiButtonIcon } from '../Button/TldrawUiButtonIcon'\nimport { TldrawUiButtonLabel } from '../Button/TldrawUiButtonLabel'\nimport { TldrawUiDropdownMenuItem } from '../TldrawUiDropdownMenu'\nimport { TLUiIconJsx } from '../TldrawUiIcon'\nimport { TldrawUiKbd } from '../TldrawUiKbd'\nimport { TldrawUiToolbarButton } from '../TldrawUiToolbar'\nimport { hideAllTooltips } from '../TldrawUiTooltip'\nimport { useTldrawUiMenuContext } from './TldrawUiMenuContext'\n\n/** @public */\nexport interface TLUiMenuItemProps<\n\tTranslationKey extends string = string,\n\tIconType extends string = string,\n> {\n\tid: string\n\t/**\n\t * The icon to display on the item. Icons are only shown in certain menu types.\n\t */\n\ticon?: IconType | TLUiIconJsx\n\t/**\n\t * An icon to display to the left of the menu item.\n\t */\n\ticonLeft?: IconType | TLUiIconJsx\n\t/**\n\t * The keyboard shortcut to display on the item.\n\t */\n\tkbd?: string\n\t/**\n\t * The label to display on the item. If it's a string, it will be translated. If it's an object, the keys will be used as the language keys and the values will be translated.\n\t */\n\tlabel?: TranslationKey | { [key: string]: TranslationKey }\n\t/**\n\t * If the editor is in readonly mode and the item is not marked as readonlyok, it will not be rendered.\n\t */\n\treadonlyOk?: boolean\n\t/**\n\t * The function to call when the item is clicked.\n\t */\n\tonSelect(source: TLUiEventSource): Promise<void> | void\n\t/**\n\t * Whether this item should be disabled.\n\t */\n\tdisabled?: boolean\n\t/**\n\t * Prevent the menu from closing when the item is clicked\n\t */\n\tnoClose?: boolean\n\t/**\n\t * Whether to show a spinner on the item.\n\t */\n\tspinner?: boolean\n\t/**\n\t * Whether the item is selected.\n\t */\n\tisSelected?: boolean\n\t/**\n\t * The function to call when the item is dragged. If this is provided, the item will be draggable.\n\t */\n\tonDragStart?(source: TLUiEventSource, info: TLPointerEventInfo): void\n}\n\n/** @public @react */\nexport function TldrawUiMenuItem<\n\tTranslationKey extends string = string,\n\tIconType extends string = string,\n>({\n\tdisabled = false,\n\tspinner = false,\n\treadonlyOk = false,\n\tid,\n\tkbd,\n\tlabel,\n\ticon,\n\ticonLeft,\n\tonSelect,\n\tnoClose,\n\tisSelected,\n\tonDragStart,\n}: TLUiMenuItemProps<TranslationKey, IconType>) {\n\tconst { type: menuType, sourceId } = useTldrawUiMenuContext()\n\n\tconst msg = useTranslation()\n\n\tconst [disableClicks, setDisableClicks] = useState(false)\n\n\tconst isReadonlyMode = useReadonly()\n\tif (isReadonlyMode && !readonlyOk) return null\n\n\tconst labelToUse = unwrapLabel(label, menuType)\n\tconst kbdToUse = kbd ? kbdStr(kbd) : undefined\n\n\tconst labelStr = labelToUse ? msg(labelToUse as TLUiTranslationKey) : undefined\n\tconst titleStr = labelStr && kbdToUse ? `${labelStr} ${kbdToUse}` : labelStr\n\n\tswitch (menuType) {\n\t\tcase 'menu': {\n\t\t\treturn (\n\t\t\t\t<TldrawUiDropdownMenuItem>\n\t\t\t\t\t<TldrawUiButton\n\t\t\t\t\t\ttype=\"menu\"\n\t\t\t\t\t\tdata-testid={`${sourceId}.${id}`}\n\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\tonClick={(e) => {\n\t\t\t\t\t\t\tif (noClose) {\n\t\t\t\t\t\t\t\tpreventDefault(e)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (disableClicks) {\n\t\t\t\t\t\t\t\tsetDisableClicks(false)\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tonSelect(sourceId)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{iconLeft && <TldrawUiButtonIcon icon={iconLeft} small />}\n\t\t\t\t\t\t<TldrawUiButtonLabel>{labelStr}</TldrawUiButtonLabel>\n\t\t\t\t\t\t{kbd && <TldrawUiKbd>{kbd}</TldrawUiKbd>}\n\t\t\t\t\t\t{icon && <TldrawUiButtonIcon icon={icon} small />}\n\t\t\t\t\t</TldrawUiButton>\n\t\t\t\t</TldrawUiDropdownMenuItem>\n\t\t\t)\n\t\t}\n\t\tcase 'context-menu': {\n\t\t\t// Hide disabled context menu items\n\t\t\tif (disabled) return null\n\n\t\t\treturn (\n\t\t\t\t<_ContextMenu.Item\n\t\t\t\t\tdir=\"ltr\"\n\t\t\t\t\tdraggable={false}\n\t\t\t\t\tclassName=\"tlui-button tlui-button__menu\"\n\t\t\t\t\tdata-testid={`${sourceId}.${id}`}\n\t\t\t\t\tonSelect={(e) => {\n\t\t\t\t\t\tif (noClose) preventDefault(e)\n\t\t\t\t\t\tif (disableClicks) {\n\t\t\t\t\t\t\tsetDisableClicks(false)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tonSelect(sourceId)\n\t\t\t\t\t\t}\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<span className=\"tlui-button__label\" draggable={false}>\n\t\t\t\t\t\t{labelStr}\n\t\t\t\t\t</span>\n\t\t\t\t\t{iconLeft && <TldrawUiButtonIcon icon={iconLeft} small />}\n\t\t\t\t\t{kbd && <TldrawUiKbd>{kbd}</TldrawUiKbd>}\n\t\t\t\t\t{spinner && <Spinner />}\n\t\t\t\t</_ContextMenu.Item>\n\t\t\t)\n\t\t}\n\t\tcase 'small-icons':\n\t\tcase 'icons': {\n\t\t\treturn (\n\t\t\t\t<TldrawUiToolbarButton\n\t\t\t\t\tdata-testid={`${sourceId}.${id}`}\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\ttitle={titleStr}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\tonClick={() => onSelect(sourceId)}\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiButtonIcon icon={icon!} small />\n\t\t\t\t</TldrawUiToolbarButton>\n\t\t\t)\n\t\t}\n\t\tcase 'keyboard-shortcuts': {\n\t\t\tif (!kbd) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Menu item '${label}' isn't shown in the keyboard shortcuts dialog because it doesn't have a keyboard shortcut.`\n\t\t\t\t)\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\treturn (\n\t\t\t\t<div className=\"tlui-shortcuts-dialog__key-pair\" data-testid={`${sourceId}.${id}`}>\n\t\t\t\t\t<div className=\"tlui-shortcuts-dialog__key-pair__key\">{labelStr}</div>\n\t\t\t\t\t<div className=\"tlui-shortcuts-dialog__key-pair__value\">\n\t\t\t\t\t\t<TldrawUiKbd visibleOnMobileLayout>{kbd}</TldrawUiKbd>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)\n\t\t}\n\t\tcase 'helper-buttons': {\n\t\t\treturn (\n\t\t\t\t<TldrawUiButton type=\"low\" onClick={() => onSelect(sourceId)}>\n\t\t\t\t\t<TldrawUiButtonIcon icon={icon!} />\n\t\t\t\t\t<TldrawUiButtonLabel>{labelStr}</TldrawUiButtonLabel>\n\t\t\t\t</TldrawUiButton>\n\t\t\t)\n\t\t}\n\t\tcase 'toolbar': {\n\t\t\tif (onDragStart) {\n\t\t\t\treturn (\n\t\t\t\t\t<DraggableToolbarButton\n\t\t\t\t\t\tid={id}\n\t\t\t\t\t\ticon={icon}\n\t\t\t\t\t\tonSelect={onSelect}\n\t\t\t\t\t\tonDragStart={onDragStart}\n\t\t\t\t\t\tlabelStr={labelStr}\n\t\t\t\t\t\ttitleStr={titleStr}\n\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\tisSelected={isSelected}\n\t\t\t\t\t/>\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn (\n\t\t\t\t<TldrawUiToolbarButton\n\t\t\t\t\taria-label={labelStr}\n\t\t\t\t\taria-pressed={isSelected ? 'true' : 'false'}\n\t\t\t\t\tdata-testid={`tools.${id}`}\n\t\t\t\t\tdata-value={id}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\tonClick={() => onSelect('toolbar')}\n\t\t\t\t\tonTouchStart={(e) => {\n\t\t\t\t\t\tpreventDefault(e)\n\t\t\t\t\t\tonSelect('toolbar')\n\t\t\t\t\t}}\n\t\t\t\t\ttitle={titleStr}\n\t\t\t\t\ttype=\"tool\"\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiButtonIcon icon={icon!} />\n\t\t\t\t</TldrawUiToolbarButton>\n\t\t\t)\n\t\t}\n\t\tcase 'toolbar-overflow': {\n\t\t\tif (onDragStart) {\n\t\t\t\treturn (\n\t\t\t\t\t<DraggableToolbarButton\n\t\t\t\t\t\tid={id}\n\t\t\t\t\t\ticon={icon}\n\t\t\t\t\t\tonSelect={onSelect}\n\t\t\t\t\t\tonDragStart={onDragStart}\n\t\t\t\t\t\tlabelStr={labelStr}\n\t\t\t\t\t\ttitleStr={titleStr}\n\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\tisSelected={isSelected}\n\t\t\t\t\t\toverflow\n\t\t\t\t\t/>\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn (\n\t\t\t\t<TldrawUiToolbarButton\n\t\t\t\t\taria-label={labelStr}\n\t\t\t\t\taria-pressed={isSelected ? 'true' : 'false'}\n\t\t\t\t\tisActive={isSelected}\n\t\t\t\t\tdata-testid={`tools.more.${id}`}\n\t\t\t\t\tdata-value={id}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\tonClick={() => onSelect('toolbar')}\n\t\t\t\t\ttitle={titleStr}\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiButtonIcon icon={icon!} />\n\t\t\t\t</TldrawUiToolbarButton>\n\t\t\t)\n\t\t}\n\t\tdefault: {\n\t\t\tthrow exhaustiveSwitchError(menuType)\n\t\t}\n\t}\n}\n\nfunction useDraggableEvents(\n\tonDragStart: TLUiToolItem['onDragStart'],\n\tonSelect: TLUiToolItem['onSelect']\n) {\n\tconst editor = useEditor()\n\tconst events = useMemo(() => {\n\t\tlet state = { name: 'idle' } as\n\t\t\t| {\n\t\t\t\t\tname: 'idle'\n\t\t\t }\n\t\t\t| {\n\t\t\t\t\tname: 'pointing'\n\t\t\t\t\tscreenSpaceStart: VecModel\n\t\t\t }\n\t\t\t| {\n\t\t\t\t\tname: 'dragging'\n\t\t\t\t\tscreenSpaceStart: VecModel\n\t\t\t }\n\t\t\t| {\n\t\t\t\t\tname: 'dragged'\n\t\t\t }\n\n\t\tfunction handlePointerDown(e: React.PointerEvent<HTMLButtonElement>) {\n\t\t\tstate = {\n\t\t\t\tname: 'pointing',\n\t\t\t\tscreenSpaceStart: { x: e.clientX, y: e.clientY },\n\t\t\t}\n\n\t\t\te.currentTarget.setPointerCapture(e.pointerId)\n\t\t}\n\n\t\tfunction handlePointerMove(e: React.PointerEvent<HTMLButtonElement>) {\n\t\t\tif ((e as any).isSpecialRedispatchedEvent) return\n\n\t\t\tif (state.name === 'pointing') {\n\t\t\t\tconst distanceSq = Vec.Dist2(state.screenSpaceStart, { x: e.clientX, y: e.clientY })\n\t\t\t\tif (\n\t\t\t\t\tdistanceSq >\n\t\t\t\t\t(editor.getInstanceState().isCoarsePointer\n\t\t\t\t\t\t? editor.options.uiCoarseDragDistanceSquared\n\t\t\t\t\t\t: editor.options.uiDragDistanceSquared)\n\t\t\t\t) {\n\t\t\t\t\tconst screenSpaceStart = state.screenSpaceStart\n\t\t\t\t\tstate = {\n\t\t\t\t\t\tname: 'dragging',\n\t\t\t\t\t\tscreenSpaceStart,\n\t\t\t\t\t}\n\n\t\t\t\t\teditor.run(() => {\n\t\t\t\t\t\teditor.setCurrentTool('select')\n\n\t\t\t\t\t\t// Set origin point\n\t\t\t\t\t\teditor.dispatch({\n\t\t\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\t\t\tname: 'pointer_down',\n\t\t\t\t\t\t\t...getPointerInfo(editor, e),\n\t\t\t\t\t\t\tpoint: screenSpaceStart,\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\t// Pointer down potentially selects shapes, so we need to deselect them.\n\t\t\t\t\t\teditor.selectNone()\n\n\t\t\t\t\t\t// start drag\n\t\t\t\t\t\tonDragStart?.('toolbar', {\n\t\t\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\t\t\tname: 'pointer_move',\n\t\t\t\t\t\t\t...getPointerInfo(editor, e),\n\t\t\t\t\t\t\tpoint: screenSpaceStart,\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\thideAllTooltips()\n\t\t\t\t\t\teditor.getContainer().focus()\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfunction handlePointerUp(e: React.PointerEvent<HTMLButtonElement>) {\n\t\t\tif ((e as any).isSpecialRedispatchedEvent) return\n\n\t\t\te.currentTarget.releasePointerCapture(e.pointerId)\n\n\t\t\teditor.dispatch({\n\t\t\t\ttype: 'pointer',\n\t\t\t\ttarget: 'canvas',\n\t\t\t\tname: 'pointer_up',\n\t\t\t\t...getPointerInfo(editor, e),\n\t\t\t})\n\t\t}\n\n\t\tfunction handleClick() {\n\t\t\tif (state.name === 'dragging' || state.name === 'dragged') {\n\t\t\t\tstate = { name: 'idle' }\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\tstate = { name: 'idle' }\n\t\t\tonSelect?.('toolbar')\n\t\t}\n\n\t\treturn {\n\t\t\tonPointerDown: handlePointerDown,\n\t\t\tonPointerMove: handlePointerMove,\n\t\t\tonPointerUp: handlePointerUp,\n\t\t\tonClick: handleClick,\n\t\t}\n\t}, [onDragStart, editor, onSelect])\n\n\treturn events\n}\n\nfunction DraggableToolbarButton({\n\tid,\n\tlabelStr,\n\ttitleStr,\n\tdisabled,\n\tisSelected,\n\ticon,\n\tonSelect,\n\tonDragStart,\n\toverflow,\n}: {\n\tid: string\n\tdisabled: boolean\n\tlabelStr?: string\n\ttitleStr?: string\n\tisSelected?: boolean\n\ticon: TLUiMenuItemProps['icon']\n\tonSelect: TLUiMenuItemProps['onSelect']\n\tonDragStart: TLUiMenuItemProps['onDragStart']\n\toverflow?: boolean\n}) {\n\tconst events = useDraggableEvents(onDragStart, onSelect)\n\n\tif (overflow) {\n\t\treturn (\n\t\t\t<TldrawUiToolbarButton\n\t\t\t\taria-label={labelStr}\n\t\t\t\taria-pressed={isSelected ? 'true' : 'false'}\n\t\t\t\tisActive={isSelected}\n\t\t\t\tclassName=\"tlui-button-grid__button\"\n\t\t\t\tdata-testid={`tools.more.${id}`}\n\t\t\t\tdata-value={id}\n\t\t\t\tdisabled={disabled}\n\t\t\t\ttitle={titleStr}\n\t\t\t\ttype=\"icon\"\n\t\t\t\t{...events}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon icon={icon!} />\n\t\t\t</TldrawUiToolbarButton>\n\t\t)\n\t}\n\n\treturn (\n\t\t<TldrawUiToolbarButton\n\t\t\taria-label={labelStr}\n\t\t\taria-pressed={isSelected ? 'true' : 'false'}\n\t\t\tdata-testid={`tools.${id}`}\n\t\t\tdata-value={id}\n\t\t\tdisabled={disabled}\n\t\t\tonTouchStart={(e) => {\n\t\t\t\tpreventDefault(e)\n\t\t\t\tonSelect('toolbar')\n\t\t\t}}\n\t\t\ttitle={titleStr}\n\t\t\ttype=\"tool\"\n\t\t\t{...events}\n\t\t>\n\t\t\t<TldrawUiButtonIcon icon={icon!} />\n\t\t</TldrawUiToolbarButton>\n\t)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const version = "4.3.0-canary.
|
|
1
|
+
const version = "4.3.0-canary.b9cf1eed518f";
|
|
2
2
|
const publishDates = {
|
|
3
3
|
major: "2025-09-18T14:39:22.803Z",
|
|
4
|
-
minor: "2025-
|
|
5
|
-
patch: "2025-
|
|
4
|
+
minor: "2025-12-07T14:49:42.949Z",
|
|
5
|
+
patch: "2025-12-07T14:49:42.949Z"
|
|
6
6
|
};
|
|
7
7
|
export {
|
|
8
8
|
publishDates,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/ui/version.ts"],
|
|
4
|
-
"sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '4.3.0-canary.
|
|
4
|
+
"sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '4.3.0-canary.b9cf1eed518f'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-12-07T14:49:42.949Z',\n\tpatch: '2025-12-07T14:49:42.949Z',\n}\n"],
|
|
5
5
|
"mappings": "AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
generateJSON,
|
|
5
5
|
generateText
|
|
6
6
|
} from "@tiptap/core";
|
|
7
|
-
import Code from "@tiptap/extension-code";
|
|
8
|
-
import Highlight from "@tiptap/extension-highlight";
|
|
9
|
-
import StarterKit from "@tiptap/starter-kit";
|
|
7
|
+
import { Code } from "@tiptap/extension-code";
|
|
8
|
+
import { Highlight } from "@tiptap/extension-highlight";
|
|
9
|
+
import { StarterKit } from "@tiptap/starter-kit";
|
|
10
10
|
import {
|
|
11
11
|
getOwnProperty,
|
|
12
12
|
WeakCache
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/utils/text/richText.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tExtension,\n\tExtensions,\n\tgenerateHTML,\n\tgenerateJSON,\n\tgenerateText,\n\tJSONContent,\n} from '@tiptap/core'\nimport Code from '@tiptap/extension-code'\nimport Highlight from '@tiptap/extension-highlight'\nimport { Node } from '@tiptap/pm/model'\nimport StarterKit from '@tiptap/starter-kit'\nimport {\n\tEditor,\n\tgetOwnProperty,\n\tRichTextFontVisitorState,\n\tTLFontFace,\n\tTLRichText,\n\tWeakCache,\n} from '@tldraw/editor'\nimport { DefaultFontFaces } from '../../shapes/shared/defaultFonts'\nimport { TextDirection } from './textDirection'\n\n/** @public */\nexport const KeyboardShiftEnterTweakExtension = Extension.create({\n\tname: 'keyboardShiftEnterHandler',\n\taddKeyboardShortcuts() {\n\t\treturn {\n\t\t\t// We don't support soft breaks, so we just use the default enter command.\n\t\t\t'Shift-Enter': ({ editor }) => editor.commands.enter(),\n\t\t}\n\t},\n})\n\n// We change the default Code to override what's in the StarterKit.\n// It allows for other attributes/extensions.\n// @ts-ignore this is fine.\nCode.config.excludes = undefined\n\n// We want the highlighting to take precedence over bolding/italics/links\n// as far as rendering is concerned. Otherwise, the highlighting\n// looks broken up.\nHighlight.config.priority = 1100\n\n/**\n * Default extensions for the TipTap editor.\n *\n * @public\n */\nexport const tipTapDefaultExtensions: Extensions = [\n\tStarterKit.configure({\n\t\tblockquote: false,\n\t\tcodeBlock: false,\n\t\thorizontalRule: false,\n\t\tlink: {\n\t\t\topenOnClick: false,\n\t\t\tautolink: true,\n\t\t},\n\t}),\n\tHighlight,\n\tKeyboardShiftEnterTweakExtension,\n\tTextDirection,\n]\n\n// todo: bust this if the editor changes, too\nconst htmlCache = new WeakCache<TLRichText, string>()\n\n/**\n * Renders HTML from a rich text string.\n *\n * @param editor - The editor instance.\n * @param richText - The rich text content.\n *\n * @public\n */\nexport function renderHtmlFromRichText(editor: Editor, richText: TLRichText) {\n\treturn htmlCache.get(richText, () => {\n\t\tconst tipTapExtensions =\n\t\t\teditor.getTextOptions().tipTapConfig?.extensions ?? tipTapDefaultExtensions\n\t\tconst html = generateHTML(richText as JSONContent, tipTapExtensions)\n\t\t// We replace empty paragraphs with a single line break to prevent the browser from collapsing them.\n\t\treturn html.replaceAll('<p dir=\"auto\"></p>', '<p><br /></p>') ?? ''\n\t})\n}\n\n/**\n * Renders HTML from a rich text string for measurement.\n * @param editor - The editor instance.\n * @param richText - The rich text content.\n *\n *\n * @public\n */\nexport function renderHtmlFromRichTextForMeasurement(editor: Editor, richText: TLRichText) {\n\tconst html = renderHtmlFromRichText(editor, richText)\n\treturn `<div class=\"tl-rich-text\">${html}</div>`\n}\n\n// A weak cache used to store plaintext that's been extracted from rich text.\nconst plainTextFromRichTextCache = new WeakCache<TLRichText, string>()\n\nexport function isEmptyRichText(richText: TLRichText) {\n\tif (richText.content.length === 1) {\n\t\tif (!(richText.content[0] as any).content) return true\n\t}\n\treturn false\n}\n\n/**\n * Renders plaintext from a rich text string.\n * @param editor - The editor instance.\n * @param richText - The rich text content.\n *\n *\n * @public\n */\nexport function renderPlaintextFromRichText(editor: Editor, richText: TLRichText) {\n\tif (isEmptyRichText(richText)) return ''\n\n\treturn plainTextFromRichTextCache.get(richText, () => {\n\t\tconst tipTapExtensions =\n\t\t\teditor.getTextOptions().tipTapConfig?.extensions ?? tipTapDefaultExtensions\n\t\treturn generateText(richText as JSONContent, tipTapExtensions, {\n\t\t\tblockSeparator: '\\n',\n\t\t})\n\t})\n}\n\n/**\n * Renders JSONContent from html.\n * @param editor - The editor instance.\n * @param richText - The rich text content.\n *\n *\n * @public\n */\nexport function renderRichTextFromHTML(editor: Editor, html: string): TLRichText {\n\tconst tipTapExtensions =\n\t\teditor.getTextOptions().tipTapConfig?.extensions ?? tipTapDefaultExtensions\n\treturn generateJSON(html, tipTapExtensions) as TLRichText\n}\n\n/** @public */\nexport function defaultAddFontsFromNode(\n\tnode: Node,\n\tstate: RichTextFontVisitorState,\n\taddFont: (font: TLFontFace) => void\n) {\n\tfor (const mark of node.marks) {\n\t\tif (mark.type.name === 'bold' && state.weight !== 'bold') {\n\t\t\tstate = { ...state, weight: 'bold' }\n\t\t}\n\t\tif (mark.type.name === 'italic' && state.style !== 'italic') {\n\t\t\tstate = { ...state, style: 'italic' }\n\t\t}\n\t\tif (mark.type.name === 'code' && state.family !== 'tldraw_mono') {\n\t\t\tstate = { ...state, family: 'tldraw_mono' }\n\t\t}\n\t}\n\n\tconst fontsForFamily = getOwnProperty(DefaultFontFaces, state.family)\n\tif (!fontsForFamily) return state\n\n\tconst fontsForStyle = getOwnProperty(fontsForFamily, state.style)\n\tif (!fontsForStyle) return state\n\n\tconst fontsForWeight = getOwnProperty(fontsForStyle, state.weight)\n\tif (!fontsForWeight) return state\n\n\taddFont(fontsForWeight)\n\n\treturn state\n}\n"],
|
|
5
|
-
"mappings": "AAAA;AAAA,EACC;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OAEM;AACP,
|
|
4
|
+
"sourcesContent": ["import {\n\tExtension,\n\tExtensions,\n\tgenerateHTML,\n\tgenerateJSON,\n\tgenerateText,\n\tJSONContent,\n} from '@tiptap/core'\nimport { Code } from '@tiptap/extension-code'\nimport { Highlight } from '@tiptap/extension-highlight'\nimport { Node } from '@tiptap/pm/model'\nimport { StarterKit } from '@tiptap/starter-kit'\nimport {\n\tEditor,\n\tgetOwnProperty,\n\tRichTextFontVisitorState,\n\tTLFontFace,\n\tTLRichText,\n\tWeakCache,\n} from '@tldraw/editor'\nimport { DefaultFontFaces } from '../../shapes/shared/defaultFonts'\nimport { TextDirection } from './textDirection'\n\n/** @public */\nexport const KeyboardShiftEnterTweakExtension = Extension.create({\n\tname: 'keyboardShiftEnterHandler',\n\taddKeyboardShortcuts() {\n\t\treturn {\n\t\t\t// We don't support soft breaks, so we just use the default enter command.\n\t\t\t'Shift-Enter': ({ editor }) => editor.commands.enter(),\n\t\t}\n\t},\n})\n\n// We change the default Code to override what's in the StarterKit.\n// It allows for other attributes/extensions.\n// @ts-ignore this is fine.\nCode.config.excludes = undefined\n\n// We want the highlighting to take precedence over bolding/italics/links\n// as far as rendering is concerned. Otherwise, the highlighting\n// looks broken up.\nHighlight.config.priority = 1100\n\n/**\n * Default extensions for the TipTap editor.\n *\n * @public\n */\nexport const tipTapDefaultExtensions: Extensions = [\n\tStarterKit.configure({\n\t\tblockquote: false,\n\t\tcodeBlock: false,\n\t\thorizontalRule: false,\n\t\tlink: {\n\t\t\topenOnClick: false,\n\t\t\tautolink: true,\n\t\t},\n\t}),\n\tHighlight,\n\tKeyboardShiftEnterTweakExtension,\n\tTextDirection,\n]\n\n// todo: bust this if the editor changes, too\nconst htmlCache = new WeakCache<TLRichText, string>()\n\n/**\n * Renders HTML from a rich text string.\n *\n * @param editor - The editor instance.\n * @param richText - The rich text content.\n *\n * @public\n */\nexport function renderHtmlFromRichText(editor: Editor, richText: TLRichText) {\n\treturn htmlCache.get(richText, () => {\n\t\tconst tipTapExtensions =\n\t\t\teditor.getTextOptions().tipTapConfig?.extensions ?? tipTapDefaultExtensions\n\t\tconst html = generateHTML(richText as JSONContent, tipTapExtensions)\n\t\t// We replace empty paragraphs with a single line break to prevent the browser from collapsing them.\n\t\treturn html.replaceAll('<p dir=\"auto\"></p>', '<p><br /></p>') ?? ''\n\t})\n}\n\n/**\n * Renders HTML from a rich text string for measurement.\n * @param editor - The editor instance.\n * @param richText - The rich text content.\n *\n *\n * @public\n */\nexport function renderHtmlFromRichTextForMeasurement(editor: Editor, richText: TLRichText) {\n\tconst html = renderHtmlFromRichText(editor, richText)\n\treturn `<div class=\"tl-rich-text\">${html}</div>`\n}\n\n// A weak cache used to store plaintext that's been extracted from rich text.\nconst plainTextFromRichTextCache = new WeakCache<TLRichText, string>()\n\nexport function isEmptyRichText(richText: TLRichText) {\n\tif (richText.content.length === 1) {\n\t\tif (!(richText.content[0] as any).content) return true\n\t}\n\treturn false\n}\n\n/**\n * Renders plaintext from a rich text string.\n * @param editor - The editor instance.\n * @param richText - The rich text content.\n *\n *\n * @public\n */\nexport function renderPlaintextFromRichText(editor: Editor, richText: TLRichText) {\n\tif (isEmptyRichText(richText)) return ''\n\n\treturn plainTextFromRichTextCache.get(richText, () => {\n\t\tconst tipTapExtensions =\n\t\t\teditor.getTextOptions().tipTapConfig?.extensions ?? tipTapDefaultExtensions\n\t\treturn generateText(richText as JSONContent, tipTapExtensions, {\n\t\t\tblockSeparator: '\\n',\n\t\t})\n\t})\n}\n\n/**\n * Renders JSONContent from html.\n * @param editor - The editor instance.\n * @param richText - The rich text content.\n *\n *\n * @public\n */\nexport function renderRichTextFromHTML(editor: Editor, html: string): TLRichText {\n\tconst tipTapExtensions =\n\t\teditor.getTextOptions().tipTapConfig?.extensions ?? tipTapDefaultExtensions\n\treturn generateJSON(html, tipTapExtensions) as TLRichText\n}\n\n/** @public */\nexport function defaultAddFontsFromNode(\n\tnode: Node,\n\tstate: RichTextFontVisitorState,\n\taddFont: (font: TLFontFace) => void\n) {\n\tfor (const mark of node.marks) {\n\t\tif (mark.type.name === 'bold' && state.weight !== 'bold') {\n\t\t\tstate = { ...state, weight: 'bold' }\n\t\t}\n\t\tif (mark.type.name === 'italic' && state.style !== 'italic') {\n\t\t\tstate = { ...state, style: 'italic' }\n\t\t}\n\t\tif (mark.type.name === 'code' && state.family !== 'tldraw_mono') {\n\t\t\tstate = { ...state, family: 'tldraw_mono' }\n\t\t}\n\t}\n\n\tconst fontsForFamily = getOwnProperty(DefaultFontFaces, state.family)\n\tif (!fontsForFamily) return state\n\n\tconst fontsForStyle = getOwnProperty(fontsForFamily, state.style)\n\tif (!fontsForStyle) return state\n\n\tconst fontsForWeight = getOwnProperty(fontsForStyle, state.weight)\n\tif (!fontsForWeight) return state\n\n\taddFont(fontsForWeight)\n\n\treturn state\n}\n"],
|
|
5
|
+
"mappings": "AAAA;AAAA,EACC;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OAEM;AACP,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAE1B,SAAS,kBAAkB;AAC3B;AAAA,EAEC;AAAA,EAIA;AAAA,OACM;AACP,SAAS,wBAAwB;AACjC,SAAS,qBAAqB;AAGvB,MAAM,mCAAmC,UAAU,OAAO;AAAA,EAChE,MAAM;AAAA,EACN,uBAAuB;AACtB,WAAO;AAAA;AAAA,MAEN,eAAe,CAAC,EAAE,OAAO,MAAM,OAAO,SAAS,MAAM;AAAA,IACtD;AAAA,EACD;AACD,CAAC;AAKD,KAAK,OAAO,WAAW;AAKvB,UAAU,OAAO,WAAW;AAOrB,MAAM,0BAAsC;AAAA,EAClD,WAAW,UAAU;AAAA,IACpB,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,gBAAgB;AAAA,IAChB,MAAM;AAAA,MACL,aAAa;AAAA,MACb,UAAU;AAAA,IACX;AAAA,EACD,CAAC;AAAA,EACD;AAAA,EACA;AAAA,EACA;AACD;AAGA,MAAM,YAAY,IAAI,UAA8B;AAU7C,SAAS,uBAAuB,QAAgB,UAAsB;AAC5E,SAAO,UAAU,IAAI,UAAU,MAAM;AACpC,UAAM,mBACL,OAAO,eAAe,EAAE,cAAc,cAAc;AACrD,UAAM,OAAO,aAAa,UAAyB,gBAAgB;AAEnE,WAAO,KAAK,WAAW,sBAAsB,eAAe,KAAK;AAAA,EAClE,CAAC;AACF;AAUO,SAAS,qCAAqC,QAAgB,UAAsB;AAC1F,QAAM,OAAO,uBAAuB,QAAQ,QAAQ;AACpD,SAAO,6BAA6B,IAAI;AACzC;AAGA,MAAM,6BAA6B,IAAI,UAA8B;AAE9D,SAAS,gBAAgB,UAAsB;AACrD,MAAI,SAAS,QAAQ,WAAW,GAAG;AAClC,QAAI,CAAE,SAAS,QAAQ,CAAC,EAAU,QAAS,QAAO;AAAA,EACnD;AACA,SAAO;AACR;AAUO,SAAS,4BAA4B,QAAgB,UAAsB;AACjF,MAAI,gBAAgB,QAAQ,EAAG,QAAO;AAEtC,SAAO,2BAA2B,IAAI,UAAU,MAAM;AACrD,UAAM,mBACL,OAAO,eAAe,EAAE,cAAc,cAAc;AACrD,WAAO,aAAa,UAAyB,kBAAkB;AAAA,MAC9D,gBAAgB;AAAA,IACjB,CAAC;AAAA,EACF,CAAC;AACF;AAUO,SAAS,uBAAuB,QAAgB,MAA0B;AAChF,QAAM,mBACL,OAAO,eAAe,EAAE,cAAc,cAAc;AACrD,SAAO,aAAa,MAAM,gBAAgB;AAC3C;AAGO,SAAS,wBACf,MACA,OACA,SACC;AACD,aAAW,QAAQ,KAAK,OAAO;AAC9B,QAAI,KAAK,KAAK,SAAS,UAAU,MAAM,WAAW,QAAQ;AACzD,cAAQ,EAAE,GAAG,OAAO,QAAQ,OAAO;AAAA,IACpC;AACA,QAAI,KAAK,KAAK,SAAS,YAAY,MAAM,UAAU,UAAU;AAC5D,cAAQ,EAAE,GAAG,OAAO,OAAO,SAAS;AAAA,IACrC;AACA,QAAI,KAAK,KAAK,SAAS,UAAU,MAAM,WAAW,eAAe;AAChE,cAAQ,EAAE,GAAG,OAAO,QAAQ,cAAc;AAAA,IAC3C;AAAA,EACD;AAEA,QAAM,iBAAiB,eAAe,kBAAkB,MAAM,MAAM;AACpE,MAAI,CAAC,eAAgB,QAAO;AAE5B,QAAM,gBAAgB,eAAe,gBAAgB,MAAM,KAAK;AAChE,MAAI,CAAC,cAAe,QAAO;AAE3B,QAAM,iBAAiB,eAAe,eAAe,MAAM,MAAM;AACjE,MAAI,CAAC,eAAgB,QAAO;AAE5B,UAAQ,cAAc;AAEtB,SAAO;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tldraw",
|
|
3
3
|
"description": "A tiny little drawing editor.",
|
|
4
|
-
"version": "4.3.0-canary.
|
|
4
|
+
"version": "4.3.0-canary.b9cf1eed518f",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
"@tiptap/pm": "^3.6.2",
|
|
63
63
|
"@tiptap/react": "^3.6.2",
|
|
64
64
|
"@tiptap/starter-kit": "^3.6.2",
|
|
65
|
-
"@tldraw/editor": "4.3.0-canary.
|
|
66
|
-
"@tldraw/store": "4.3.0-canary.
|
|
65
|
+
"@tldraw/editor": "4.3.0-canary.b9cf1eed518f",
|
|
66
|
+
"@tldraw/store": "4.3.0-canary.b9cf1eed518f",
|
|
67
67
|
"classnames": "^2.5.1",
|
|
68
68
|
"hotkeys-js": "^3.13.9",
|
|
69
69
|
"idb": "^7.1.1",
|
package/src/index.ts
CHANGED
|
@@ -63,7 +63,7 @@ export const TldrawSelectionForeground = track(function TldrawSelectionForegroun
|
|
|
63
63
|
|
|
64
64
|
if (onlyShape && editor.isShapeHidden(onlyShape)) return null
|
|
65
65
|
|
|
66
|
-
const zoom = editor.
|
|
66
|
+
const zoom = editor.getEfficientZoomLevel()
|
|
67
67
|
const isChangingStyle = editor.getInstanceState().isChangingStyle
|
|
68
68
|
|
|
69
69
|
const width = expandedBounds.width
|
|
@@ -532,7 +532,7 @@ export const MobileRotateHandle = function RotateHandle({
|
|
|
532
532
|
const events = useSelectionEvents('mobile_rotate')
|
|
533
533
|
|
|
534
534
|
const editor = useEditor()
|
|
535
|
-
const zoom = useValue('zoom level', () => editor.
|
|
535
|
+
const zoom = useValue('zoom level', () => editor.getEfficientZoomLevel(), [editor])
|
|
536
536
|
const bgRadius = Math.max(14 * (1 / zoom), 20 / Math.max(1, zoom))
|
|
537
537
|
const msg = useTranslation()
|
|
538
538
|
return (
|
|
@@ -45,7 +45,6 @@ import {
|
|
|
45
45
|
useEditor,
|
|
46
46
|
useIsEditing,
|
|
47
47
|
useSharedSafeId,
|
|
48
|
-
useValue,
|
|
49
48
|
} from '@tldraw/editor'
|
|
50
49
|
import React, { useMemo } from 'react'
|
|
51
50
|
import { updateArrowTerminal } from '../../bindings/arrow/ArrowBindingUtil'
|
|
@@ -56,6 +55,7 @@ import { ShapeFill } from '../shared/ShapeFill'
|
|
|
56
55
|
import { ARROW_LABEL_PADDING, STROKE_SIZES, TEXT_PROPS } from '../shared/default-shape-constants'
|
|
57
56
|
import { getFillDefForCanvas, getFillDefForExport } from '../shared/defaultStyleDefs'
|
|
58
57
|
import { useDefaultColorTheme } from '../shared/useDefaultColorTheme'
|
|
58
|
+
import { useEfficientZoomThreshold } from '../shared/useEfficientZoomThreshold'
|
|
59
59
|
import { getArrowBodyPath, getArrowHandlePath } from './ArrowPath'
|
|
60
60
|
import { ArrowShapeOptions } from './arrow-types'
|
|
61
61
|
import {
|
|
@@ -120,6 +120,8 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|
|
120
120
|
|
|
121
121
|
shouldBeExact: (editor: Editor) => editor.inputs.altKey,
|
|
122
122
|
shouldIgnoreTargets: (editor: Editor) => editor.inputs.ctrlKey,
|
|
123
|
+
|
|
124
|
+
showTextOutline: true,
|
|
123
125
|
}
|
|
124
126
|
|
|
125
127
|
override canEdit() {
|
|
@@ -269,7 +271,7 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|
|
269
271
|
|
|
270
272
|
const segmentStart = shapePageTransform.applyToPoint(info.route.midpointHandle.segmentStart)
|
|
271
273
|
const segmentEnd = shapePageTransform.applyToPoint(info.route.midpointHandle.segmentEnd)
|
|
272
|
-
const segmentLength = Vec.Dist(segmentStart, segmentEnd) * this.editor.
|
|
274
|
+
const segmentLength = Vec.Dist(segmentStart, segmentEnd) * this.editor.getEfficientZoomLevel()
|
|
273
275
|
|
|
274
276
|
if (segmentLength > this.options.elbowMinSegmentLengthToShowMidpointHandle) {
|
|
275
277
|
handles.push({
|
|
@@ -369,7 +371,8 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|
|
369
371
|
|
|
370
372
|
// we want to snap to certain points. the maximum distance at which a snap will occur is
|
|
371
373
|
// relative to the zoom level:
|
|
372
|
-
const maxSnapDistance =
|
|
374
|
+
const maxSnapDistance =
|
|
375
|
+
this.options.elbowMidpointSnapDistance / this.editor.getEfficientZoomLevel()
|
|
373
376
|
|
|
374
377
|
// we snap to the midpoint of the range by default
|
|
375
378
|
const midPoint = perpDistanceToLineAngle(
|
|
@@ -794,6 +797,7 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|
|
794
797
|
textWidth={labelPosition.box.w - ARROW_LABEL_PADDING * 2 * shape.props.scale}
|
|
795
798
|
isSelected={isSelected}
|
|
796
799
|
padding={0}
|
|
800
|
+
showTextOutline={this.options.showTextOutline}
|
|
797
801
|
style={{
|
|
798
802
|
transform: `translate(${labelPosition.box.center.x}px, ${labelPosition.box.center.y}px)`,
|
|
799
803
|
}}
|
|
@@ -944,7 +948,7 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|
|
944
948
|
.box.clone()
|
|
945
949
|
.expandBy(-ARROW_LABEL_PADDING * shape.props.scale)}
|
|
946
950
|
padding={0}
|
|
947
|
-
showTextOutline={
|
|
951
|
+
showTextOutline={this.options.showTextOutline}
|
|
948
952
|
/>
|
|
949
953
|
</g>
|
|
950
954
|
)
|
|
@@ -1005,13 +1009,7 @@ const ArrowSvg = track(function ArrowSvg({
|
|
|
1005
1009
|
const editor = useEditor()
|
|
1006
1010
|
const theme = useDefaultColorTheme()
|
|
1007
1011
|
const info = getArrowInfo(editor, shape)
|
|
1008
|
-
const isForceSolid =
|
|
1009
|
-
'force solid',
|
|
1010
|
-
() => {
|
|
1011
|
-
return editor.getZoomLevel() < 0.2
|
|
1012
|
-
},
|
|
1013
|
-
[editor]
|
|
1014
|
-
)
|
|
1012
|
+
const isForceSolid = useEfficientZoomThreshold(shape.props.scale * 0.25)
|
|
1015
1013
|
const clipPathId = useSharedSafeId(shape.id + '_clip')
|
|
1016
1014
|
const arrowheadDotId = useSharedSafeId('arrowhead-dot')
|
|
1017
1015
|
const arrowheadCrossId = useSharedSafeId('arrowhead-cross')
|
|
@@ -1037,7 +1035,7 @@ const ArrowSvg = track(function ArrowSvg({
|
|
|
1037
1035
|
start: 'skip',
|
|
1038
1036
|
end: 'skip',
|
|
1039
1037
|
lengthRatio: 2.5,
|
|
1040
|
-
strokeWidth: 2 / editor.
|
|
1038
|
+
strokeWidth: 2 / editor.getEfficientZoomLevel(),
|
|
1041
1039
|
props: {
|
|
1042
1040
|
className: 'tl-arrow-hint',
|
|
1043
1041
|
markerStart: bindings.start
|
|
@@ -95,6 +95,8 @@ export interface ArrowShapeOptions {
|
|
|
95
95
|
* When creating an arrow, should it bind to the target shape.
|
|
96
96
|
*/
|
|
97
97
|
shouldIgnoreTargets(editor: Editor): boolean
|
|
98
|
+
/** Whether to show the outline of the arrow shape's label (using the same color as the canvas). This helps with overlapping shapes. It does not show up on Safari, where text outline is a performance issues. */
|
|
99
|
+
readonly showTextOutline: boolean
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
/** @public */
|
|
@@ -139,7 +139,7 @@ export class DrawShapeUtil extends ShapeUtil<TLDrawShape> {
|
|
|
139
139
|
const forceSolid = useValue(
|
|
140
140
|
'force solid',
|
|
141
141
|
() => {
|
|
142
|
-
const zoomLevel = this.editor.
|
|
142
|
+
const zoomLevel = this.editor.getEfficientZoomLevel()
|
|
143
143
|
return zoomLevel < 0.5 && zoomLevel < 1.5 / sw
|
|
144
144
|
},
|
|
145
145
|
[this.editor, sw]
|
|
@@ -244,7 +244,7 @@ function DrawShapeSvg({ shape, zoomOverride }: { shape: TLDrawShape; zoomOverrid
|
|
|
244
244
|
const forceSolid = useValue(
|
|
245
245
|
'force solid',
|
|
246
246
|
() => {
|
|
247
|
-
const zoomLevel = zoomOverride ?? editor.
|
|
247
|
+
const zoomLevel = zoomOverride ?? editor.getEfficientZoomLevel()
|
|
248
248
|
return zoomLevel < 0.5 && zoomLevel < 1.5 / sw
|
|
249
249
|
},
|
|
250
250
|
[editor, sw, zoomOverride]
|
|
@@ -253,7 +253,7 @@ function DrawShapeSvg({ shape, zoomOverride }: { shape: TLDrawShape; zoomOverrid
|
|
|
253
253
|
const dotAdjustment = useValue(
|
|
254
254
|
'dot adjustment',
|
|
255
255
|
() => {
|
|
256
|
-
const zoomLevel = zoomOverride ?? editor.
|
|
256
|
+
const zoomLevel = zoomOverride ?? editor.getEfficientZoomLevel()
|
|
257
257
|
// If we're zoomed way out (10%), then we need to make the dotted line go to 9 instead 0.1
|
|
258
258
|
// Chrome doesn't render anything otherwise.
|
|
259
259
|
return zoomLevel < 0.2 ? 0 : 0.1
|
|
@@ -115,7 +115,7 @@ export class FrameShapeUtil extends BaseBoxShapeUtil<TLFrameShape> {
|
|
|
115
115
|
override getGeometry(shape: TLFrameShape): Geometry2d {
|
|
116
116
|
const { editor } = this
|
|
117
117
|
|
|
118
|
-
const z = editor.
|
|
118
|
+
const z = editor.getEfficientZoomLevel()
|
|
119
119
|
|
|
120
120
|
// Which dimension measures the top edge after rotation?
|
|
121
121
|
const labelSide = getFrameHeadingSide(editor, shape)
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
import { getFillDefForCanvas, getFillDefForExport } from '../shared/defaultStyleDefs'
|
|
44
44
|
import { useDefaultColorTheme } from '../shared/useDefaultColorTheme'
|
|
45
45
|
import { useIsReadyForEditing } from '../shared/useEditablePlainText'
|
|
46
|
+
import { useEfficientZoomThreshold } from '../shared/useEfficientZoomThreshold'
|
|
46
47
|
import { GeoShapeBody } from './components/GeoShapeBody'
|
|
47
48
|
import { getGeoShapePath } from './getGeoShapePath'
|
|
48
49
|
|
|
@@ -54,6 +55,10 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
|
|
54
55
|
static override props = geoShapeProps
|
|
55
56
|
static override migrations = geoShapeMigrations
|
|
56
57
|
|
|
58
|
+
override options = {
|
|
59
|
+
showTextOutline: true,
|
|
60
|
+
}
|
|
61
|
+
|
|
57
62
|
override canEdit() {
|
|
58
63
|
return true
|
|
59
64
|
}
|
|
@@ -195,7 +200,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
|
|
195
200
|
const isReadyForEditing = useIsReadyForEditing(editor, shape.id)
|
|
196
201
|
const isEmpty = isEmptyRichText(shape.props.richText)
|
|
197
202
|
const showHtmlContainer = isReadyForEditing || !isEmpty
|
|
198
|
-
const isForceSolid =
|
|
203
|
+
const isForceSolid = useEfficientZoomThreshold(shape.props.scale * 0.25)
|
|
199
204
|
|
|
200
205
|
return (
|
|
201
206
|
<>
|
|
@@ -224,6 +229,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
|
|
224
229
|
isSelected={isOnlySelected}
|
|
225
230
|
labelColor={getColorValue(theme, props.labelColor, 'solid')}
|
|
226
231
|
wrap
|
|
232
|
+
showTextOutline={this.options.showTextOutline}
|
|
227
233
|
/>
|
|
228
234
|
</HTMLContainer>
|
|
229
235
|
)}
|
|
@@ -233,9 +239,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
|
|
233
239
|
}
|
|
234
240
|
|
|
235
241
|
indicator(shape: TLGeoShape) {
|
|
236
|
-
const isZoomedOut =
|
|
237
|
-
this.editor,
|
|
238
|
-
])
|
|
242
|
+
const isZoomedOut = useEfficientZoomThreshold(shape.props.scale * 0.25)
|
|
239
243
|
|
|
240
244
|
const { size, dash, scale } = shape.props
|
|
241
245
|
const strokeWidth = STROKE_SIZES[size]
|
|
@@ -283,6 +287,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
|
|
283
287
|
labelColor={getColorValue(theme, props.labelColor, 'solid')}
|
|
284
288
|
bounds={bounds}
|
|
285
289
|
padding={LABEL_PADDING}
|
|
290
|
+
showTextOutline={this.options.showTextOutline}
|
|
286
291
|
/>
|
|
287
292
|
)
|
|
288
293
|
}
|
|
@@ -315,7 +315,7 @@ function useHighlightForceSolid(editor: Editor, shape: TLHighlightShape) {
|
|
|
315
315
|
'forceSolid',
|
|
316
316
|
() => {
|
|
317
317
|
const sw = getStrokeWidth(shape)
|
|
318
|
-
const zoomLevel = editor.
|
|
318
|
+
const zoomLevel = editor.getEfficientZoomLevel()
|
|
319
319
|
if (sw / zoomLevel < 1.5) {
|
|
320
320
|
return true
|
|
321
321
|
}
|
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
} from '../shared/default-shape-constants'
|
|
51
51
|
import { useDefaultColorTheme } from '../shared/useDefaultColorTheme'
|
|
52
52
|
import { useIsReadyForEditing } from '../shared/useEditablePlainText'
|
|
53
|
+
import { useEfficientZoomThreshold } from '../shared/useEfficientZoomThreshold'
|
|
53
54
|
import {
|
|
54
55
|
CLONE_HANDLE_MARGIN,
|
|
55
56
|
NOTE_CENTER_OFFSET,
|
|
@@ -158,7 +159,7 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
|
|
|
158
159
|
const isCoarsePointer = this.editor.getInstanceState().isCoarsePointer
|
|
159
160
|
if (isCoarsePointer) return []
|
|
160
161
|
|
|
161
|
-
const zoom = this.editor.
|
|
162
|
+
const zoom = this.editor.getEfficientZoomLevel()
|
|
162
163
|
if (zoom * scale < 0.25) return []
|
|
163
164
|
|
|
164
165
|
const nh = getNoteHeight(shape)
|
|
@@ -268,15 +269,12 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
|
|
|
268
269
|
[this.editor]
|
|
269
270
|
)
|
|
270
271
|
|
|
271
|
-
// todo: consider hiding shadows on dark mode if they're invisible anyway
|
|
272
|
-
|
|
273
|
-
const hideShadows = useValue('zoom', () => this.editor.getZoomLevel() < 0.35 / scale, [
|
|
274
|
-
scale,
|
|
275
|
-
this.editor,
|
|
276
|
-
])
|
|
277
|
-
|
|
278
272
|
const isDarkMode = useValue('dark mode', () => this.editor.user.getIsDarkMode(), [this.editor])
|
|
279
273
|
|
|
274
|
+
// Shadows are hidden when zoomed out far enough or in dark mode
|
|
275
|
+
let hideShadows = useEfficientZoomThreshold(scale * 0.25)
|
|
276
|
+
if (isDarkMode) hideShadows = true
|
|
277
|
+
|
|
280
278
|
const isSelected = shape.id === this.editor.getOnlySelectedShapeId()
|
|
281
279
|
|
|
282
280
|
const isReadyForEditing = useIsReadyForEditing(this.editor, shape.id)
|
|
@@ -318,6 +316,7 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
|
|
|
318
316
|
wrap
|
|
319
317
|
padding={LABEL_PADDING * scale}
|
|
320
318
|
hasCustomTabBehavior
|
|
319
|
+
showTextOutline={false}
|
|
321
320
|
onKeyDown={handleKeyDown}
|
|
322
321
|
/>
|
|
323
322
|
)}
|