@tldraw/editor 3.14.0-canary.fd2ad122b803 → 3.14.0-canary.ff61ab6deaa2

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 (206) hide show
  1. package/dist-cjs/index.d.ts +59 -63
  2. package/dist-cjs/index.js +10 -8
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/editor/Editor.js +21 -58
  5. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  6. package/dist-cjs/lib/editor/derivations/bindingsIndex.js +22 -22
  7. package/dist-cjs/lib/editor/derivations/bindingsIndex.js.map +2 -2
  8. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +16 -20
  9. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +3 -3
  10. package/dist-cjs/lib/editor/derivations/parentsToChildren.js +16 -16
  11. package/dist-cjs/lib/editor/derivations/parentsToChildren.js.map +2 -2
  12. package/dist-cjs/lib/editor/managers/{ClickManager.js → ClickManager/ClickManager.js} +1 -1
  13. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +7 -0
  14. package/dist-cjs/lib/editor/managers/{EdgeScrollManager.js → EdgeScrollManager/EdgeScrollManager.js} +2 -2
  15. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +7 -0
  16. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +7 -0
  17. package/dist-cjs/lib/editor/managers/{FontManager.js → FontManager/FontManager.js} +5 -1
  18. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +7 -0
  19. package/dist-cjs/lib/editor/managers/{HistoryManager.js → HistoryManager/HistoryManager.js} +64 -6
  20. package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js.map +7 -0
  21. package/dist-cjs/lib/editor/managers/{ScribbleManager.js → ScribbleManager/ScribbleManager.js} +1 -1
  22. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js.map +7 -0
  23. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +7 -0
  24. package/dist-cjs/lib/editor/managers/{TickManager.js → TickManager/TickManager.js} +1 -1
  25. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +7 -0
  26. package/dist-cjs/lib/editor/managers/{UserPreferencesManager.js → UserPreferencesManager/UserPreferencesManager.js} +1 -1
  27. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +7 -0
  28. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +8 -0
  29. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  30. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js +6 -0
  31. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +2 -2
  32. package/dist-cjs/lib/exports/getSvgJsx.js.map +1 -1
  33. package/dist-cjs/lib/primitives/Box.js +39 -33
  34. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  35. package/dist-cjs/lib/primitives/Vec.js +13 -8
  36. package/dist-cjs/lib/primitives/Vec.js.map +2 -2
  37. package/dist-cjs/lib/primitives/geometry/Arc2d.js +41 -21
  38. package/dist-cjs/lib/primitives/geometry/Arc2d.js.map +2 -2
  39. package/dist-cjs/lib/primitives/geometry/Circle2d.js +11 -11
  40. package/dist-cjs/lib/primitives/geometry/Circle2d.js.map +2 -2
  41. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js +13 -16
  42. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
  43. package/dist-cjs/lib/primitives/geometry/CubicSpline2d.js +4 -4
  44. package/dist-cjs/lib/primitives/geometry/CubicSpline2d.js.map +2 -2
  45. package/dist-cjs/lib/primitives/geometry/Edge2d.js +14 -17
  46. package/dist-cjs/lib/primitives/geometry/Edge2d.js.map +2 -2
  47. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js +10 -10
  48. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js.map +2 -2
  49. package/dist-cjs/lib/primitives/geometry/Point2d.js +6 -6
  50. package/dist-cjs/lib/primitives/geometry/Point2d.js.map +2 -2
  51. package/dist-cjs/lib/primitives/geometry/Polygon2d.js +3 -0
  52. package/dist-cjs/lib/primitives/geometry/Polygon2d.js.map +2 -2
  53. package/dist-cjs/lib/primitives/geometry/Polyline2d.js +8 -5
  54. package/dist-cjs/lib/primitives/geometry/Polyline2d.js.map +2 -2
  55. package/dist-cjs/lib/primitives/geometry/Rectangle2d.js +22 -11
  56. package/dist-cjs/lib/primitives/geometry/Rectangle2d.js.map +2 -2
  57. package/dist-cjs/lib/primitives/geometry/Stadium2d.js +22 -22
  58. package/dist-cjs/lib/primitives/geometry/Stadium2d.js.map +2 -2
  59. package/dist-cjs/lib/utils/areShapesContentEqual.js +1 -1
  60. package/dist-cjs/lib/utils/areShapesContentEqual.js.map +2 -2
  61. package/dist-cjs/lib/utils/reorderShapes.js +11 -10
  62. package/dist-cjs/lib/utils/reorderShapes.js.map +2 -2
  63. package/dist-cjs/lib/utils/richText.js.map +1 -1
  64. package/dist-cjs/version.js +3 -3
  65. package/dist-cjs/version.js.map +1 -1
  66. package/dist-esm/index.d.mts +59 -63
  67. package/dist-esm/index.mjs +14 -8
  68. package/dist-esm/index.mjs.map +2 -2
  69. package/dist-esm/lib/editor/Editor.mjs +21 -58
  70. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  71. package/dist-esm/lib/editor/derivations/bindingsIndex.mjs +22 -22
  72. package/dist-esm/lib/editor/derivations/bindingsIndex.mjs.map +2 -2
  73. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +16 -20
  74. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +3 -3
  75. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs +16 -16
  76. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs.map +2 -2
  77. package/dist-esm/lib/editor/managers/{ClickManager.mjs → ClickManager/ClickManager.mjs} +1 -1
  78. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +7 -0
  79. package/dist-esm/lib/editor/managers/{EdgeScrollManager.mjs → EdgeScrollManager/EdgeScrollManager.mjs} +2 -2
  80. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +7 -0
  81. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +7 -0
  82. package/dist-esm/lib/editor/managers/{FontManager.mjs → FontManager/FontManager.mjs} +5 -1
  83. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +7 -0
  84. package/dist-esm/lib/editor/managers/{HistoryManager.mjs → HistoryManager/HistoryManager.mjs} +60 -2
  85. package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs.map +7 -0
  86. package/dist-esm/lib/editor/managers/{ScribbleManager.mjs → ScribbleManager/ScribbleManager.mjs} +1 -1
  87. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs.map +7 -0
  88. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +7 -0
  89. package/dist-esm/lib/editor/managers/{TickManager.mjs → TickManager/TickManager.mjs} +1 -1
  90. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +7 -0
  91. package/dist-esm/lib/editor/managers/{UserPreferencesManager.mjs → UserPreferencesManager/UserPreferencesManager.mjs} +1 -1
  92. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +7 -0
  93. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +8 -0
  94. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  95. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs +6 -0
  96. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +2 -2
  97. package/dist-esm/lib/exports/getSvgJsx.mjs.map +1 -1
  98. package/dist-esm/lib/primitives/Box.mjs +39 -33
  99. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  100. package/dist-esm/lib/primitives/Vec.mjs +13 -8
  101. package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
  102. package/dist-esm/lib/primitives/geometry/Arc2d.mjs +41 -21
  103. package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
  104. package/dist-esm/lib/primitives/geometry/Circle2d.mjs +11 -11
  105. package/dist-esm/lib/primitives/geometry/Circle2d.mjs.map +2 -2
  106. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +13 -16
  107. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
  108. package/dist-esm/lib/primitives/geometry/CubicSpline2d.mjs +4 -4
  109. package/dist-esm/lib/primitives/geometry/CubicSpline2d.mjs.map +2 -2
  110. package/dist-esm/lib/primitives/geometry/Edge2d.mjs +14 -17
  111. package/dist-esm/lib/primitives/geometry/Edge2d.mjs.map +2 -2
  112. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs +11 -11
  113. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs.map +2 -2
  114. package/dist-esm/lib/primitives/geometry/Point2d.mjs +6 -6
  115. package/dist-esm/lib/primitives/geometry/Point2d.mjs.map +2 -2
  116. package/dist-esm/lib/primitives/geometry/Polygon2d.mjs +3 -0
  117. package/dist-esm/lib/primitives/geometry/Polygon2d.mjs.map +2 -2
  118. package/dist-esm/lib/primitives/geometry/Polyline2d.mjs +8 -5
  119. package/dist-esm/lib/primitives/geometry/Polyline2d.mjs.map +2 -2
  120. package/dist-esm/lib/primitives/geometry/Rectangle2d.mjs +22 -11
  121. package/dist-esm/lib/primitives/geometry/Rectangle2d.mjs.map +2 -2
  122. package/dist-esm/lib/primitives/geometry/Stadium2d.mjs +22 -22
  123. package/dist-esm/lib/primitives/geometry/Stadium2d.mjs.map +2 -2
  124. package/dist-esm/lib/utils/areShapesContentEqual.mjs +1 -1
  125. package/dist-esm/lib/utils/areShapesContentEqual.mjs.map +2 -2
  126. package/dist-esm/lib/utils/reorderShapes.mjs +11 -10
  127. package/dist-esm/lib/utils/reorderShapes.mjs.map +2 -2
  128. package/dist-esm/lib/utils/richText.mjs.map +1 -1
  129. package/dist-esm/version.mjs +3 -3
  130. package/dist-esm/version.mjs.map +1 -1
  131. package/package.json +7 -7
  132. package/src/index.ts +14 -7
  133. package/src/lib/editor/Editor.ts +24 -60
  134. package/src/lib/editor/derivations/bindingsIndex.ts +27 -26
  135. package/src/lib/editor/derivations/notVisibleShapes.ts +24 -25
  136. package/src/lib/editor/derivations/parentsToChildren.ts +28 -25
  137. package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +442 -0
  138. package/src/lib/editor/managers/{ClickManager.ts → ClickManager/ClickManager.ts} +3 -3
  139. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +374 -0
  140. package/src/lib/editor/managers/{EdgeScrollManager.ts → EdgeScrollManager/EdgeScrollManager.ts} +3 -3
  141. package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +455 -0
  142. package/src/lib/editor/managers/{FocusManager.ts → FocusManager/FocusManager.ts} +1 -1
  143. package/src/lib/editor/managers/FontManager/FontManager.test.ts +263 -0
  144. package/src/lib/editor/managers/{FontManager.ts → FontManager/FontManager.ts} +6 -2
  145. package/src/lib/editor/managers/{HistoryManager.test.ts → HistoryManager/HistoryManager.test.ts} +388 -1
  146. package/src/lib/editor/managers/{HistoryManager.ts → HistoryManager/HistoryManager.ts} +73 -2
  147. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +624 -0
  148. package/src/lib/editor/managers/{ScribbleManager.ts → ScribbleManager/ScribbleManager.ts} +2 -2
  149. package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +485 -0
  150. package/src/lib/editor/managers/TextManager/TextManager.test.ts +411 -0
  151. package/src/lib/editor/managers/{TextManager.ts → TextManager/TextManager.ts} +1 -1
  152. package/src/lib/editor/managers/TickManager/TickManager.test.ts +314 -0
  153. package/src/lib/editor/managers/{TickManager.ts → TickManager/TickManager.ts} +2 -2
  154. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +591 -0
  155. package/src/lib/editor/managers/{UserPreferencesManager.ts → UserPreferencesManager/UserPreferencesManager.ts} +2 -2
  156. package/src/lib/editor/shapes/ShapeUtil.ts +10 -1
  157. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +8 -0
  158. package/src/lib/exports/getSvgJsx.tsx +1 -1
  159. package/src/lib/primitives/Box.test.ts +588 -7
  160. package/src/lib/primitives/Box.ts +41 -33
  161. package/src/lib/primitives/Vec.test.ts +2 -2
  162. package/src/lib/primitives/Vec.ts +13 -8
  163. package/src/lib/primitives/geometry/Arc2d.ts +42 -23
  164. package/src/lib/primitives/geometry/Circle2d.ts +12 -12
  165. package/src/lib/primitives/geometry/CubicBezier2d.test.ts +5 -0
  166. package/src/lib/primitives/geometry/CubicBezier2d.ts +13 -17
  167. package/src/lib/primitives/geometry/CubicSpline2d.ts +5 -5
  168. package/src/lib/primitives/geometry/Edge2d.ts +14 -18
  169. package/src/lib/primitives/geometry/Ellipse2d.ts +12 -13
  170. package/src/lib/primitives/geometry/Point2d.ts +6 -6
  171. package/src/lib/primitives/geometry/Polygon2d.ts +4 -0
  172. package/src/lib/primitives/geometry/Polyline2d.ts +10 -7
  173. package/src/lib/primitives/geometry/Rectangle2d.ts +24 -11
  174. package/src/lib/primitives/geometry/Stadium2d.ts +22 -23
  175. package/src/lib/utils/areShapesContentEqual.ts +2 -1
  176. package/src/lib/utils/reorderShapes.ts +10 -13
  177. package/src/lib/utils/richText.ts +1 -1
  178. package/src/version.ts +3 -3
  179. package/dist-cjs/lib/editor/managers/ClickManager.js.map +0 -7
  180. package/dist-cjs/lib/editor/managers/EdgeScrollManager.js.map +0 -7
  181. package/dist-cjs/lib/editor/managers/FocusManager.js.map +0 -7
  182. package/dist-cjs/lib/editor/managers/FontManager.js.map +0 -7
  183. package/dist-cjs/lib/editor/managers/HistoryManager.js.map +0 -7
  184. package/dist-cjs/lib/editor/managers/ScribbleManager.js.map +0 -7
  185. package/dist-cjs/lib/editor/managers/Stack.js +0 -82
  186. package/dist-cjs/lib/editor/managers/Stack.js.map +0 -7
  187. package/dist-cjs/lib/editor/managers/TextManager.js.map +0 -7
  188. package/dist-cjs/lib/editor/managers/TickManager.js.map +0 -7
  189. package/dist-cjs/lib/editor/managers/UserPreferencesManager.js.map +0 -7
  190. package/dist-esm/lib/editor/managers/ClickManager.mjs.map +0 -7
  191. package/dist-esm/lib/editor/managers/EdgeScrollManager.mjs.map +0 -7
  192. package/dist-esm/lib/editor/managers/FocusManager.mjs.map +0 -7
  193. package/dist-esm/lib/editor/managers/FontManager.mjs.map +0 -7
  194. package/dist-esm/lib/editor/managers/HistoryManager.mjs.map +0 -7
  195. package/dist-esm/lib/editor/managers/ScribbleManager.mjs.map +0 -7
  196. package/dist-esm/lib/editor/managers/Stack.mjs +0 -62
  197. package/dist-esm/lib/editor/managers/Stack.mjs.map +0 -7
  198. package/dist-esm/lib/editor/managers/TextManager.mjs.map +0 -7
  199. package/dist-esm/lib/editor/managers/TickManager.mjs.map +0 -7
  200. package/dist-esm/lib/editor/managers/UserPreferencesManager.mjs.map +0 -7
  201. package/src/lib/editor/managers/ScribbleManager.test.ts +0 -32
  202. package/src/lib/editor/managers/Stack.ts +0 -71
  203. /package/dist-cjs/lib/editor/managers/{FocusManager.js → FocusManager/FocusManager.js} +0 -0
  204. /package/dist-cjs/lib/editor/managers/{TextManager.js → TextManager/TextManager.js} +0 -0
  205. /package/dist-esm/lib/editor/managers/{FocusManager.mjs → FocusManager/FocusManager.mjs} +0 -0
  206. /package/dist-esm/lib/editor/managers/{TextManager.mjs → TextManager/TextManager.mjs} +0 -0
