tldraw 3.16.0-canary.5f82fb812214 → 3.16.0-canary.5f8d98bccb38

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.
Files changed (128) hide show
  1. package/dist-cjs/index.d.ts +131 -100
  2. package/dist-cjs/index.js +25 -14
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/shapes/arrow/arrowLabel.js +6 -0
  5. package/dist-cjs/lib/shapes/arrow/arrowLabel.js.map +3 -3
  6. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +1 -3
  7. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +2 -2
  8. package/dist-cjs/lib/shapes/shared/useEditablePlainText.js +0 -2
  9. package/dist-cjs/lib/shapes/shared/useEditablePlainText.js.map +2 -2
  10. package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js +0 -2
  11. package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js.map +2 -2
  12. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanel.js +9 -4
  13. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanel.js.map +2 -2
  14. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js +255 -316
  15. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js.map +2 -2
  16. package/dist-cjs/lib/ui/components/{primitives/TldrawUiButtonPicker.js → StylePanel/StylePanelButtonPicker.js} +52 -45
  17. package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js.map +7 -0
  18. package/dist-cjs/lib/ui/components/StylePanel/StylePanelContext.js +68 -0
  19. package/dist-cjs/lib/ui/components/StylePanel/StylePanelContext.js.map +7 -0
  20. package/dist-cjs/lib/ui/components/StylePanel/{DoubleDropdownPicker.js → StylePanelDoubleDropdownPicker.js} +23 -22
  21. package/dist-cjs/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.js.map +7 -0
  22. package/dist-cjs/lib/ui/components/StylePanel/{DropdownPicker.js → StylePanelDropdownPicker.js} +23 -20
  23. package/dist-cjs/lib/ui/components/StylePanel/StylePanelDropdownPicker.js.map +7 -0
  24. package/dist-cjs/lib/ui/components/StylePanel/StylePanelSubheading.js +28 -0
  25. package/dist-cjs/lib/ui/components/StylePanel/StylePanelSubheading.js.map +7 -0
  26. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js +2 -0
  27. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js.map +2 -2
  28. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +14 -5
  29. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
  30. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +5 -5
  31. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +1 -1
  32. package/dist-cjs/lib/ui/context/actions.js +7 -8
  33. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  34. package/dist-cjs/lib/ui/hooks/useExportAs.js +3 -2
  35. package/dist-cjs/lib/ui/hooks/useExportAs.js.map +2 -2
  36. package/dist-cjs/lib/ui/hooks/useTools.js +1 -1
  37. package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
  38. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +2 -2
  39. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +1 -1
  40. package/dist-cjs/lib/ui/version.js +3 -3
  41. package/dist-cjs/lib/ui/version.js.map +1 -1
  42. package/dist-cjs/lib/utils/export/copyAs.js +1 -2
  43. package/dist-cjs/lib/utils/export/copyAs.js.map +2 -2
  44. package/dist-cjs/lib/utils/export/export.js +0 -20
  45. package/dist-cjs/lib/utils/export/export.js.map +2 -2
  46. package/dist-cjs/lib/utils/export/exportAs.js +1 -2
  47. package/dist-cjs/lib/utils/export/exportAs.js.map +2 -2
  48. package/dist-esm/index.d.mts +131 -100
  49. package/dist-esm/index.mjs +51 -28
  50. package/dist-esm/index.mjs.map +2 -2
  51. package/dist-esm/lib/shapes/arrow/arrowLabel.mjs +6 -0
  52. package/dist-esm/lib/shapes/arrow/arrowLabel.mjs.map +3 -3
  53. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +1 -3
  54. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
  55. package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs +0 -2
  56. package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs.map +2 -2
  57. package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs +0 -2
  58. package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs.map +2 -2
  59. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanel.mjs +14 -5
  60. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanel.mjs.map +2 -2
  61. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs +257 -320
  62. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs.map +2 -2
  63. package/dist-esm/lib/ui/components/{primitives/TldrawUiButtonPicker.mjs → StylePanel/StylePanelButtonPicker.mjs} +54 -43
  64. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs.map +7 -0
  65. package/dist-esm/lib/ui/components/StylePanel/StylePanelContext.mjs +48 -0
  66. package/dist-esm/lib/ui/components/StylePanel/StylePanelContext.mjs.map +7 -0
  67. package/dist-esm/lib/ui/components/StylePanel/{DoubleDropdownPicker.mjs → StylePanelDoubleDropdownPicker.mjs} +20 -19
  68. package/dist-esm/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.mjs.map +7 -0
  69. package/dist-esm/lib/ui/components/StylePanel/{DropdownPicker.mjs → StylePanelDropdownPicker.mjs} +20 -17
  70. package/dist-esm/lib/ui/components/StylePanel/StylePanelDropdownPicker.mjs.map +7 -0
  71. package/dist-esm/lib/ui/components/StylePanel/StylePanelSubheading.mjs +8 -0
  72. package/dist-esm/lib/ui/components/StylePanel/StylePanelSubheading.mjs.map +7 -0
  73. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs +2 -0
  74. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs.map +2 -2
  75. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +14 -5
  76. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
  77. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +5 -5
  78. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +1 -1
  79. package/dist-esm/lib/ui/context/actions.mjs +7 -8
  80. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  81. package/dist-esm/lib/ui/hooks/useExportAs.mjs +3 -2
  82. package/dist-esm/lib/ui/hooks/useExportAs.mjs.map +2 -2
  83. package/dist-esm/lib/ui/hooks/useTools.mjs +1 -1
  84. package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
  85. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +2 -2
  86. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +1 -1
  87. package/dist-esm/lib/ui/version.mjs +3 -3
  88. package/dist-esm/lib/ui/version.mjs.map +1 -1
  89. package/dist-esm/lib/utils/export/copyAs.mjs +1 -2
  90. package/dist-esm/lib/utils/export/copyAs.mjs.map +2 -2
  91. package/dist-esm/lib/utils/export/export.mjs +0 -20
  92. package/dist-esm/lib/utils/export/export.mjs.map +2 -2
  93. package/dist-esm/lib/utils/export/exportAs.mjs +1 -2
  94. package/dist-esm/lib/utils/export/exportAs.mjs.map +2 -2
  95. package/package.json +3 -3
  96. package/src/index.ts +38 -21
  97. package/src/lib/shapes/arrow/arrowLabel.ts +8 -0
  98. package/src/lib/shapes/shared/PlainTextLabel.tsx +0 -6
  99. package/src/lib/shapes/shared/useEditablePlainText.ts +0 -6
  100. package/src/lib/shapes/shared/useImageOrVideoAsset.ts +0 -7
  101. package/src/lib/ui/components/StylePanel/DefaultStylePanel.tsx +27 -13
  102. package/src/lib/ui/components/StylePanel/DefaultStylePanelContent.tsx +260 -381
  103. package/src/lib/ui/components/{primitives/TldrawUiButtonPicker.tsx → StylePanel/StylePanelButtonPicker.tsx} +63 -50
  104. package/src/lib/ui/components/StylePanel/StylePanelContext.tsx +63 -0
  105. package/src/lib/ui/components/StylePanel/{DoubleDropdownPicker.tsx → StylePanelDoubleDropdownPicker.tsx} +28 -19
  106. package/src/lib/ui/components/StylePanel/StylePanelDropdownPicker.tsx +119 -0
  107. package/src/lib/ui/components/StylePanel/StylePanelSubheading.tsx +9 -0
  108. package/src/lib/ui/components/primitives/TldrawUiToolbar.tsx +3 -0
  109. package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +18 -8
  110. package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +6 -6
  111. package/src/lib/ui/context/actions.tsx +7 -8
  112. package/src/lib/ui/hooks/useExportAs.ts +3 -2
  113. package/src/lib/ui/hooks/useTools.tsx +1 -1
  114. package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +2 -2
  115. package/src/lib/ui/version.ts +3 -3
  116. package/src/lib/ui.css +16 -2
  117. package/src/lib/utils/export/copyAs.ts +1 -24
  118. package/src/lib/utils/export/export.ts +0 -36
  119. package/src/lib/utils/export/exportAs.ts +1 -32
  120. package/src/test/getCulledShapes.test.tsx +71 -2
  121. package/tldraw.css +24 -5
  122. package/dist-cjs/lib/ui/components/StylePanel/DoubleDropdownPicker.js.map +0 -7
  123. package/dist-cjs/lib/ui/components/StylePanel/DropdownPicker.js.map +0 -7
  124. package/dist-cjs/lib/ui/components/primitives/TldrawUiButtonPicker.js.map +0 -7
  125. package/dist-esm/lib/ui/components/StylePanel/DoubleDropdownPicker.mjs.map +0 -7
  126. package/dist-esm/lib/ui/components/StylePanel/DropdownPicker.mjs.map +0 -7
  127. package/dist-esm/lib/ui/components/primitives/TldrawUiButtonPicker.mjs.map +0 -7
  128. package/src/lib/ui/components/StylePanel/DropdownPicker.tsx +0 -110
