tldraw 4.4.0 → 4.4.1
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.js +1 -1
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +5 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
- package/dist-cjs/lib/ui/version.js +2 -2
- package/dist-cjs/lib/ui/version.js.map +1 -1
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +5 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
- package/dist-esm/lib/ui/version.mjs +2 -2
- package/dist-esm/lib/ui/version.mjs.map +1 -1
- package/package.json +3 -3
- package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +7 -2
- package/src/lib/ui/version.ts +2 -2
package/dist-cjs/index.js
CHANGED
|
@@ -603,7 +603,7 @@ var import_buildFromV1Document = require("./lib/utils/tldr/buildFromV1Document")
|
|
|
603
603
|
var import_file = require("./lib/utils/tldr/file");
|
|
604
604
|
(0, import_editor.registerTldrawLibraryVersion)(
|
|
605
605
|
"tldraw",
|
|
606
|
-
"4.4.
|
|
606
|
+
"4.4.1",
|
|
607
607
|
"cjs"
|
|
608
608
|
);
|
|
609
609
|
//# sourceMappingURL=index.js.map
|
|
@@ -195,8 +195,11 @@ function TooltipSingleton() {
|
|
|
195
195
|
const activeRect = currentTooltip.targetElement.getBoundingClientRect();
|
|
196
196
|
const trigger = triggerRef.current;
|
|
197
197
|
trigger.style.position = "fixed";
|
|
198
|
-
trigger.style.left =
|
|
199
|
-
trigger.style.top =
|
|
198
|
+
trigger.style.left = "0px";
|
|
199
|
+
trigger.style.top = "0px";
|
|
200
|
+
const cbOffset = trigger.getBoundingClientRect();
|
|
201
|
+
trigger.style.left = `${activeRect.left - cbOffset.left}px`;
|
|
202
|
+
trigger.style.top = `${activeRect.top - cbOffset.top}px`;
|
|
200
203
|
trigger.style.width = `${activeRect.width}px`;
|
|
201
204
|
trigger.style.height = `${activeRect.height}px`;
|
|
202
205
|
trigger.style.pointerEvents = "none";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/ui/components/primitives/TldrawUiTooltip.tsx"],
|
|
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 childElement = child as React.ReactElement<{\n\t\t\tonMouseEnter?(event: React.MouseEvent<HTMLElement>): void\n\t\t\tonMouseLeave?(event: React.MouseEvent<HTMLElement>): void\n\t\t\tonFocus?(event: React.FocusEvent<HTMLElement>): void\n\t\t\tonBlur?(event: React.FocusEvent<HTMLElement>): void\n\t\t}>\n\n\t\tconst handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchildElement.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\tchildElement.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\tchildElement.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\tchildElement.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(childElement, {\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": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmMG;AAnMH,oBAQO;AACP,sBAAoC;AACpC,mBAQO;AACP,oBAAuC;AAEvC,MAAM,2BAA2B;AAuCjC,MAAM,eAAe;AAAA,EACpB,OAAe,WAAkC;AAAA,EACzC,YAAQ,oBAAmB,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAAA,EAEpE,OAAO,cAA8B;AACpC,QAAI,CAAC,eAAe,UAAU;AAC7B,qBAAe,WAAW,IAAI,eAAe;AAAA,IAC9C;AACA,WAAO,eAAe;AAAA,EACvB;AAAA,EAEA,kBAAkB;AACjB,SAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAAA,EACtC;AAAA,EAEA,YAAY,OAAqB;AAChC,UAAM,eAAe,KAAK,MAAM,IAAI;AAEpC,YAAQ,MAAM,MAAM;AAAA,MACnB,KAAK,gBAAgB;AAEpB,YAAI,aAAa,SAAS,mBAAmB;AAC5C,uBAAa,aAAa,SAAS;AAAA,QACpC;AACA,aAAK,MAAM,IAAI,EAAE,MAAM,eAAe,CAAC;AACvC;AAAA,MACD;AAAA,MAEA,KAAK,cAAc;AAElB,YAAI,aAAa,SAAS,gBAAgB;AACzC,eAAK,MAAM,IAAI,EAAE,MAAM,OAAO,CAAC;AAAA,QAChC;AACA;AAAA,MACD;AAAA,MAEA,KAAK,QAAQ;AAEZ,YAAI,aAAa,SAAS,gBAAgB;AACzC;AAAA,QACD;AAGA,YAAI,aAAa,SAAS,mBAAmB;AAC5C,uBAAa,aAAa,SAAS;AAAA,QACpC;AAGA,aAAK,MAAM,IAAI,EAAE,MAAM,WAAW,SAAS,MAAM,QAAQ,CAAC;AAC1D;AAAA,MACD;AAAA,MAEA,KAAK,QAAQ;AACZ,cAAM,EAAE,WAAW,QAAQ,QAAQ,IAAI;AAGvC,YAAI,aAAa,SAAS,aAAa,aAAa,QAAQ,OAAO,WAAW;AAC7E,cAAI,UAAU,CAAC,SAAS;AAEvB,kBAAM,YAAY,OAAO,OAAO,WAAW,MAAM;AAChD,oBAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,kBAAI,MAAM,SAAS,qBAAqB,MAAM,QAAQ,OAAO,WAAW;AACvE,qBAAK,MAAM,IAAI,EAAE,MAAM,OAAO,CAAC;AAAA,cAChC;AAAA,YACD,GAAG,GAAG;AACN,iBAAK,MAAM,IAAI;AAAA,cACd,MAAM;AAAA,cACN,SAAS,aAAa;AAAA,cACtB;AAAA,YACD,CAAC;AAAA,UACF,OAAO;AACN,iBAAK,MAAM,IAAI,EAAE,MAAM,OAAO,CAAC;AAAA,UAChC;AAAA,QACD,WACC,aAAa,SAAS,qBACtB,aAAa,QAAQ,OAAO,WAC3B;AAED,cAAI,SAAS;AACZ,yBAAa,aAAa,SAAS;AACnC,iBAAK,MAAM,IAAI,EAAE,MAAM,OAAO,CAAC;AAAA,UAChC;AAAA,QACD;AACA;AAAA,MACD;AAAA,MAEA,KAAK,YAAY;AAChB,YAAI,aAAa,SAAS,mBAAmB;AAC5C,uBAAa,aAAa,SAAS;AAAA,QACpC;AAEA,YAAI,aAAa,SAAS,gBAAgB;AACzC;AAAA,QACD;AACA,aAAK,MAAM,IAAI,EAAE,MAAM,OAAO,CAAC;AAC/B;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA,EAEA,wBAA4C;AAC3C,UAAM,eAAe,KAAK,MAAM,IAAI;AACpC,QAAI,UAA8B;AAElC,QAAI,aAAa,SAAS,WAAW;AACpC,gBAAU,aAAa;AAAA,IACxB,WAAW,aAAa,SAAS,mBAAmB;AACnD,gBAAU,aAAa;AAAA,IACxB;AAEA,QAAI,CAAC,QAAS,QAAO;AACrB,QAAI,4BAAc,IAAI,EAAE,mBAAmB,CAAC,QAAQ,aAAc,QAAO;AACzE,WAAO;AAAA,EACR;AACD;AAEA,MAAM,iBAAiB,eAAe,YAAY;AAG3C,SAAS,kBAAkB;AACjC,iBAAe,gBAAgB;AAChC;AAGA,MAAM,8BAA0B,4BAAuB,KAAK;AAQrD,SAAS,wBAAwB,EAAE,SAAS,GAAiC;AACnF,SACC,4CAAC,gBAAAA,QAAS,UAAT,EAAkB,mBAAmB,KACrC,uDAAC,wBAAwB,UAAxB,EAAiC,OAAO,MACvC;AAAA;AAAA,IACD,4CAAC,oBAAiB;AAAA,KACnB,GACD;AAEF;AAGA,SAAS,mBAAmB;AAC3B,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAAS,KAAK;AAC1C,QAAM,iBAAa,qBAAuB,IAAI;AAC9C,QAAM,qBAAiB,qBAAO,IAAI;AAClC,QAAM,aAAS,8BAAe;AAE9B,QAAM,qBAAiB;AAAA,IACtB;AAAA,IACA,MAAM,eAAe,sBAAsB;AAAA,IAC3C,CAAC;AAAA,EACF;AAEA,QAAM,kBAAc,wBAAS,gBAAgB,MAAM,QAAQ,eAAe,GAAG,CAAC,MAAM,CAAC;AAGrF,8BAAU,MAAM;AACf,QAAI,gBAAgB,YAAY,UAAU,gBAAgB;AACzD,qBAAe,YAAY;AAAA,QAC1B,MAAM;AAAA,QACN,WAAW,eAAe;AAAA,QAC1B;AAAA,QACA,SAAS;AAAA,MACV,CAAC;AAAA,IACF;AAAA,EACD,GAAG,CAAC,aAAa,QAAQ,gBAAgB,MAAM,CAAC;AAEhD,8BAAU,MAAM;AACf,aAAS,cAAc,OAAsB;AAC5C,UAAI,MAAM,QAAQ,YAAY,kBAAkB,QAAQ;AACvD,wBAAgB;AAChB,cAAM,gBAAgB;AAAA,MACvB;AAAA,IACD;AAEA,aAAS,iBAAiB,WAAW,eAAe,EAAE,SAAS,KAAK,CAAC;AACrE,WAAO,MAAM;AACZ,eAAS,oBAAoB,WAAW,eAAe,EAAE,SAAS,KAAK,CAAC;AAAA,IACzE;AAAA,EACD,GAAG,CAAC,gBAAgB,MAAM,CAAC;AAG3B,8BAAU,MAAM;AACf,aAAS,oBAAoB;AAC5B,qBAAe,YAAY,EAAE,MAAM,eAAe,CAAC;AAAA,IACpD;AAEA,aAAS,kBAAkB;AAC1B,qBAAe,YAAY,EAAE,MAAM,aAAa,CAAC;AAAA,IAClD;AAEA,aAAS,iBAAiB,eAAe,mBAAmB,EAAE,SAAS,KAAK,CAAC;AAC7E,aAAS,iBAAiB,aAAa,iBAAiB,EAAE,SAAS,KAAK,CAAC;AACzE,aAAS,iBAAiB,iBAAiB,iBAAiB,EAAE,SAAS,KAAK,CAAC;AAC7E,WAAO,MAAM;AACZ,eAAS,oBAAoB,eAAe,mBAAmB,EAAE,SAAS,KAAK,CAAC;AAChF,eAAS,oBAAoB,aAAa,iBAAiB,EAAE,SAAS,KAAK,CAAC;AAC5E,eAAS,oBAAoB,iBAAiB,iBAAiB,EAAE,SAAS,KAAK,CAAC;AAEhF,qBAAe,YAAY,EAAE,MAAM,aAAa,CAAC;AAAA,IAClD;AAAA,EACD,GAAG,CAAC,CAAC;AAGL,8BAAU,MAAM;AACf,QAAI,QAA8C;AAClD,QAAI,kBAAkB,WAAW,SAAS;AAEzC,YAAM,aAAa,eAAe,cAAc,sBAAsB;AACtE,YAAM,UAAU,WAAW;AAE3B,cAAQ,MAAM,WAAW;AACzB,cAAQ,MAAM,OAAO,GAAG,WAAW,IAAI;
|
|
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 = '0px'\n\t\t\ttrigger.style.top = '0px'\n\t\t\tconst cbOffset = trigger.getBoundingClientRect()\n\n\t\t\ttrigger.style.left = `${activeRect.left - cbOffset.left}px`\n\t\t\ttrigger.style.top = `${activeRect.top - cbOffset.top}px`\n\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 childElement = child as React.ReactElement<{\n\t\t\tonMouseEnter?(event: React.MouseEvent<HTMLElement>): void\n\t\t\tonMouseLeave?(event: React.MouseEvent<HTMLElement>): void\n\t\t\tonFocus?(event: React.FocusEvent<HTMLElement>): void\n\t\t\tonBlur?(event: React.FocusEvent<HTMLElement>): void\n\t\t}>\n\n\t\tconst handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchildElement.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\tchildElement.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\tchildElement.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\tchildElement.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(childElement, {\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": ["_Tooltip", "React"]
|
|
7
7
|
}
|
|
@@ -22,10 +22,10 @@ __export(version_exports, {
|
|
|
22
22
|
version: () => version
|
|
23
23
|
});
|
|
24
24
|
module.exports = __toCommonJS(version_exports);
|
|
25
|
-
const version = "4.4.
|
|
25
|
+
const version = "4.4.1";
|
|
26
26
|
const publishDates = {
|
|
27
27
|
major: "2025-09-18T14:39:22.803Z",
|
|
28
28
|
minor: "2026-02-18T12:03:50.380Z",
|
|
29
|
-
patch: "2026-
|
|
29
|
+
patch: "2026-03-09T10:00:04.152Z"
|
|
30
30
|
};
|
|
31
31
|
//# sourceMappingURL=version.js.map
|
|
@@ -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.4.
|
|
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.4.1'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2026-02-18T12:03:50.380Z',\n\tpatch: '2026-03-09T10:00:04.152Z',\n}\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-esm/index.mjs
CHANGED
|
@@ -174,8 +174,11 @@ function TooltipSingleton() {
|
|
|
174
174
|
const activeRect = currentTooltip.targetElement.getBoundingClientRect();
|
|
175
175
|
const trigger = triggerRef.current;
|
|
176
176
|
trigger.style.position = "fixed";
|
|
177
|
-
trigger.style.left =
|
|
178
|
-
trigger.style.top =
|
|
177
|
+
trigger.style.left = "0px";
|
|
178
|
+
trigger.style.top = "0px";
|
|
179
|
+
const cbOffset = trigger.getBoundingClientRect();
|
|
180
|
+
trigger.style.left = `${activeRect.left - cbOffset.left}px`;
|
|
181
|
+
trigger.style.top = `${activeRect.top - cbOffset.top}px`;
|
|
179
182
|
trigger.style.width = `${activeRect.width}px`;
|
|
180
183
|
trigger.style.height = `${activeRect.height}px`;
|
|
181
184
|
trigger.style.pointerEvents = "none";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/ui/components/primitives/TldrawUiTooltip.tsx"],
|
|
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 childElement = child as React.ReactElement<{\n\t\t\tonMouseEnter?(event: React.MouseEvent<HTMLElement>): void\n\t\t\tonMouseLeave?(event: React.MouseEvent<HTMLElement>): void\n\t\t\tonFocus?(event: React.FocusEvent<HTMLElement>): void\n\t\t\tonBlur?(event: React.FocusEvent<HTMLElement>): void\n\t\t}>\n\n\t\tconst handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchildElement.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\tchildElement.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\tchildElement.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\tchildElement.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(childElement, {\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": "AAmMG,
|
|
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 = '0px'\n\t\t\ttrigger.style.top = '0px'\n\t\t\tconst cbOffset = trigger.getBoundingClientRect()\n\n\t\t\ttrigger.style.left = `${activeRect.left - cbOffset.left}px`\n\t\t\ttrigger.style.top = `${activeRect.top - cbOffset.top}px`\n\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 childElement = child as React.ReactElement<{\n\t\t\tonMouseEnter?(event: React.MouseEvent<HTMLElement>): void\n\t\t\tonMouseLeave?(event: React.MouseEvent<HTMLElement>): void\n\t\t\tonFocus?(event: React.FocusEvent<HTMLElement>): void\n\t\t\tonBlur?(event: React.FocusEvent<HTMLElement>): void\n\t\t}>\n\n\t\tconst handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchildElement.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\tchildElement.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\tchildElement.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\tchildElement.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(childElement, {\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
|
}
|
|
@@ -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.4.
|
|
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.4.1'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2026-02-18T12:03:50.380Z',\n\tpatch: '2026-03-09T10:00:04.152Z',\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
|
}
|
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.4.
|
|
4
|
+
"version": "4.4.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -61,8 +61,8 @@
|
|
|
61
61
|
"@tiptap/pm": "^3.12.1",
|
|
62
62
|
"@tiptap/react": "^3.12.1",
|
|
63
63
|
"@tiptap/starter-kit": "^3.12.1",
|
|
64
|
-
"@tldraw/editor": "4.4.
|
|
65
|
-
"@tldraw/store": "4.4.
|
|
64
|
+
"@tldraw/editor": "4.4.1",
|
|
65
|
+
"@tldraw/store": "4.4.1",
|
|
66
66
|
"classnames": "^2.5.1",
|
|
67
67
|
"hotkeys-js": "^3.13.9",
|
|
68
68
|
"idb": "^7.1.1",
|
|
@@ -273,8 +273,13 @@ function TooltipSingleton() {
|
|
|
273
273
|
const trigger = triggerRef.current
|
|
274
274
|
|
|
275
275
|
trigger.style.position = 'fixed'
|
|
276
|
-
trigger.style.left =
|
|
277
|
-
trigger.style.top =
|
|
276
|
+
trigger.style.left = '0px'
|
|
277
|
+
trigger.style.top = '0px'
|
|
278
|
+
const cbOffset = trigger.getBoundingClientRect()
|
|
279
|
+
|
|
280
|
+
trigger.style.left = `${activeRect.left - cbOffset.left}px`
|
|
281
|
+
trigger.style.top = `${activeRect.top - cbOffset.top}px`
|
|
282
|
+
|
|
278
283
|
trigger.style.width = `${activeRect.width}px`
|
|
279
284
|
trigger.style.height = `${activeRect.height}px`
|
|
280
285
|
trigger.style.pointerEvents = 'none'
|
package/src/lib/ui/version.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// This file is automatically generated by internal/scripts/refresh-assets.ts.
|
|
2
2
|
// Do not edit manually. Or do, I'm a comment, not a cop.
|
|
3
3
|
|
|
4
|
-
export const version = '4.4.
|
|
4
|
+
export const version = '4.4.1'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2025-09-18T14:39:22.803Z',
|
|
7
7
|
minor: '2026-02-18T12:03:50.380Z',
|
|
8
|
-
patch: '2026-
|
|
8
|
+
patch: '2026-03-09T10:00:04.152Z',
|
|
9
9
|
}
|