@@ -0,0 +1,411 @@
1
+ import { Editor } from '../../Editor'
2
+ import { TextManager, TLMeasureTextSpanOpts } from './TextManager'
3
+
4
+ // Create a simple mock DOM environment
5
+ const mockElement = {
6
+ classList: { add: jest.fn() },
7
+ tabIndex: -1,
8
+ cloneNode: jest.fn(),
9
+ innerHTML: '',
10
+ textContent: '',
11
+ setAttribute: jest.fn(),
12
+ style: { setProperty: jest.fn() },
13
+ scrollWidth: 100,
14
+ getBoundingClientRect: jest.fn(() => ({
15
+ width: 100,
16
+ height: 20,
17
+ left: 0,
18
+ top: 0,
19
+ right: 100,
20
+ bottom: 20,
21
+ })),
22
+ remove: jest.fn(),
23
+ insertAdjacentElement: jest.fn(),
24
+ childNodes: [],
25
+ }
26
+
27
+ // Mock document.createElement to return our mock element
28
+ const mockCreateElement = jest.fn(() => {
29
+ const element = { ...mockElement }
30
+ element.cloneNode = jest.fn(() => ({ ...element }))
31
+ return element
32
+ })
33
+
34
+ // Mock editor
35
+ const mockEditor = {
36
+ getContainer: jest.fn(() => ({
37
+ appendChild: jest.fn(),
38
+ })),
39
+ } as unknown as Editor
40
+
41
+ // Setup global mocks
42
+ global.document = {
43
+ createElement: mockCreateElement,
44
+ } as any
45
+
46
+ global.Range = jest.fn(() => ({
47
+ setStart: jest.fn(),
48
+ setEnd: jest.fn(),
49
+ getClientRects: jest.fn(() => [
50
+ {
51
+ width: 10,
52
+ height: 16,
53
+ left: 0,
54
+ top: 0,
55
+ right: 10,
56
+ bottom: 16,
57
+ },
58
+ ]),
59
+ })) as any
60
+
61
+ describe('TextManager', () => {
62
+ let textManager: TextManager
63
+
64
+ beforeEach(() => {
65
+ jest.clearAllMocks()
66
+ textManager = new TextManager(mockEditor)
67
+ })
68
+
69
+ describe('constructor', () => {
70
+ it('should create a TextManager instance', () => {
71
+ expect(textManager).toBeInstanceOf(TextManager)
72
+ expect(textManager.editor).toBe(mockEditor)
73
+ })
74
+ })
75
+
76
+ describe('measureText', () => {
77
+ const defaultOpts = {
78
+ fontStyle: 'normal',
79
+ fontWeight: '400',
80
+ fontFamily: 'Arial',
81
+ fontSize: 16,
82
+ lineHeight: 1.2,
83
+ maxWidth: 200,
84
+ minWidth: null,
85
+ padding: '0px',
86
+ }
87
+
88
+ it('should call measureHtml with normalized text', () => {
89
+ const spy = jest.spyOn(textManager, 'measureHtml')
90
+ textManager.measureText('Hello World', defaultOpts)
91
+ expect(spy).toHaveBeenCalledWith('Hello World', defaultOpts)
92
+ })
93
+
94
+ it('should normalize line breaks', () => {
95
+ const spy = jest.spyOn(textManager, 'measureHtml')
96
+ textManager.measureText('Hello\nWorld\r\nTest', defaultOpts)
97
+ // The text should be normalized to use consistent line breaks
98
+ expect(spy).toHaveBeenCalled()
99
+ })
100
+
101
+ it('should handle empty text', () => {
102
+ const result = textManager.measureText('', defaultOpts)
103
+ expect(result).toHaveProperty('x', 0)
104
+ expect(result).toHaveProperty('y', 0)
105
+ expect(result).toHaveProperty('w')
106
+ expect(result).toHaveProperty('h')
107
+ expect(result).toHaveProperty('scrollWidth')
108
+ })
109
+ })
110
+
111
+ describe('measureHtml', () => {
112
+ const defaultOpts = {
113
+ fontStyle: 'normal',
114
+ fontWeight: '400',
115
+ fontFamily: 'Arial',
116
+ fontSize: 16,
117
+ lineHeight: 1.2,
118
+ maxWidth: 200,
119
+ minWidth: null,
120
+ padding: '0px',
121
+ }
122
+
123
+ it('should return measurement object with correct structure', () => {
124
+ const result = textManager.measureHtml('<span>Test</span>', defaultOpts)
125
+
126
+ expect(result).toMatchObject({
127
+ x: 0,
128
+ y: 0,
129
+ w: expect.any(Number),
130
+ h: expect.any(Number),
131
+ scrollWidth: expect.any(Number),
132
+ })
133
+ })
134
+
135
+ it('should handle null maxWidth', () => {
136
+ const opts = { ...defaultOpts, maxWidth: null }
137
+ const result = textManager.measureHtml('Test', opts)
138
+
139
+ expect(result).toMatchObject({
140
+ x: 0,
141
+ y: 0,
142
+ w: expect.any(Number),
143
+ h: expect.any(Number),
144
+ scrollWidth: expect.any(Number),
145
+ })
146
+ })
147
+
148
+ it('should handle overflow wrap breaking', () => {
149
+ const opts = { ...defaultOpts, disableOverflowWrapBreaking: true }
150
+ const result = textManager.measureHtml('Test', opts)
151
+
152
+ expect(result).toMatchObject({
153
+ x: 0,
154
+ y: 0,
155
+ w: expect.any(Number),
156
+ h: expect.any(Number),
157
+ scrollWidth: expect.any(Number),
158
+ })
159
+ })
160
+
161
+ it('should handle other styles', () => {
162
+ const opts = {
163
+ ...defaultOpts,
164
+ otherStyles: {
165
+ 'text-decoration': 'underline',
166
+ color: 'red',
167
+ },
168
+ }
169
+
170
+ const result = textManager.measureHtml('Test', opts)
171
+ expect(result).toMatchObject({
172
+ x: 0,
173
+ y: 0,
174
+ w: expect.any(Number),
175
+ h: expect.any(Number),
176
+ scrollWidth: expect.any(Number),
177
+ })
178
+ })
179
+ })
180
+
181
+ describe('measureElementTextNodeSpans', () => {
182
+ it('should handle elements with text nodes', () => {
183
+ const mockTextNode = {
184
+ nodeType: 3, // TEXT_NODE
185
+ textContent: 'Hello',
186
+ }
187
+
188
+ const mockElementWithText = {
189
+ childNodes: [mockTextNode],
190
+ getBoundingClientRect: () => ({ left: 0, top: 0 }),
191
+ }
192
+
193
+ const result = textManager.measureElementTextNodeSpans(mockElementWithText as any)
194
+
195
+ expect(result).toHaveProperty('spans')
196
+ expect(result).toHaveProperty('didTruncate')
197
+ expect(Array.isArray(result.spans)).toBe(true)
198
+ expect(typeof result.didTruncate).toBe('boolean')
199
+ })
200
+
201
+ it('should handle empty elements', () => {
202
+ const mockEmptyElement = {
203
+ childNodes: [],
204
+ getBoundingClientRect: () => ({ left: 0, top: 0 }),
205
+ }
206
+
207
+ const result = textManager.measureElementTextNodeSpans(mockEmptyElement as any)
208
+
209
+ expect(result.didTruncate).toBe(false)
210
+ expect(result.spans).toHaveLength(0)
211
+ })
212
+
213
+ it('should handle truncation option', () => {
214
+ const mockTextNode = {
215
+ nodeType: 3, // TEXT_NODE
216
+ textContent: 'Hello World',
217
+ }
218
+
219
+ const mockElementWithText = {
220
+ childNodes: [mockTextNode],
221
+ getBoundingClientRect: () => ({ left: 0, top: 0 }),
222
+ }
223
+
224
+ const result = textManager.measureElementTextNodeSpans(mockElementWithText as any, {
225
+ shouldTruncateToFirstLine: true,
226
+ })
227
+
228
+ expect(result).toHaveProperty('spans')
229
+ expect(result).toHaveProperty('didTruncate')
230
+ })
231
+ })
232
+
233
+ describe('measureTextSpans', () => {
234
+ const defaultOpts: TLMeasureTextSpanOpts = {
235
+ overflow: 'wrap',
236
+ width: 200,
237
+ height: 100,
238
+ padding: 10,
239
+ fontSize: 16,
240
+ fontWeight: '400',
241
+ fontFamily: 'Arial',
242
+ fontStyle: 'normal',
243
+ lineHeight: 1.2,
244
+ textAlign: 'start',
245
+ }
246
+
247
+ it('should return empty array for empty text', () => {
248
+ const result = textManager.measureTextSpans('', defaultOpts)
249
+ expect(result).toEqual([])
250
+ })
251
+
252
+ it('should return array of text spans for non-empty text', () => {
253
+ // Mock measureElementTextNodeSpans to return some spans
254
+ jest.spyOn(textManager, 'measureElementTextNodeSpans').mockReturnValue({
255
+ spans: [
256
+ {
257
+ text: 'Hello World',
258
+ box: { x: 0, y: 0, w: 100, h: 16 },
259
+ },
260
+ ],
261
+ didTruncate: false,
262
+ })
263
+
264
+ const result = textManager.measureTextSpans('Hello World', defaultOpts)
265
+
266
+ expect(Array.isArray(result)).toBe(true)
267
+ expect(result.length).toBeGreaterThan(0)
268
+ expect(result[0]).toHaveProperty('text')
269
+ expect(result[0]).toHaveProperty('box')
270
+ })
271
+
272
+ it('should handle wrap overflow', () => {
273
+ jest.spyOn(textManager, 'measureElementTextNodeSpans').mockReturnValue({
274
+ spans: [
275
+ {
276
+ text: 'Hello World',
277
+ box: { x: 0, y: 0, w: 100, h: 16 },
278
+ },
279
+ ],
280
+ didTruncate: false,
281
+ })
282
+
283
+ const opts = { ...defaultOpts, overflow: 'wrap' as const }
284
+ const result = textManager.measureTextSpans('Hello World', opts)
285
+
286
+ expect(Array.isArray(result)).toBe(true)
287
+ })
288
+
289
+ it('should handle truncate-ellipsis overflow', () => {
290
+ // Mock the calls for ellipsis handling
291
+ jest
292
+ .spyOn(textManager, 'measureElementTextNodeSpans')
293
+ .mockReturnValueOnce({
294
+ spans: [
295
+ {
296
+ text: 'Hello Wo',
297
+ box: { x: 0, y: 0, w: 80, h: 16 },
298
+ },
299
+ ],
300
+ didTruncate: true,
301
+ })
302
+ .mockReturnValueOnce({
303
+ spans: [
304
+ {
305
+ text: '…',
306
+ box: { x: 0, y: 0, w: 10, h: 16 },
307
+ },
308
+ ],
309
+ didTruncate: false,
310
+ })
311
+ .mockReturnValueOnce({
312
+ spans: [
313
+ {
314
+ text: 'Hello W',
315
+ box: { x: 0, y: 0, w: 70, h: 16 },
316
+ },
317
+ ],
318
+ didTruncate: false,
319
+ })
320
+
321
+ const opts = { ...defaultOpts, overflow: 'truncate-ellipsis' as const }
322
+ const result = textManager.measureTextSpans('Hello World', opts)
323
+
324
+ expect(Array.isArray(result)).toBe(true)
325
+ })
326
+
327
+ it('should handle truncate-clip overflow', () => {
328
+ jest.spyOn(textManager, 'measureElementTextNodeSpans').mockReturnValue({
329
+ spans: [
330
+ {
331
+ text: 'Hello Wo',
332
+ box: { x: 0, y: 0, w: 80, h: 16 },
333
+ },
334
+ ],
335
+ didTruncate: true,
336
+ })
337
+
338
+ const opts = { ...defaultOpts, overflow: 'truncate-clip' as const }
339
+ const result = textManager.measureTextSpans('Hello World', opts)
340
+
341
+ expect(Array.isArray(result)).toBe(true)
342
+ })
343
+
344
+ it('should handle different text alignments', () => {
345
+ jest.spyOn(textManager, 'measureElementTextNodeSpans').mockReturnValue({
346
+ spans: [
347
+ {
348
+ text: 'Test',
349
+ box: { x: 0, y: 0, w: 40, h: 16 },
350
+ },
351
+ ],
352
+ didTruncate: false,
353
+ })
354
+
355
+ const alignments: Array<TLMeasureTextSpanOpts['textAlign']> = ['start', 'middle', 'end']
356
+
357
+ alignments.forEach((textAlign) => {
358
+ const opts = { ...defaultOpts, textAlign }
359
+ const result = textManager.measureTextSpans('Test', opts)
360
+ expect(Array.isArray(result)).toBe(true)
361
+ })
362
+ })
363
+
364
+ it('should handle custom font properties', () => {
365
+ jest.spyOn(textManager, 'measureElementTextNodeSpans').mockReturnValue({
366
+ spans: [
367
+ {
368
+ text: 'Test',
369
+ box: { x: 0, y: 0, w: 40, h: 16 },
370
+ },
371
+ ],
372
+ didTruncate: false,
373
+ })
374
+
375
+ const opts = {
376
+ ...defaultOpts,
377
+ fontSize: 18,
378
+ fontFamily: 'Times',
379
+ fontWeight: 'bold',
380
+ fontStyle: 'italic',
381
+ lineHeight: 1.5,
382
+ }
383
+
384
+ const result = textManager.measureTextSpans('Test', opts)
385
+ expect(Array.isArray(result)).toBe(true)
386
+ })
387
+
388
+ it('should handle other styles', () => {
389
+ jest.spyOn(textManager, 'measureElementTextNodeSpans').mockReturnValue({
390
+ spans: [
391
+ {
392
+ text: 'Test',
393
+ box: { x: 0, y: 0, w: 40, h: 16 },
394
+ },
395
+ ],
396
+ didTruncate: false,
397
+ })
398
+
399
+ const opts = {
400
+ ...defaultOpts,
401
+ otherStyles: {
402
+ 'text-shadow': '1px 1px 1px black',
403
+ 'letter-spacing': '1px',
404
+ },
405
+ }
406
+
407
+ const result = textManager.measureTextSpans('Test', opts)
408
+ expect(Array.isArray(result)).toBe(true)
409
+ })
410
+ })
411
+ })
@@ -1,5 +1,5 @@
1
1
  import { BoxModel, TLDefaultHorizontalAlignStyle } from '@tldraw/tlschema'
2
- import { Editor } from '../Editor'
2
+ import { Editor } from '../../Editor'
3
3
 
4
4
  const fixNewLines = /\r?\n|\r/g
5
5