@@ -4,46 +4,52 @@ import {
4
4
  SharedStyle,
5
5
  StyleProp,
6
6
  TLDefaultColorStyle,
7
- TLDefaultColorTheme,
8
7
  useEditor,
9
8
  } from '@tldraw/editor'
10
9
  import { memo, ReactElement, useMemo, useRef } from 'react'
10
+ import { useDefaultColorTheme } from '../../../shapes/shared/useDefaultColorTheme'
11
11
  import { StyleValuesForUi } from '../../../styles'
12
12
  import { PORTRAIT_BREAKPOINT } from '../../constants'
13
13
  import { useBreakpoint } from '../../context/breakpoints'
14
14
  import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
15
15
  import { useTranslation } from '../../hooks/useTranslation/useTranslation'
16
- import { TldrawUiButtonIcon } from './Button/TldrawUiButtonIcon'
17
- import { TldrawUiToolbarToggleGroup, TldrawUiToolbarToggleItem } from './TldrawUiToolbar'
18
- import { TldrawUiGrid, TldrawUiRow } from './layout'
16
+ import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
17
+ import {
18
+ TldrawUiToolbar,
19
+ TldrawUiToolbarToggleGroup,
20
+ TldrawUiToolbarToggleItem,
21
+ } from '../primitives/TldrawUiToolbar'
22
+ import { TldrawUiGrid, TldrawUiRow } from '../primitives/layout'
23
+ import { useStylePanelContext } from './StylePanelContext'
24
+ import { StylePanelSubheading } from './StylePanelSubheading'
19
25
 
