tldraw 3.16.0-canary.2b8b5023f0a5 → 3.16.0-canary.555a872cc1c7
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 +43 -4
- package/dist-cjs/index.js +4 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/canvas/TldrawScribble.js +1 -1
- package/dist-cjs/lib/canvas/TldrawScribble.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/elbow/ElbowArrowDebug.js +3 -3
- package/dist-cjs/lib/shapes/arrow/elbow/ElbowArrowDebug.js.map +1 -1
- package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js +1 -1
- package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +1 -1
- package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +4 -4
- package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/frame/components/FrameHeading.js +1 -1
- package/dist-cjs/lib/shapes/frame/components/FrameHeading.js.map +2 -2
- package/dist-cjs/lib/shapes/image/ImageShapeUtil.js +3 -3
- package/dist-cjs/lib/shapes/image/ImageShapeUtil.js.map +1 -1
- package/dist-cjs/lib/shapes/video/VideoShapeUtil.js +3 -3
- package/dist-cjs/lib/shapes/video/VideoShapeUtil.js.map +1 -1
- package/dist-cjs/lib/ui/TldrawUi.js +14 -0
- package/dist-cjs/lib/ui/TldrawUi.js.map +3 -3
- package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js +10 -2
- package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js.map +2 -2
- package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js +4 -4
- package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js.map +2 -2
- package/dist-cjs/lib/ui/components/MobileStylePanel.js +4 -2
- package/dist-cjs/lib/ui/components/MobileStylePanel.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js +1 -1
- package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js +66 -22
- package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js.map +3 -3
- package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js +188 -78
- package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js.map +3 -3
- package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js +15 -3
- package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +127 -158
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/layout.js +30 -5
- package/dist-cjs/lib/ui/components/primitives/layout.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuContext.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js +25 -12
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +0 -18
- 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-esm/index.d.mts +43 -4
- package/dist-esm/index.mjs +8 -2
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/canvas/TldrawScribble.mjs +1 -1
- package/dist-esm/lib/canvas/TldrawScribble.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/elbow/ElbowArrowDebug.mjs +3 -3
- package/dist-esm/lib/shapes/arrow/elbow/ElbowArrowDebug.mjs.map +1 -1
- package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +1 -1
- package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +1 -1
- package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +4 -4
- package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/frame/components/FrameHeading.mjs +1 -1
- package/dist-esm/lib/shapes/frame/components/FrameHeading.mjs.map +2 -2
- package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs +3 -3
- package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs.map +1 -1
- package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs +3 -3
- package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs.map +1 -1
- package/dist-esm/lib/ui/TldrawUi.mjs +16 -2
- package/dist-esm/lib/ui/TldrawUi.mjs.map +3 -3
- package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs +10 -2
- package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs +4 -4
- package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs.map +2 -2
- package/dist-esm/lib/ui/components/MobileStylePanel.mjs +4 -2
- package/dist-esm/lib/ui/components/MobileStylePanel.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs +1 -1
- package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/DefaultToolbar.mjs +56 -22
- package/dist-esm/lib/ui/components/Toolbar/DefaultToolbar.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs +192 -80
- package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs.map +3 -3
- package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs +16 -4
- package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +136 -160
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/layout.mjs +31 -6
- package/dist-esm/lib/ui/components/primitives/layout.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuContext.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuGroup.mjs +25 -12
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuGroup.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +0 -18
- 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/package.json +3 -3
- package/src/index.ts +5 -0
- package/src/lib/canvas/TldrawScribble.tsx +1 -1
- package/src/lib/shapes/arrow/elbow/ElbowArrowDebug.tsx +3 -3
- package/src/lib/shapes/embed/EmbedShapeUtil.tsx +1 -1
- package/src/lib/shapes/frame/FrameShapeUtil.tsx +12 -4
- package/src/lib/shapes/frame/components/FrameHeading.tsx +1 -1
- package/src/lib/shapes/image/ImageShapeUtil.tsx +3 -3
- package/src/lib/shapes/video/VideoShapeUtil.tsx +3 -3
- package/src/lib/ui/TldrawUi.tsx +17 -2
- package/src/lib/ui/components/ActionsMenu/DefaultActionsMenu.tsx +13 -2
- package/src/lib/ui/components/Minimap/MinimapManager.ts +4 -4
- package/src/lib/ui/components/MobileStylePanel.tsx +4 -3
- package/src/lib/ui/components/Toolbar/DefaultImageToolbarContent.tsx +1 -1
- package/src/lib/ui/components/Toolbar/DefaultToolbar.tsx +55 -24
- package/src/lib/ui/components/Toolbar/OverflowingToolbar.tsx +208 -56
- package/src/lib/ui/components/primitives/TldrawUiToolbar.tsx +22 -5
- package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +145 -176
- package/src/lib/ui/components/primitives/layout.tsx +79 -5
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuContext.tsx +0 -1
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuGroup.tsx +29 -16
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +0 -16
- package/src/lib/ui/version.ts +3 -3
- package/src/lib/ui.css +342 -243
- package/tldraw.css +635 -533
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
import { assert, Editor, uniqueId, useMaybeEditor,
|
|
1
|
+
import { assert, Atom, atom, Editor, uniqueId, useMaybeEditor, useValue } from '@tldraw/editor'
|
|
2
2
|
import { Tooltip as _Tooltip } from 'radix-ui'
|
|
3
|
-
import React, {
|
|
4
|
-
|
|
3
|
+
import React, {
|
|
4
|
+
createContext,
|
|
5
|
+
forwardRef,
|
|
6
|
+
ReactNode,
|
|
7
|
+
useContext,
|
|
8
|
+
useEffect,
|
|
9
|
+
useRef,
|
|
10
|
+
useState,
|
|
11
|
+
} from 'react'
|
|
12
|
+
import { useTldrawUiOrientation } from './layout'
|
|
5
13
|
|
|
6
14
|
const DEFAULT_TOOLTIP_DELAY_MS = 700
|
|
7
15
|
|
|
@@ -17,14 +25,15 @@ export interface TldrawUiTooltipProps {
|
|
|
17
25
|
// Singleton tooltip manager
|
|
18
26
|
class TooltipManager {
|
|
19
27
|
private static instance: TooltipManager | null = null
|
|
20
|
-
private
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
private currentTooltip = atom<{
|
|
29
|
+
id: string
|
|
30
|
+
content: ReactNode
|
|
31
|
+
side: 'top' | 'right' | 'bottom' | 'left'
|
|
32
|
+
sideOffset: number
|
|
33
|
+
targetElement: HTMLElement
|
|
34
|
+
} | null>('current tooltip', null)
|
|
24
35
|
private destroyTimeoutId: number | null = null
|
|
25
36
|
private subscribers: Set<() => void> = new Set()
|
|
26
|
-
private activeElement: HTMLElement | null = null
|
|
27
|
-
private editor: Editor | null = null
|
|
28
37
|
|
|
29
38
|
static getInstance(): TooltipManager {
|
|
30
39
|
if (!TooltipManager.instance) {
|
|
@@ -33,23 +42,10 @@ class TooltipManager {
|
|
|
33
42
|
return TooltipManager.instance
|
|
34
43
|
}
|
|
35
44
|
|
|
36
|
-
setEditor(editor: Editor | null) {
|
|
37
|
-
this.editor = editor
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
subscribe(callback: () => void): () => void {
|
|
41
|
-
this.subscribers.add(callback)
|
|
42
|
-
return () => this.subscribers.delete(callback)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
private notify() {
|
|
46
|
-
this.subscribers.forEach((callback) => callback())
|
|
47
|
-
}
|
|
48
|
-
|
|
49
45
|
showTooltip(
|
|
50
46
|
tooltipId: string,
|
|
51
47
|
content: string | React.ReactNode,
|
|
52
|
-
|
|
48
|
+
targetElement: HTMLElement,
|
|
53
49
|
side: 'top' | 'right' | 'bottom' | 'left' = 'bottom',
|
|
54
50
|
sideOffset: number = 5
|
|
55
51
|
) {
|
|
@@ -60,51 +56,53 @@ class TooltipManager {
|
|
|
60
56
|
}
|
|
61
57
|
|
|
62
58
|
// Update current tooltip
|
|
63
|
-
this.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
59
|
+
this.currentTooltip.set({
|
|
60
|
+
id: tooltipId,
|
|
61
|
+
content,
|
|
62
|
+
side,
|
|
63
|
+
sideOffset,
|
|
64
|
+
targetElement,
|
|
65
|
+
})
|
|
70
66
|
}
|
|
71
67
|
|
|
72
|
-
hideTooltip(tooltipId: string, instant: boolean = false) {
|
|
68
|
+
hideTooltip(editor: Editor | null, tooltipId: string, instant: boolean = false) {
|
|
73
69
|
const hide = () => {
|
|
74
70
|
// Only hide if this is the current tooltip
|
|
75
|
-
if (this.
|
|
76
|
-
this.
|
|
77
|
-
this.currentContent = ''
|
|
78
|
-
this.activeElement = null
|
|
71
|
+
if (this.currentTooltip.get()?.id === tooltipId) {
|
|
72
|
+
this.currentTooltip.set(null)
|
|
79
73
|
this.destroyTimeoutId = null
|
|
80
|
-
this.notify()
|
|
81
74
|
}
|
|
82
75
|
}
|
|
83
76
|
|
|
84
|
-
if (instant) {
|
|
85
|
-
hide()
|
|
86
|
-
} else if (this.editor) {
|
|
77
|
+
if (editor && !instant) {
|
|
87
78
|
// Start destroy timeout (1 second)
|
|
88
|
-
this.destroyTimeoutId =
|
|
79
|
+
this.destroyTimeoutId = editor.timers.setTimeout(hide, 300)
|
|
80
|
+
} else {
|
|
81
|
+
hide()
|
|
89
82
|
}
|
|
90
83
|
}
|
|
91
84
|
|
|
92
85
|
hideAllTooltips() {
|
|
93
|
-
this.
|
|
94
|
-
this.currentContent = ''
|
|
95
|
-
this.activeElement = null
|
|
86
|
+
this.currentTooltip.set(null)
|
|
96
87
|
this.destroyTimeoutId = null
|
|
97
|
-
this.notify()
|
|
98
88
|
}
|
|
99
89
|
|
|
100
90
|
getCurrentTooltipData() {
|
|
101
|
-
return
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
91
|
+
if (!this.supportsHover()) return null
|
|
92
|
+
return this.currentTooltip.get()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private supportsHoverAtom: Atom<boolean> | null = null
|
|
96
|
+
supportsHover() {
|
|
97
|
+
if (!this.supportsHoverAtom) {
|
|
98
|
+
const mediaQuery = window.matchMedia('(hover: hover)')
|
|
99
|
+
const supportsHover = atom('has hover', mediaQuery.matches)
|
|
100
|
+
this.supportsHoverAtom = supportsHover
|
|
101
|
+
mediaQuery.addEventListener('change', (e) => {
|
|
102
|
+
supportsHover.set(e.matches)
|
|
103
|
+
})
|
|
107
104
|
}
|
|
105
|
+
return this.supportsHoverAtom.get()
|
|
108
106
|
}
|
|
109
107
|
}
|
|
110
108
|
|
|
@@ -133,65 +131,30 @@ export function TldrawUiTooltipProvider({ children }: TldrawUiTooltipProviderPro
|
|
|
133
131
|
// The singleton tooltip component that renders once
|
|
134
132
|
function TooltipSingleton() {
|
|
135
133
|
const editor = useMaybeEditor()
|
|
136
|
-
const [, forceUpdate] = useState({})
|
|
137
134
|
const [isOpen, setIsOpen] = useState(false)
|
|
138
135
|
const triggerRef = useRef<HTMLDivElement>(null)
|
|
139
|
-
const previousPositionRef = useRef<{ x: number; y: number } | null>(null)
|
|
140
|
-
const prefersReducedMotion = usePrefersReducedMotion()
|
|
141
|
-
const [shouldAnimate, setShouldAnimate] = useState(false)
|
|
142
136
|
const isFirstShowRef = useRef(true)
|
|
143
137
|
const showTimeoutRef = useRef<number | null>(null)
|
|
144
138
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
tooltipManager.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
// Subscribe to tooltip manager updates
|
|
151
|
-
useEffect(() => {
|
|
152
|
-
const unsubscribe = tooltipManager.subscribe(() => {
|
|
153
|
-
forceUpdate({})
|
|
154
|
-
})
|
|
155
|
-
return unsubscribe
|
|
156
|
-
}, [])
|
|
157
|
-
|
|
158
|
-
const tooltipData = tooltipManager.getCurrentTooltipData()
|
|
139
|
+
const currentTooltip = useValue(
|
|
140
|
+
'current tooltip',
|
|
141
|
+
() => tooltipManager.getCurrentTooltipData(),
|
|
142
|
+
[]
|
|
143
|
+
)
|
|
159
144
|
|
|
160
145
|
// Update open state and trigger position
|
|
161
146
|
useEffect(() => {
|
|
162
|
-
const shouldBeOpen = Boolean(tooltipData.id && tooltipData.element)
|
|
163
|
-
|
|
164
147
|
// Clear any existing show timeout
|
|
165
148
|
if (showTimeoutRef.current) {
|
|
166
149
|
clearTimeout(showTimeoutRef.current)
|
|
167
150
|
showTimeoutRef.current = null
|
|
168
151
|
}
|
|
169
152
|
|
|
170
|
-
if (
|
|
153
|
+
if (currentTooltip && triggerRef.current) {
|
|
171
154
|
// Position the invisible trigger element over the active element
|
|
172
|
-
const activeRect =
|
|
155
|
+
const activeRect = currentTooltip.targetElement.getBoundingClientRect()
|
|
173
156
|
const trigger = triggerRef.current
|
|
174
157
|
|
|
175
|
-
const newPosition = {
|
|
176
|
-
x: activeRect.left + activeRect.width / 2,
|
|
177
|
-
y: activeRect.top + activeRect.height / 2,
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Determine if we should animate
|
|
181
|
-
let shouldAnimateCheck = false
|
|
182
|
-
if (previousPositionRef.current) {
|
|
183
|
-
const isNearPrevious = Vec.DistMin(previousPositionRef.current, newPosition, 200)
|
|
184
|
-
// Only animate if the distance is less than 200px (nearby tooltips)
|
|
185
|
-
shouldAnimateCheck =
|
|
186
|
-
!prefersReducedMotion &&
|
|
187
|
-
isNearPrevious &&
|
|
188
|
-
Math.abs(newPosition.y - previousPositionRef.current.y) < 50
|
|
189
|
-
}
|
|
190
|
-
// Don't animate on initial show (previousPositionRef.current is null)
|
|
191
|
-
|
|
192
|
-
setShouldAnimate(isFirstShowRef.current ? false : shouldAnimateCheck)
|
|
193
|
-
previousPositionRef.current = newPosition
|
|
194
|
-
|
|
195
158
|
trigger.style.position = 'fixed'
|
|
196
159
|
trigger.style.left = `${activeRect.left}px`
|
|
197
160
|
trigger.style.top = `${activeRect.top}px`
|
|
@@ -210,18 +173,15 @@ function TooltipSingleton() {
|
|
|
210
173
|
// Subsequent tooltips show immediately
|
|
211
174
|
setIsOpen(true)
|
|
212
175
|
}
|
|
213
|
-
} else
|
|
176
|
+
} else {
|
|
214
177
|
// Hide tooltip immediately
|
|
215
178
|
setIsOpen(false)
|
|
216
|
-
// Reset position tracking when tooltip closes
|
|
217
|
-
previousPositionRef.current = null
|
|
218
|
-
setShouldAnimate(false)
|
|
219
179
|
// Reset first show state after tooltip is hidden
|
|
220
180
|
isFirstShowRef.current = true
|
|
221
181
|
}
|
|
222
|
-
}, [
|
|
182
|
+
}, [editor, currentTooltip])
|
|
223
183
|
|
|
224
|
-
if (!
|
|
184
|
+
if (!currentTooltip) {
|
|
225
185
|
return null
|
|
226
186
|
}
|
|
227
187
|
|
|
@@ -232,14 +192,13 @@ function TooltipSingleton() {
|
|
|
232
192
|
</_Tooltip.Trigger>
|
|
233
193
|
<_Tooltip.Content
|
|
234
194
|
className="tlui-tooltip"
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
sideOffset={tooltipData.sideOffset}
|
|
195
|
+
side={currentTooltip.side}
|
|
196
|
+
sideOffset={currentTooltip.sideOffset}
|
|
238
197
|
avoidCollisions
|
|
239
198
|
collisionPadding={8}
|
|
240
199
|
dir="ltr"
|
|
241
200
|
>
|
|
242
|
-
{
|
|
201
|
+
{currentTooltip.content}
|
|
243
202
|
<_Tooltip.Arrow className="tlui-tooltip__arrow" />
|
|
244
203
|
</_Tooltip.Content>
|
|
245
204
|
</_Tooltip.Root>
|
|
@@ -247,86 +206,96 @@ function TooltipSingleton() {
|
|
|
247
206
|
}
|
|
248
207
|
|
|
249
208
|
/** @public @react */
|
|
250
|
-
export
|
|
251
|
-
children,
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
209
|
+
export const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProps>(
|
|
210
|
+
({ children, content, side, sideOffset = 5, disabled = false }, ref) => {
|
|
211
|
+
const editor = useMaybeEditor()
|
|
212
|
+
const tooltipId = useRef<string>(uniqueId())
|
|
213
|
+
const hasProvider = useContext(TooltipSingletonContext)
|
|
214
|
+
|
|
215
|
+
const orientationCtx = useTldrawUiOrientation()
|
|
216
|
+
const sideToUse = side ?? orientationCtx.tooltipSide
|
|
217
|
+
|
|
218
|
+
useEffect(() => {
|
|
219
|
+
const currentTooltipId = tooltipId.current
|
|
220
|
+
return () => {
|
|
221
|
+
if (hasProvider) {
|
|
222
|
+
tooltipManager.hideTooltip(editor, currentTooltipId, true)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}, [editor, hasProvider])
|
|
260
226
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
227
|
+
// Don't show tooltip if disabled, no content, or UI labels are disabled
|
|
228
|
+
if (disabled || !content) {
|
|
229
|
+
return <>{children}</>
|
|
230
|
+
}
|
|
265
231
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
>
|
|
273
|
-
<_Tooltip.Trigger asChild>{children}</_Tooltip.Trigger>
|
|
274
|
-
<_Tooltip.Content
|
|
275
|
-
className="tlui-tooltip"
|
|
276
|
-
side={side}
|
|
277
|
-
sideOffset={sideOffset}
|
|
278
|
-
avoidCollisions
|
|
279
|
-
collisionPadding={8}
|
|
280
|
-
dir="ltr"
|
|
232
|
+
// Fallback to old behavior if no provider
|
|
233
|
+
if (!hasProvider) {
|
|
234
|
+
return (
|
|
235
|
+
<_Tooltip.Root
|
|
236
|
+
delayDuration={editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS}
|
|
237
|
+
disableHoverableContent
|
|
281
238
|
>
|
|
282
|
-
{
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
239
|
+
<_Tooltip.Trigger asChild ref={ref}>
|
|
240
|
+
{children}
|
|
241
|
+
</_Tooltip.Trigger>
|
|
242
|
+
<_Tooltip.Content
|
|
243
|
+
className="tlui-tooltip"
|
|
244
|
+
side={sideToUse}
|
|
245
|
+
sideOffset={sideOffset}
|
|
246
|
+
avoidCollisions
|
|
247
|
+
collisionPadding={8}
|
|
248
|
+
dir="ltr"
|
|
249
|
+
>
|
|
250
|
+
{content}
|
|
251
|
+
<_Tooltip.Arrow className="tlui-tooltip__arrow" />
|
|
252
|
+
</_Tooltip.Content>
|
|
253
|
+
</_Tooltip.Root>
|
|
254
|
+
)
|
|
255
|
+
}
|
|
291
256
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
257
|
+
const child = React.Children.only(children)
|
|
258
|
+
assert(React.isValidElement(child), 'TldrawUiTooltip children must be a single element')
|
|
259
|
+
|
|
260
|
+
const handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {
|
|
261
|
+
child.props.onMouseEnter?.(event)
|
|
262
|
+
tooltipManager.showTooltip(
|
|
263
|
+
tooltipId.current,
|
|
264
|
+
content,
|
|
265
|
+
event.currentTarget as HTMLElement,
|
|
266
|
+
sideToUse,
|
|
267
|
+
sideOffset
|
|
268
|
+
)
|
|
269
|
+
}
|
|
302
270
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
271
|
+
const handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {
|
|
272
|
+
child.props.onMouseLeave?.(event)
|
|
273
|
+
tooltipManager.hideTooltip(editor, tooltipId.current)
|
|
274
|
+
}
|
|
307
275
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
276
|
+
const handleFocus = (event: React.FocusEvent<HTMLElement>) => {
|
|
277
|
+
child.props.onFocus?.(event)
|
|
278
|
+
tooltipManager.showTooltip(
|
|
279
|
+
tooltipId.current,
|
|
280
|
+
content,
|
|
281
|
+
event.currentTarget as HTMLElement,
|
|
282
|
+
sideToUse,
|
|
283
|
+
sideOffset
|
|
284
|
+
)
|
|
285
|
+
}
|
|
318
286
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
287
|
+
const handleBlur = (event: React.FocusEvent<HTMLElement>) => {
|
|
288
|
+
child.props.onBlur?.(event)
|
|
289
|
+
tooltipManager.hideTooltip(editor, tooltipId.current)
|
|
290
|
+
}
|
|
323
291
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
292
|
+
const childrenWithHandlers = React.cloneElement(children as React.ReactElement, {
|
|
293
|
+
onMouseEnter: handleMouseEnter,
|
|
294
|
+
onMouseLeave: handleMouseLeave,
|
|
295
|
+
onFocus: handleFocus,
|
|
296
|
+
onBlur: handleBlur,
|
|
297
|
+
})
|
|
330
298
|
|
|
331
|
-
|
|
332
|
-
}
|
|
299
|
+
return childrenWithHandlers
|
|
300
|
+
}
|
|
301
|
+
)
|
|
@@ -1,10 +1,60 @@
|
|
|
1
1
|
import classNames from 'classnames'
|
|
2
2
|
import { Slot } from 'radix-ui'
|
|
3
|
-
import { HTMLAttributes, ReactNode, forwardRef } from 'react'
|
|
3
|
+
import { HTMLAttributes, ReactNode, createContext, forwardRef, useContext } from 'react'
|
|
4
|
+
|
|
5
|
+
/** @public */
|
|
6
|
+
export interface TldrawUiOrientationContext {
|
|
7
|
+
orientation: 'horizontal' | 'vertical'
|
|
8
|
+
tooltipSide: 'top' | 'right' | 'bottom' | 'left'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const TldrawUiOrientationContext = createContext<TldrawUiOrientationContext>({
|
|
12
|
+
orientation: 'horizontal',
|
|
13
|
+
tooltipSide: 'bottom',
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
/** @public */
|
|
17
|
+
export interface TldrawUiOrientationProviderProps {
|
|
18
|
+
children: ReactNode
|
|
19
|
+
orientation: 'horizontal' | 'vertical'
|
|
20
|
+
tooltipSide?: 'top' | 'right' | 'bottom' | 'left'
|
|
21
|
+
}
|
|
22
|
+
/** @public @react */
|
|
23
|
+
export function TldrawUiOrientationProvider({
|
|
24
|
+
children,
|
|
25
|
+
orientation,
|
|
26
|
+
tooltipSide,
|
|
27
|
+
}: TldrawUiOrientationProviderProps) {
|
|
28
|
+
const prevContext = useTldrawUiOrientation()
|
|
29
|
+
// generally, we want tooltip side to cascade down through the layout - apart from when the
|
|
30
|
+
// orientation changes. If the tooltip side is "bottom", and then I include some vertical layout
|
|
31
|
+
// elements, keeping the tooltip side as bottom will cause the tooltip to overlap elements
|
|
32
|
+
// stacked on top of each other. In the absence of a tooltip side, we pick a default side based
|
|
33
|
+
// on the orientation whenever the orientation changes.
|
|
34
|
+
const tooltipSideToUse =
|
|
35
|
+
tooltipSide ??
|
|
36
|
+
(orientation === prevContext.orientation
|
|
37
|
+
? prevContext.tooltipSide
|
|
38
|
+
: orientation === 'horizontal'
|
|
39
|
+
? 'bottom'
|
|
40
|
+
: 'right')
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<TldrawUiOrientationContext.Provider value={{ orientation, tooltipSide: tooltipSideToUse }}>
|
|
44
|
+
{children}
|
|
45
|
+
</TldrawUiOrientationContext.Provider>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** @public */
|
|
50
|
+
export function useTldrawUiOrientation() {
|
|
51
|
+
return useContext(TldrawUiOrientationContext)
|
|
52
|
+
}
|
|
4
53
|
|
|
5
54
|
/** @public */
|
|
6
55
|
export interface TLUiLayoutProps extends HTMLAttributes<HTMLDivElement> {
|
|
7
56
|
children: ReactNode
|
|
57
|
+
tooltipSide?: 'top' | 'right' | 'bottom' | 'left'
|
|
8
58
|
asChild?: boolean
|
|
9
59
|
}
|
|
10
60
|
|
|
@@ -14,9 +64,29 @@ export interface TLUiLayoutProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
14
64
|
* @public @react
|
|
15
65
|
*/
|
|
16
66
|
export const TldrawUiRow = forwardRef<HTMLDivElement, TLUiLayoutProps>(
|
|
17
|
-
({ asChild, className, ...props }, ref) => {
|
|
67
|
+
({ asChild, className, tooltipSide, ...props }, ref) => {
|
|
68
|
+
const Component = asChild ? Slot.Root : 'div'
|
|
69
|
+
return (
|
|
70
|
+
<TldrawUiOrientationProvider orientation="horizontal" tooltipSide={tooltipSide}>
|
|
71
|
+
<Component ref={ref} className={classNames('tlui-row', className)} {...props} />
|
|
72
|
+
</TldrawUiOrientationProvider>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A column, usually of UI controls like buttons, select dropdown, checkboxes, etc.
|
|
79
|
+
*
|
|
80
|
+
* @public @react
|
|
81
|
+
*/
|
|
82
|
+
export const TldrawUiColumn = forwardRef<HTMLDivElement, TLUiLayoutProps>(
|
|
83
|
+
({ asChild, className, tooltipSide, ...props }, ref) => {
|
|
18
84
|
const Component = asChild ? Slot.Root : 'div'
|
|
19
|
-
return
|
|
85
|
+
return (
|
|
86
|
+
<TldrawUiOrientationProvider orientation="vertical" tooltipSide={tooltipSide}>
|
|
87
|
+
<Component ref={ref} className={classNames('tlui-column', className)} {...props} />
|
|
88
|
+
</TldrawUiOrientationProvider>
|
|
89
|
+
)
|
|
20
90
|
}
|
|
21
91
|
)
|
|
22
92
|
|
|
@@ -26,8 +96,12 @@ export const TldrawUiRow = forwardRef<HTMLDivElement, TLUiLayoutProps>(
|
|
|
26
96
|
*
|
|
27
97
|
* @public @react */
|
|
28
98
|
export const TldrawUiGrid = forwardRef<HTMLDivElement, TLUiLayoutProps>(
|
|
29
|
-
({ asChild, className, ...props }, ref) => {
|
|
99
|
+
({ asChild, className, tooltipSide, ...props }, ref) => {
|
|
30
100
|
const Component = asChild ? Slot.Root : 'div'
|
|
31
|
-
return
|
|
101
|
+
return (
|
|
102
|
+
<TldrawUiOrientationProvider orientation="horizontal" tooltipSide={tooltipSide}>
|
|
103
|
+
<Component ref={ref} className={classNames('tlui-grid', className)} {...props} />
|
|
104
|
+
</TldrawUiOrientationProvider>
|
|
105
|
+
)
|
|
32
106
|
}
|
|
33
107
|
)
|
|
@@ -3,6 +3,7 @@ import { ReactNode } from 'react'
|
|
|
3
3
|
import { unwrapLabel } from '../../../context/actions'
|
|
4
4
|
import { TLUiTranslationKey } from '../../../hooks/useTranslation/TLUiTranslationKey'
|
|
5
5
|
import { useTranslation } from '../../../hooks/useTranslation/useTranslation'
|
|
6
|
+
import { TldrawUiColumn, TldrawUiGrid, TldrawUiRow, useTldrawUiOrientation } from '../layout'
|
|
6
7
|
import { TldrawUiDropdownMenuGroup } from '../TldrawUiDropdownMenu'
|
|
7
8
|
import { useTldrawUiMenuContext } from './TldrawUiMenuContext'
|
|
8
9
|
|
|
@@ -19,25 +20,19 @@ export interface TLUiMenuGroupProps<TranslationKey extends string = string> {
|
|
|
19
20
|
|
|
20
21
|
/** @public @react */
|
|
21
22
|
export function TldrawUiMenuGroup({ id, label, className, children }: TLUiMenuGroupProps) {
|
|
22
|
-
const
|
|
23
|
+
const menu = useTldrawUiMenuContext()
|
|
24
|
+
const { orientation } = useTldrawUiOrientation()
|
|
23
25
|
const msg = useTranslation()
|
|
24
|
-
const labelToUse = unwrapLabel(label,
|
|
26
|
+
const labelToUse = unwrapLabel(label, menu.type)
|
|
25
27
|
const labelStr = labelToUse ? msg(labelToUse as TLUiTranslationKey) : undefined
|
|
26
28
|
|
|
27
|
-
switch (
|
|
28
|
-
case 'panel': {
|
|
29
|
-
return (
|
|
30
|
-
<div
|
|
31
|
-
className={classNames('tlui-menu__group', className)}
|
|
32
|
-
data-testid={`${sourceId}-group.${id}`}
|
|
33
|
-
>
|
|
34
|
-
{children}
|
|
35
|
-
</div>
|
|
36
|
-
)
|
|
37
|
-
}
|
|
29
|
+
switch (menu.type) {
|
|
38
30
|
case 'menu': {
|
|
39
31
|
return (
|
|
40
|
-
<TldrawUiDropdownMenuGroup
|
|
32
|
+
<TldrawUiDropdownMenuGroup
|
|
33
|
+
className={className}
|
|
34
|
+
data-testid={`${menu.sourceId}-group.${id}`}
|
|
35
|
+
>
|
|
41
36
|
{children}
|
|
42
37
|
</TldrawUiDropdownMenuGroup>
|
|
43
38
|
)
|
|
@@ -47,7 +42,7 @@ export function TldrawUiMenuGroup({ id, label, className, children }: TLUiMenuGr
|
|
|
47
42
|
<div
|
|
48
43
|
dir="ltr"
|
|
49
44
|
className={classNames('tlui-menu__group', className)}
|
|
50
|
-
data-testid={`${sourceId}-group.${id}`}
|
|
45
|
+
data-testid={`${menu.sourceId}-group.${id}`}
|
|
51
46
|
>
|
|
52
47
|
{children}
|
|
53
48
|
</div>
|
|
@@ -56,12 +51,30 @@ export function TldrawUiMenuGroup({ id, label, className, children }: TLUiMenuGr
|
|
|
56
51
|
case 'keyboard-shortcuts': {
|
|
57
52
|
// todo: if groups need a label, let's give em a label
|
|
58
53
|
return (
|
|
59
|
-
<div className="tlui-shortcuts-dialog__group" data-testid={`${sourceId}-group.${id}`}>
|
|
54
|
+
<div className="tlui-shortcuts-dialog__group" data-testid={`${menu.sourceId}-group.${id}`}>
|
|
60
55
|
<h2 className="tlui-shortcuts-dialog__group__title">{labelStr}</h2>
|
|
61
56
|
<div className="tlui-shortcuts-dialog__group__content">{children}</div>
|
|
62
57
|
</div>
|
|
63
58
|
)
|
|
64
59
|
}
|
|
60
|
+
case 'toolbar': {
|
|
61
|
+
const Layout = orientation === 'horizontal' ? TldrawUiRow : TldrawUiColumn
|
|
62
|
+
return (
|
|
63
|
+
<Layout className="tlui-main-toolbar__group" data-testid={`${menu.sourceId}-group.${id}`}>
|
|
64
|
+
{children}
|
|
65
|
+
</Layout>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
case 'toolbar-overflow': {
|
|
69
|
+
return (
|
|
70
|
+
<TldrawUiGrid
|
|
71
|
+
className="tlui-main-toolbar__group"
|
|
72
|
+
data-testid={`${menu.sourceId}-group.${id}`}
|
|
73
|
+
>
|
|
74
|
+
{children}
|
|
75
|
+
</TldrawUiGrid>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
65
78
|
default: {
|
|
66
79
|
return children
|
|
67
80
|
}
|
|
@@ -120,7 +120,6 @@ export function TldrawUiMenuItem<
|
|
|
120
120
|
type="menu"
|
|
121
121
|
data-testid={`${sourceId}.${id}`}
|
|
122
122
|
disabled={disabled}
|
|
123
|
-
title={titleStr}
|
|
124
123
|
onClick={(e) => {
|
|
125
124
|
if (noClose) {
|
|
126
125
|
preventDefault(e)
|
|
@@ -146,7 +145,6 @@ export function TldrawUiMenuItem<
|
|
|
146
145
|
return (
|
|
147
146
|
<_ContextMenu.Item
|
|
148
147
|
dir="ltr"
|
|
149
|
-
title={titleStr}
|
|
150
148
|
draggable={false}
|
|
151
149
|
className="tlui-button tlui-button__menu"
|
|
152
150
|
data-testid={`${sourceId}.${id}`}
|
|
@@ -168,20 +166,6 @@ export function TldrawUiMenuItem<
|
|
|
168
166
|
</_ContextMenu.Item>
|
|
169
167
|
)
|
|
170
168
|
}
|
|
171
|
-
case 'panel': {
|
|
172
|
-
return (
|
|
173
|
-
<TldrawUiButton
|
|
174
|
-
data-testid={`${sourceId}.${id}`}
|
|
175
|
-
type="menu"
|
|
176
|
-
title={titleStr}
|
|
177
|
-
disabled={disabled}
|
|
178
|
-
onClick={() => onSelect(sourceId)}
|
|
179
|
-
>
|
|
180
|
-
<TldrawUiButtonLabel>{labelStr}</TldrawUiButtonLabel>
|
|
181
|
-
{spinner ? <Spinner /> : icon && <TldrawUiButtonIcon icon={icon} />}
|
|
182
|
-
</TldrawUiButton>
|
|
183
|
-
)
|
|
184
|
-
}
|
|
185
169
|
case 'small-icons':
|
|
186
170
|
case 'icons': {
|
|
187
171
|
return (
|
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 = '3.16.0-canary.
|
|
4
|
+
export const version = '3.16.0-canary.555a872cc1c7'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2024-09-13T14:36:29.063Z',
|
|
7
|
-
minor: '2025-08-
|
|
8
|
-
patch: '2025-08-
|
|
7
|
+
minor: '2025-08-13T13:42:06.638Z',
|
|
8
|
+
patch: '2025-08-13T13:42:06.638Z',
|
|
9
9
|
}
|