20
26
  /** @public */
21
- export interface TLUiButtonPickerProps<T extends string> {
27
+ export interface StylePanelButtonPickerProps<T extends string> {
22
28
  title: string
23
29
  uiType: string
24
30
  style: StyleProp<T>
25
31
  value: SharedStyle<T>
26
32
  items: StyleValuesForUi<T>
27
- theme: TLDefaultColorTheme
28
- onValueChange(style: StyleProp<T>, value: T): void
33
+ onValueChange?(style: StyleProp<T>, value: T): void
29
34
  onHistoryMark?(id: string): void
30
35
  }
31
36
 
32
37
  /** @public */
33
- export const TldrawUiButtonPicker = memo(function TldrawUiButtonPicker<T extends string>(
34
- props: TLUiButtonPickerProps<T>
38
+ export const StylePanelButtonPicker = memo(function StylePanelButtonPicker<T extends string>(
39
+ props: StylePanelButtonPickerProps<T>
35
40
  ) {
41
+ const ctx = useStylePanelContext()
42
+
36
43
  const {
37
44
  uiType,
38
45
  items,
39
46
  title,
40
47
  style,
41
48
  value,
42
- // columns = clamp(items.length, 2, 4),
43
- onValueChange,
44
- onHistoryMark,
45
- theme,
49
+ onValueChange = ctx.onValueChange,
50
+ onHistoryMark = ctx.onHistoryMark,
46
51
  } = props
52
+ const theme = useDefaultColorTheme()
47
53
  const editor = useEditor()
48
54
  const msg = useTranslation()
49
55
  const breakpoint = useBreakpoint()
@@ -117,43 +123,50 @@ export const TldrawUiButtonPicker = memo(function TldrawUiButtonPicker<T extends
117
123
  }
118
124
  }, [editor, breakpoint, value, onHistoryMark, onValueChange, style])
119
125
 
120
- const Wrapper = items.length > 4 ? TldrawUiGrid : TldrawUiRow
126
+ const Layout = items.length > 4 ? TldrawUiGrid : TldrawUiRow
121
127
 
122
128
  return (
123
- <Wrapper asChild>
124
- <TldrawUiToolbarToggleGroup
125
- data-testid={`style.${uiType}`}
126
- type="single"
127
- value={value.type === 'shared' ? value.value : undefined}
128
- >
129
- {items.map((item) => {
130
- const label = title + ' — ' + msg(`${uiType}-style.${item.value}` as TLUiTranslationKey)
131
- return (
132
- <TldrawUiToolbarToggleItem
133
- type="icon"
134
- key={item.value}
135
- data-id={item.value}
136
- data-testid={`style.${uiType}.${item.value}`}
137
- aria-label={label}
138
- value={item.value}
139
- data-state={value.type === 'shared' && value.value === item.value ? 'on' : 'off'}
140
- data-isactive={value.type === 'shared' && value.value === item.value}
141
- title={label}
142
- style={
143
- style === (DefaultColorStyle as StyleProp<unknown>)
144
- ? { color: getColorValue(theme, item.value as TLDefaultColorStyle, 'solid') }
145
- : undefined
146
- }
147
- onPointerEnter={handleButtonPointerEnter}
148
- onPointerDown={handleButtonPointerDown}
149
- onPointerUp={handleButtonPointerUp}
150
- onClick={handleButtonClick}
151
- >
152
- <TldrawUiButtonIcon icon={item.icon} />
153
- </TldrawUiToolbarToggleItem>
154
- )
155
- })}
156
- </TldrawUiToolbarToggleGroup>
157
- </Wrapper>
129
+ <>
130
+ {ctx.showUiLabels && <StylePanelSubheading>{title}</StylePanelSubheading>}
131
+ <TldrawUiToolbar label={title}>
132
+ <TldrawUiToolbarToggleGroup
133
+ data-testid={`style.${uiType}`}
134
+ type="single"
135
+ value={value.type === 'shared' ? value.value : undefined}
136
+ asChild
137
+ >
138
+ <Layout>
139
+ {items.map((item) => {
140
+ const label =
141
+ title + ' — ' + msg(`${uiType}-style.${item.value}` as TLUiTranslationKey)
142
+ return (
143
+ <TldrawUiToolbarToggleItem
144
+ type="icon"
145
+ key={item.value}
146
+ data-id={item.value}
147
+ data-testid={`style.${uiType}.${item.value}`}
148
+ aria-label={label}
149
+ value={item.value}
150
+ data-state={value.type === 'shared' && value.value === item.value ? 'on' : 'off'}
151
+ data-isactive={value.type === 'shared' && value.value === item.value}
152
+ title={label}
153
+ style={
154
+ style === (DefaultColorStyle as StyleProp<unknown>)
155
+ ? { color: getColorValue(theme, item.value as TLDefaultColorStyle, 'solid') }
156
+ : undefined
157
+ }
158
+ onPointerEnter={handleButtonPointerEnter}
159
+ onPointerDown={handleButtonPointerDown}
160
+ onPointerUp={handleButtonPointerUp}
161
+ onClick={handleButtonClick}
162
+ >
163
+ <TldrawUiButtonIcon icon={item.icon} />
164
+ </TldrawUiToolbarToggleItem>
165
+ )
166
+ })}
167
+ </Layout>
168
+ </TldrawUiToolbarToggleGroup>
169
+ </TldrawUiToolbar>
170
+ </>
158
171
  )
159
- }) as <T extends string>(props: TLUiButtonPickerProps<T>) => ReactElement
172
+ }) as <T extends string>(props: StylePanelButtonPickerProps<T>) => ReactElement
@@ -0,0 +1,63 @@
1
+ import { ReadonlySharedStyleMap, StyleProp, useEditor, useValue } from '@tldraw/editor'
2
+ import { createContext, useCallback, useContext } from 'react'
3
+ import { useUiEvents } from '../../context/events'
4
+
5
+ /** @public */
6
+ export interface StylePanelContext {
7
+ styles: ReadonlySharedStyleMap
8
+ showUiLabels: boolean
9
+ onHistoryMark(id: string): void
10
+ onValueChange<T>(style: StyleProp<T>, value: T): void
11
+ }
12
+ const StylePanelContext = createContext<null | StylePanelContext>(null)
13
+
14
+ /** @public */
15
+ export interface StylePanelContextProviderProps {
16
+ children: React.ReactNode
17
+ styles: ReadonlySharedStyleMap
18
+ }
19
+
20
+ /** @public @react */
21
+ export function StylePanelContextProvider({ children, styles }: StylePanelContextProviderProps) {
22
+ const editor = useEditor()
23
+ const trackEvent = useUiEvents()
24
+
25
+ const onHistoryMark = useCallback((id: string) => editor.markHistoryStoppingPoint(id), [editor])
26
+ const showUiLabels = useValue('showUiLabels', () => editor.user.getShowUiLabels(), [editor])
27
+ const onValueChange = useCallback(
28
+ function <T>(style: StyleProp<T>, value: T) {
29
+ editor.run(() => {
30
+ if (editor.isIn('select')) {
31
+ editor.setStyleForSelectedShapes(style, value)
32
+ }
33
+ editor.setStyleForNextShapes(style, value)
34
+ editor.updateInstanceState({ isChangingStyle: true })
35
+ })
36
+
37
+ trackEvent('set-style', { source: 'style-panel', id: style.id, value: value as string })
38
+ },
39
+ [editor, trackEvent]
40
+ )
41
+
42
+ return (
43
+ <StylePanelContext.Provider
44
+ value={{
45
+ styles: styles,
46
+ showUiLabels,
47
+ onHistoryMark,
48
+ onValueChange,
49
+ }}
50
+ >
51
+ {children}
52
+ </StylePanelContext.Provider>
53
+ )
54
+ }
55
+
56
+ /** @public */
57
+ export function useStylePanelContext() {
58
+ const context = useContext(StylePanelContext)
59
+ if (!context) {
60
+ throw new Error('useStylePanelContext must be used within a StylePanelContextProvider')
61
+ }
62
+ return context
63
+ }
@@ -11,8 +11,10 @@ import {
11
11
  } from '../primitives/TldrawUiPopover'
12
12
  import { TldrawUiToolbar, TldrawUiToolbarButton } from '../primitives/TldrawUiToolbar'
13
13
  import { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'
14
+ import { useStylePanelContext } from './StylePanelContext'
14
15
 
15
- interface DoubleDropdownPickerProps<T extends string> {
16
+ /** @public */
17
+ export interface StylePanelDoubleDropdownPickerProps<T extends string> {
16
18
  uiTypeA: string
17
19
  uiTypeB: string
18
20
  label: TLUiTranslationKey | Exclude<string, TLUiTranslationKey>
@@ -24,23 +26,27 @@ interface DoubleDropdownPickerProps<T extends string> {
24
26
  styleB: StyleProp<T>
25
27
  valueA: SharedStyle<T>
26
28
  valueB: SharedStyle<T>
27
- onValueChange(style: StyleProp<T>, value: T): void
29
+ onValueChange?(style: StyleProp<T>, value: T): void
28
30
  }
29
31
 
30
- function DoubleDropdownPickerInner<T extends string>({
31
- label,
32
- uiTypeA,
33
- uiTypeB,
34
- labelA,
35
- labelB,
36
- itemsA,
37
- itemsB,
38
- styleA,
39
- styleB,
40
- valueA,
41
- valueB,
42
- onValueChange,
43
- }: DoubleDropdownPickerProps<T>) {
32
+ function DoubleDropdownPickerInner<T extends string>(
33
+ props: StylePanelDoubleDropdownPickerProps<T>
34
+ ) {
35
+ const ctx = useStylePanelContext()
36
+ const {
37
+ label,
38
+ uiTypeA,
39
+ uiTypeB,
40
+ labelA,
41
+ labelB,
42
+ itemsA,
43
+ itemsB,
44
+ styleA,
45
+ styleB,
46
+ valueA,
47
+ valueB,
48
+ onValueChange = ctx.onValueChange,
49
+ } = props
44
50
  const editor = useEditor()
45
51
  const msg = useTranslation()
46
52
  const [isOpenA, setIsOpenA] = React.useState(false)
@@ -155,6 +161,9 @@ function DoubleDropdownPickerInner<T extends string>({
155
161
  }
156
162
 
157
163
  // need to memo like this to get generics
158
- export const DoubleDropdownPicker = React.memo(
159
- DoubleDropdownPickerInner
160
- ) as typeof DoubleDropdownPickerInner
164
+ /** @public @react */
165
+ export const StylePanelDoubleDropdownPicker = React.memo(DoubleDropdownPickerInner) as <
166
+ T extends string,
167
+ >(
168
+ props: StylePanelDoubleDropdownPickerProps<T>
169
+ ) => React.JSX.Element
@@ -0,0 +1,119 @@
1
+ import { SharedStyle, StyleProp, tlmenus, useEditor } from '@tldraw/editor'
2
+ import * as React from 'react'
3
+ import { StyleValuesForUi } from '../../../styles'
4
+ import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
5
+ import { useTranslation } from '../../hooks/useTranslation/useTranslation'
6
+ import { TLUiIconType } from '../../icon-types'
7
+ import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
8
+ import { TldrawUiButtonLabel } from '../primitives/Button/TldrawUiButtonLabel'
9
+ import {
10
+ TldrawUiPopover,
11
+ TldrawUiPopoverContent,
12
+ TldrawUiPopoverTrigger,
13
+ } from '../primitives/TldrawUiPopover'
14
+ import { TldrawUiToolbar, TldrawUiToolbarButton } from '../primitives/TldrawUiToolbar'
15
+ import { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'
16
+ import { useStylePanelContext } from './StylePanelContext'
17
+
18
+ /** @public */
19
+ export interface StylePanelDropdownPickerProps<T extends string> {
20
+ id: string
21
+ label?: TLUiTranslationKey | Exclude<string, TLUiTranslationKey>
22
+ uiType: string
23
+ stylePanelType: string
24
+ style: StyleProp<T>
25
+ value: SharedStyle<T>
26
+ items: StyleValuesForUi<T>
27
+ type: 'icon' | 'tool' | 'menu'
28
+ onValueChange?(style: StyleProp<T>, value: T): void
29
+ }
30
+
31
+ function DropdownPickerInner<T extends string>(props: StylePanelDropdownPickerProps<T>) {
32
+ const ctx = useStylePanelContext()
33
+ const {
34
+ id,
35
+ label,
36
+ uiType,
37
+ stylePanelType,
38
+ style,
39
+ items,
40
+ type,
41
+ value,
42
+ onValueChange = ctx.onValueChange,
43
+ } = props
44
+ const msg = useTranslation()
45
+ const editor = useEditor()
46
+ const [isOpen, setIsOpen] = React.useState(false)
47
+
48
+ const icon = React.useMemo(
49
+ () => items.find((item) => value.type === 'shared' && item.value === value.value)?.icon,
50
+ [items, value]
51
+ )
52
+
53
+ const stylePanelName = msg(`style-panel.${stylePanelType}` as TLUiTranslationKey)
54
+
55
+ const titleStr =
56
+ value.type === 'mixed'
57
+ ? msg('style-panel.mixed')
58
+ : stylePanelName + ' — ' + msg(`${uiType}-style.${value.value}` as TLUiTranslationKey)
59
+ const labelStr = label ? msg(label) : ''
60
+
61
+ const popoverId = `style panel ${id}`
62
+ return (
63
+ <TldrawUiToolbar label={stylePanelName}>
64
+ <TldrawUiPopover
65
+ id={popoverId}
66
+ open={isOpen}
67
+ onOpenChange={setIsOpen}
68
+ className="tlui-style-panel__dropdown-picker"
69
+ >
70
+ <TldrawUiPopoverTrigger>
71
+ <TldrawUiToolbarButton
72
+ type={type}
73
+ data-testid={`style.${uiType}`}
74
+ data-direction="left"
75
+ title={titleStr}
76
+ >
77
+ {labelStr && <TldrawUiButtonLabel>{labelStr}</TldrawUiButtonLabel>}
78
+ <TldrawUiButtonIcon icon={(icon as TLUiIconType) ?? 'mixed'} />
79
+ </TldrawUiToolbarButton>
80
+ </TldrawUiPopoverTrigger>
81
+ <TldrawUiPopoverContent side="left" align="center">
82
+ <TldrawUiToolbar orientation={items.length > 4 ? 'grid' : 'horizontal'} label={labelStr}>
83
+ <TldrawUiMenuContextProvider type="icons" sourceId="style-panel">
84
+ {items.map((item) => {
85
+ return (
86
+ <TldrawUiToolbarButton
87
+ key={item.value}
88
+ type="icon"
89
+ data-testid={`style.${uiType}.${item.value}`}
90
+ title={
91
+ stylePanelName +
92
+ ' — ' +
93
+ msg(`${uiType}-style.${item.value}` as TLUiTranslationKey)
94
+ }
95
+ isActive={icon === item.icon}
96
+ onClick={() => {
97
+ ctx.onHistoryMark('select style dropdown item')
98
+ onValueChange(style, item.value)
99
+ tlmenus.deleteOpenMenu(popoverId, editor.contextId)
100
+ setIsOpen(false)
101
+ }}
102
+ >
103
+ <TldrawUiButtonIcon icon={item.icon} />
104
+ </TldrawUiToolbarButton>
105
+ )
106
+ })}
107
+ </TldrawUiMenuContextProvider>
108
+ </TldrawUiToolbar>
109
+ </TldrawUiPopoverContent>
110
+ </TldrawUiPopover>
111
+ </TldrawUiToolbar>
112
+ )
113
+ }
114
+
115
+ // need to export like this to get generics
116
+ /** @public @react */
117
+ export const StylePanelDropdownPicker = React.memo(DropdownPickerInner) as <T extends string>(
118
+ props: StylePanelDropdownPickerProps<T>
119
+ ) => React.JSX.Element
@@ -0,0 +1,9 @@
1
+ /** @public */
2
+ export interface StylePanelSubheadingProps {
3
+ children: React.ReactNode
4
+ }
5
+
6
+ /** @public @react */
7
+ export function StylePanelSubheading({ children }: StylePanelSubheadingProps) {
8
+ return <h3 className="tlui-style-panel__subheading">{children}</h3>
9
+ }
@@ -94,6 +94,7 @@ export interface TLUiToolbarToggleGroupProps extends React.HTMLAttributes<HTMLDi
94
94
  // TODO: fix up this type later
95
95
  defaultValue?: any
96
96
  type: 'single' | 'multiple'
97
+ asChild?: boolean
97
98
  }
98
99
 
99
100
  /** @public @react */
@@ -101,10 +102,12 @@ export const TldrawUiToolbarToggleGroup = ({
101
102
  children,
102
103
  className,
103
104
  type,
105
+ asChild,
104
106
  ...props
105
107
  }: TLUiToolbarToggleGroupProps) => {
106
108
  return (
107
109
  <_Toolbar.ToggleGroup
110
+ asChild={asChild}
108
111
  type={type}
109
112
  {...props}
110
113
  // TODO: this fixes a bug in Radix until they fix it.
@@ -142,6 +142,7 @@ function TooltipSingleton() {
142
142
  const [isOpen, setIsOpen] = useState(false)
143
143
  const triggerRef = useRef<HTMLDivElement>(null)
144
144
  const isFirstShowRef = useRef(true)
145
+ const editor = useMaybeEditor()
145
146
 
146
147
  const currentTooltip = useValue(
147
148
  'current tooltip',
@@ -149,6 +150,15 @@ function TooltipSingleton() {
149
150
  []
150
151
  )
151
152
 
153
+ const cameraState = useValue('camera state', () => editor?.getCameraState(), [editor])
154
+
155
+ // Hide tooltip when camera is moving (panning/zooming)
156
+ useEffect(() => {
157
+ if (cameraState === 'moving' && isOpen && currentTooltip) {
158
+ tooltipManager.hideTooltip(editor, currentTooltip.id, true)
159
+ }
160
+ }, [cameraState, isOpen, currentTooltip, editor])
161
+
152
162
  // Update open state and trigger position
153
163
  useEffect(() => {
154
164
  let timer: ReturnType<typeof setTimeout> | null = null
@@ -231,12 +241,11 @@ export const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProp
231
241
  const editor = useMaybeEditor()
232
242
  const tooltipId = useRef<string>(uniqueId())
233
243
  const hasProvider = useContext(TooltipSingletonContext)
244
+ const showUiLabels = useValue('showUiLabels', () => editor?.user.getShowUiLabels(), [editor])
234
245
 
235
246
  const orientationCtx = useTldrawUiOrientation()
236
247
  const sideToUse = side ?? orientationCtx.tooltipSide
237
248
 
238
- const camera = useValue('camera', () => editor?.getCamera(), [])
239
-
240
249
  useEffect(() => {
241
250
  const currentTooltipId = tooltipId.current
242
251
  return () => {
@@ -246,17 +255,18 @@ export const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProp
246
255
  }
247
256
  }, [editor, hasProvider])
248
257
 
249
- useEffect(() => {
250
- tooltipManager.hideTooltip(editor, tooltipId.current, true)
251
- }, [editor, camera])
252
-
253
258
  // Don't show tooltip if disabled, no content, or UI labels are disabled
254
259
  if (disabled || !content) {
255
260
  return <>{children}</>
256
261
  }
257
262
 
258
- const delayDurationToUse =
259
- delayDuration ?? (editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS)
263
+ let delayDurationToUse
264
+ if (showUiLabels) {
265
+ delayDurationToUse = 0
266
+ } else {
267
+ delayDurationToUse =
268
+ delayDuration ?? (editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS)
269
+ }
260
270
 
261
271
  // Fallback to old behavior if no provider
262
272
  if (!hasProvider) {
@@ -213,7 +213,7 @@ export function TldrawUiMenuItem<
213
213
  icon={icon}
214
214
  onSelect={onSelect}
215
215
  onDragStart={onDragStart}
216
- labelToUse={labelToUse}
216
+ labelStr={labelStr}
217
217
  titleStr={titleStr}
218
218
  disabled={disabled}
219
219
  isSelected={isSelected}
@@ -247,7 +247,7 @@ export function TldrawUiMenuItem<
247
247
  icon={icon}
248
248
  onSelect={onSelect}
249
249
  onDragStart={onDragStart}
250
- labelToUse={labelToUse}
250
+ labelStr={labelStr}
251
251
  titleStr={titleStr}
252
252
  disabled={disabled}
253
253
  isSelected={isSelected}
@@ -392,7 +392,7 @@ function useDraggableEvents(
392
392
 
393
393
  function DraggableToolbarButton({
394
394
  id,
395
- labelToUse,
395
+ labelStr,
396
396
  titleStr,
397
397
  disabled,
398
398
  isSelected,
@@ -403,7 +403,7 @@ function DraggableToolbarButton({
403
403
  }: {
404
404
  id: string
405
405
  disabled: boolean
406
- labelToUse?: string
406
+ labelStr?: string
407
407
  titleStr?: string
408
408
  isSelected?: boolean
409
409
  icon: TLUiMenuItemProps['icon']
@@ -416,7 +416,7 @@ function DraggableToolbarButton({
416
416
  if (overflow) {
417
417
  return (
418
418
  <TldrawUiToolbarButton
419
- aria-label={labelToUse}
419
+ aria-label={labelStr}
420
420
  aria-pressed={isSelected ? 'true' : 'false'}
421
421
  isActive={isSelected}
422
422
  className="tlui-button-grid__button"
@@ -434,7 +434,7 @@ function DraggableToolbarButton({
434
434
 
435
435
  return (
436
436
  <TldrawUiToolbarButton
437
- aria-label={labelToUse}
437
+ aria-label={labelStr}
438
438
  aria-pressed={isSelected ? 'true' : 'false'}
439
439
  data-testid={`tools.${id}`}
440
440
  data-value={id}
@@ -225,7 +225,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
225
225
  if (ids.length === 0) ids = Array.from(editor.getCurrentPageShapeIds().values())
226
226
  if (ids.length === 0) return
227
227
  trackEvent('export-as', { format: 'svg', source })
228
- helpers.exportAs(ids, 'svg', getExportName(editor, defaultDocumentName))
228
+ helpers.exportAs(ids, { format: 'svg', name: getExportName(editor, defaultDocumentName) })
229
229
  },
230
230
  },
231
231
  {
@@ -241,7 +241,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
241
241
  if (ids.length === 0) ids = Array.from(editor.getCurrentPageShapeIds().values())
242
242
  if (ids.length === 0) return
243
243
  trackEvent('export-as', { format: 'png', source })
244
- helpers.exportAs(ids, 'png', getExportName(editor, defaultDocumentName))
244
+ helpers.exportAs(ids, { format: 'png', name: getExportName(editor, defaultDocumentName) })
245
245
  },
246
246
  },
247
247
  {
@@ -257,11 +257,10 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
257
257
  if (ids.length === 0) ids = Array.from(editor.getCurrentPageShapeIds().values())
258
258
  if (ids.length === 0) return
259
259
  trackEvent('export-all-as', { format: 'svg', source })
260
- helpers.exportAs(
261
- Array.from(editor.getCurrentPageShapeIds()),
262
- 'svg',
263
- getExportName(editor, defaultDocumentName)
264
- )
260
+ helpers.exportAs(Array.from(editor.getCurrentPageShapeIds()), {
261
+ format: 'svg',
262
+ name: getExportName(editor, defaultDocumentName),
263
+ })
265
264
  },
266
265
  },
267
266
  {
@@ -276,7 +275,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
276
275
  const ids = Array.from(editor.getCurrentPageShapeIds().values())
277
276
  if (ids.length === 0) return
278
277
  trackEvent('export-all-as', { format: 'png', source })
279
- helpers.exportAs(ids, 'png', getExportName(editor, defaultDocumentName))
278
+ helpers.exportAs(ids, { format: 'png', name: getExportName(editor, defaultDocumentName) })
280
279
  },
281
280
  },
282
281
  {
@@ -11,12 +11,13 @@ export function useExportAs() {
11
11
  const msg = useTranslation()
12
12
 
13
13
  return useCallback(
14
- (ids: TLShapeId[], format: TLExportType = 'png', name: string | undefined) => {
14
+ (ids: TLShapeId[], opts: { format?: TLExportType; name?: string; scale?: number } = {}) => {
15
15
  assert(editor, 'useExportAs: editor is required')
16
+ const { format = 'png', name, scale = 1 } = opts
16
17
  exportAs(editor, ids, {
17
18
  format,
18
19
  name,
19
- scale: 1,
20
+ scale,
20
21
  }).catch((e) => {
21
22
  console.error(e.message)
22
23
  addToast({
@@ -176,7 +176,7 @@ export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) {
176
176
  editor.createShape({
177
177
  id,
178
178
  type: 'arrow',
179
- props: { start: { x: 0, y: 0 }, end: { x: 200, y: 0 } },
179
+ props: { start: { x: 0, y: 200 }, end: { x: 200, y: 0 } },
180
180
  }),
181
181
  })
182
182
  trackEvent('drag-tool', { source, id: 'arrow' })
@@ -92,9 +92,9 @@ export const DEFAULT_TRANSLATION = {
92
92
  'action.toggle-wrap-mode': 'Toggle Select on wrap',
93
93
  'action.toggle-reduce-motion.menu': 'Reduce motion',
94
94
  'action.toggle-reduce-motion': 'Toggle reduce motion',
95
- 'action.toggle-keyboard-shortcuts.menu': 'Keyboard shortcuts',
95
+ 'action.toggle-keyboard-shortcuts.menu': 'Enable keyboard shortcuts',
96
96
  'action.toggle-keyboard-shortcuts': 'Toggle keyboard shortcuts',
97
- 'action.toggle-ui-labels.menu': 'UI labels',
97
+ 'action.toggle-ui-labels.menu': 'Enable UI labels',
98
98
  'action.toggle-ui-labels': 'Toggle UI labels',
99
99
  'action.toggle-edge-scrolling.menu': 'Edge scrolling',
100
100
  'action.toggle-edge-scrolling': 'Toggle edge scrolling',
@@ -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.5f82fb812214'
4
+ export const version = '3.16.0-canary.5f8d98bccb38'
5
5
  export const publishDates = {
6
6
  major: '2024-09-13T14:36:29.063Z',
7
- minor: '2025-08-28T18:10:41.249Z',
8
- patch: '2025-08-28T18:10:41.249Z',
7
+ minor: '2025-09-11T07:12:59.993Z',
8
+ patch: '2025-09-11T07:12:59.993Z',
9
9
  }