@tldraw/editor 3.14.0-canary.ee1f3f027485 → 3.14.0-canary.efa8f9545620

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 (227) hide show
  1. package/dist-cjs/index.d.ts +128 -104
  2. package/dist-cjs/index.js +8 -8
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/config/TLSessionStateSnapshot.js +1 -12
  5. package/dist-cjs/lib/config/TLSessionStateSnapshot.js.map +3 -3
  6. package/dist-cjs/lib/editor/Editor.js +82 -78
  7. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  8. package/dist-cjs/lib/editor/bindings/BindingUtil.js.map +2 -2
  9. package/dist-cjs/lib/editor/derivations/bindingsIndex.js +22 -22
  10. package/dist-cjs/lib/editor/derivations/bindingsIndex.js.map +2 -2
  11. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +16 -20
  12. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +3 -3
  13. package/dist-cjs/lib/editor/derivations/parentsToChildren.js +16 -16
  14. package/dist-cjs/lib/editor/derivations/parentsToChildren.js.map +2 -2
  15. package/dist-cjs/lib/editor/managers/{ClickManager.js → ClickManager/ClickManager.js} +1 -1
  16. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +7 -0
  17. package/dist-cjs/lib/editor/managers/{EdgeScrollManager.js → EdgeScrollManager/EdgeScrollManager.js} +2 -2
  18. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +7 -0
  19. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +7 -0
  20. package/dist-cjs/lib/editor/managers/{FontManager.js → FontManager/FontManager.js} +4 -1
  21. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +7 -0
  22. package/dist-cjs/lib/editor/managers/{HistoryManager.js → HistoryManager/HistoryManager.js} +64 -6
  23. package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js.map +7 -0
  24. package/dist-cjs/lib/editor/managers/{ScribbleManager.js → ScribbleManager/ScribbleManager.js} +1 -1
  25. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js.map +7 -0
  26. package/dist-cjs/lib/editor/managers/{TextManager.js → TextManager/TextManager.js} +73 -42
  27. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +7 -0
  28. package/dist-cjs/lib/editor/managers/{TickManager.js → TickManager/TickManager.js} +1 -1
  29. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +7 -0
  30. package/dist-cjs/lib/editor/managers/{UserPreferencesManager.js → UserPreferencesManager/UserPreferencesManager.js} +1 -1
  31. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +7 -0
  32. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +8 -0
  33. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  34. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js +6 -0
  35. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +2 -2
  36. package/dist-cjs/lib/editor/tools/StateNode.js +3 -3
  37. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  38. package/dist-cjs/lib/editor/types/emit-types.js.map +1 -1
  39. package/dist-cjs/lib/editor/types/external-content.js.map +1 -1
  40. package/dist-cjs/lib/exports/getSvgJsx.js.map +1 -1
  41. package/dist-cjs/lib/hooks/useCanvasEvents.js +1 -2
  42. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  43. package/dist-cjs/lib/primitives/Box.js +33 -33
  44. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  45. package/dist-cjs/lib/primitives/Vec.js +13 -8
  46. package/dist-cjs/lib/primitives/Vec.js.map +2 -2
  47. package/dist-cjs/lib/primitives/geometry/Arc2d.js +41 -21
  48. package/dist-cjs/lib/primitives/geometry/Arc2d.js.map +2 -2
  49. package/dist-cjs/lib/primitives/geometry/Circle2d.js +11 -11
  50. package/dist-cjs/lib/primitives/geometry/Circle2d.js.map +2 -2
  51. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js +13 -16
  52. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
  53. package/dist-cjs/lib/primitives/geometry/CubicSpline2d.js +4 -4
  54. package/dist-cjs/lib/primitives/geometry/CubicSpline2d.js.map +2 -2
  55. package/dist-cjs/lib/primitives/geometry/Edge2d.js +14 -17
  56. package/dist-cjs/lib/primitives/geometry/Edge2d.js.map +2 -2
  57. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js +10 -10
  58. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js.map +2 -2
  59. package/dist-cjs/lib/primitives/geometry/Point2d.js +6 -6
  60. package/dist-cjs/lib/primitives/geometry/Point2d.js.map +2 -2
  61. package/dist-cjs/lib/primitives/geometry/Polygon2d.js +3 -0
  62. package/dist-cjs/lib/primitives/geometry/Polygon2d.js.map +2 -2
  63. package/dist-cjs/lib/primitives/geometry/Polyline2d.js +8 -5
  64. package/dist-cjs/lib/primitives/geometry/Polyline2d.js.map +2 -2
  65. package/dist-cjs/lib/primitives/geometry/Rectangle2d.js +22 -11
  66. package/dist-cjs/lib/primitives/geometry/Rectangle2d.js.map +2 -2
  67. package/dist-cjs/lib/primitives/geometry/Stadium2d.js +22 -22
  68. package/dist-cjs/lib/primitives/geometry/Stadium2d.js.map +2 -2
  69. package/dist-cjs/lib/utils/reorderShapes.js +11 -10
  70. package/dist-cjs/lib/utils/reorderShapes.js.map +2 -2
  71. package/dist-cjs/lib/utils/richText.js +7 -2
  72. package/dist-cjs/lib/utils/richText.js.map +2 -2
  73. package/dist-cjs/version.js +3 -3
  74. package/dist-cjs/version.js.map +1 -1
  75. package/dist-esm/index.d.mts +128 -104
  76. package/dist-esm/index.mjs +12 -8
  77. package/dist-esm/index.mjs.map +2 -2
  78. package/dist-esm/lib/config/TLSessionStateSnapshot.mjs +1 -1
  79. package/dist-esm/lib/config/TLSessionStateSnapshot.mjs.map +2 -2
  80. package/dist-esm/lib/editor/Editor.mjs +82 -78
  81. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  82. package/dist-esm/lib/editor/bindings/BindingUtil.mjs.map +2 -2
  83. package/dist-esm/lib/editor/derivations/bindingsIndex.mjs +22 -22
  84. package/dist-esm/lib/editor/derivations/bindingsIndex.mjs.map +2 -2
  85. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +16 -20
  86. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +3 -3
  87. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs +16 -16
  88. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs.map +2 -2
  89. package/dist-esm/lib/editor/managers/{ClickManager.mjs → ClickManager/ClickManager.mjs} +1 -1
  90. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +7 -0
  91. package/dist-esm/lib/editor/managers/{EdgeScrollManager.mjs → EdgeScrollManager/EdgeScrollManager.mjs} +2 -2
  92. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +7 -0
  93. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +7 -0
  94. package/dist-esm/lib/editor/managers/{FontManager.mjs → FontManager/FontManager.mjs} +4 -1
  95. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +7 -0
  96. package/dist-esm/lib/editor/managers/{HistoryManager.mjs → HistoryManager/HistoryManager.mjs} +60 -2
  97. package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs.map +7 -0
  98. package/dist-esm/lib/editor/managers/{ScribbleManager.mjs → ScribbleManager/ScribbleManager.mjs} +1 -1
  99. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs.map +7 -0
  100. package/dist-esm/lib/editor/managers/{TextManager.mjs → TextManager/TextManager.mjs} +73 -42
  101. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +7 -0
  102. package/dist-esm/lib/editor/managers/{TickManager.mjs → TickManager/TickManager.mjs} +1 -1
  103. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +7 -0
  104. package/dist-esm/lib/editor/managers/{UserPreferencesManager.mjs → UserPreferencesManager/UserPreferencesManager.mjs} +1 -1
  105. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +7 -0
  106. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +8 -0
  107. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  108. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs +6 -0
  109. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +2 -2
  110. package/dist-esm/lib/editor/tools/StateNode.mjs +3 -3
  111. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  112. package/dist-esm/lib/exports/getSvgJsx.mjs.map +1 -1
  113. package/dist-esm/lib/hooks/useCanvasEvents.mjs +1 -2
  114. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  115. package/dist-esm/lib/primitives/Box.mjs +33 -33
  116. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  117. package/dist-esm/lib/primitives/Vec.mjs +13 -8
  118. package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
  119. package/dist-esm/lib/primitives/geometry/Arc2d.mjs +41 -21
  120. package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
  121. package/dist-esm/lib/primitives/geometry/Circle2d.mjs +11 -11
  122. package/dist-esm/lib/primitives/geometry/Circle2d.mjs.map +2 -2
  123. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +13 -16
  124. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
  125. package/dist-esm/lib/primitives/geometry/CubicSpline2d.mjs +4 -4
  126. package/dist-esm/lib/primitives/geometry/CubicSpline2d.mjs.map +2 -2
  127. package/dist-esm/lib/primitives/geometry/Edge2d.mjs +14 -17
  128. package/dist-esm/lib/primitives/geometry/Edge2d.mjs.map +2 -2
  129. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs +11 -11
  130. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs.map +2 -2
  131. package/dist-esm/lib/primitives/geometry/Point2d.mjs +6 -6
  132. package/dist-esm/lib/primitives/geometry/Point2d.mjs.map +2 -2
  133. package/dist-esm/lib/primitives/geometry/Polygon2d.mjs +3 -0
  134. package/dist-esm/lib/primitives/geometry/Polygon2d.mjs.map +2 -2
  135. package/dist-esm/lib/primitives/geometry/Polyline2d.mjs +8 -5
  136. package/dist-esm/lib/primitives/geometry/Polyline2d.mjs.map +2 -2
  137. package/dist-esm/lib/primitives/geometry/Rectangle2d.mjs +22 -11
  138. package/dist-esm/lib/primitives/geometry/Rectangle2d.mjs.map +2 -2
  139. package/dist-esm/lib/primitives/geometry/Stadium2d.mjs +22 -22
  140. package/dist-esm/lib/primitives/geometry/Stadium2d.mjs.map +2 -2
  141. package/dist-esm/lib/utils/reorderShapes.mjs +11 -10
  142. package/dist-esm/lib/utils/reorderShapes.mjs.map +2 -2
  143. package/dist-esm/lib/utils/richText.mjs +8 -3
  144. package/dist-esm/lib/utils/richText.mjs.map +2 -2
  145. package/dist-esm/version.mjs +3 -3
  146. package/dist-esm/version.mjs.map +1 -1
  147. package/editor.css +433 -482
  148. package/package.json +8 -9
  149. package/src/index.ts +15 -7
  150. package/src/lib/config/TLSessionStateSnapshot.ts +1 -1
  151. package/src/lib/editor/Editor.test.ts +252 -3
  152. package/src/lib/editor/Editor.ts +83 -76
  153. package/src/lib/editor/bindings/BindingUtil.ts +6 -0
  154. package/src/lib/editor/derivations/bindingsIndex.ts +27 -26
  155. package/src/lib/editor/derivations/notVisibleShapes.ts +24 -25
  156. package/src/lib/editor/derivations/parentsToChildren.ts +28 -25
  157. package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +442 -0
  158. package/src/lib/editor/managers/{ClickManager.ts → ClickManager/ClickManager.ts} +3 -3
  159. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +374 -0
  160. package/src/lib/editor/managers/{EdgeScrollManager.ts → EdgeScrollManager/EdgeScrollManager.ts} +3 -3
  161. package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +455 -0
  162. package/src/lib/editor/managers/{FocusManager.ts → FocusManager/FocusManager.ts} +1 -1
  163. package/src/lib/editor/managers/FontManager/FontManager.test.ts +263 -0
  164. package/src/lib/editor/managers/{FontManager.ts → FontManager/FontManager.ts} +5 -2
  165. package/src/lib/editor/managers/{HistoryManager.test.ts → HistoryManager/HistoryManager.test.ts} +388 -1
  166. package/src/lib/editor/managers/{HistoryManager.ts → HistoryManager/HistoryManager.ts} +73 -2
  167. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +624 -0
  168. package/src/lib/editor/managers/{ScribbleManager.ts → ScribbleManager/ScribbleManager.ts} +2 -2
  169. package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +485 -0
  170. package/src/lib/editor/managers/TextManager/TextManager.test.ts +407 -0
  171. package/src/lib/editor/managers/{TextManager.ts → TextManager/TextManager.ts} +119 -87
  172. package/src/lib/editor/managers/TickManager/TickManager.test.ts +314 -0
  173. package/src/lib/editor/managers/{TickManager.ts → TickManager/TickManager.ts} +2 -2
  174. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +591 -0
  175. package/src/lib/editor/managers/{UserPreferencesManager.ts → UserPreferencesManager/UserPreferencesManager.ts} +2 -2
  176. package/src/lib/editor/shapes/ShapeUtil.ts +11 -1
  177. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +8 -0
  178. package/src/lib/editor/tools/StateNode.ts +3 -3
  179. package/src/lib/editor/types/emit-types.ts +4 -0
  180. package/src/lib/editor/types/external-content.ts +11 -2
  181. package/src/lib/exports/getSvgJsx.tsx +1 -1
  182. package/src/lib/hooks/useCanvasEvents.ts +0 -1
  183. package/src/lib/primitives/Box.test.ts +588 -7
  184. package/src/lib/primitives/Box.ts +33 -33
  185. package/src/lib/primitives/Vec.test.ts +2 -2
  186. package/src/lib/primitives/Vec.ts +13 -8
  187. package/src/lib/primitives/geometry/Arc2d.ts +42 -23
  188. package/src/lib/primitives/geometry/Circle2d.ts +12 -12
  189. package/src/lib/primitives/geometry/CubicBezier2d.test.ts +5 -0
  190. package/src/lib/primitives/geometry/CubicBezier2d.ts +13 -17
  191. package/src/lib/primitives/geometry/CubicSpline2d.ts +5 -5
  192. package/src/lib/primitives/geometry/Edge2d.ts +14 -18
  193. package/src/lib/primitives/geometry/Ellipse2d.ts +12 -13
  194. package/src/lib/primitives/geometry/Point2d.ts +6 -6
  195. package/src/lib/primitives/geometry/Polygon2d.ts +4 -0
  196. package/src/lib/primitives/geometry/Polyline2d.ts +10 -7
  197. package/src/lib/primitives/geometry/Rectangle2d.ts +24 -11
  198. package/src/lib/primitives/geometry/Stadium2d.ts +22 -23
  199. package/src/lib/utils/reorderShapes.ts +10 -13
  200. package/src/lib/utils/richText.ts +10 -4
  201. package/src/version.ts +3 -3
  202. package/dist-cjs/lib/editor/managers/ClickManager.js.map +0 -7
  203. package/dist-cjs/lib/editor/managers/EdgeScrollManager.js.map +0 -7
  204. package/dist-cjs/lib/editor/managers/FocusManager.js.map +0 -7
  205. package/dist-cjs/lib/editor/managers/FontManager.js.map +0 -7
  206. package/dist-cjs/lib/editor/managers/HistoryManager.js.map +0 -7
  207. package/dist-cjs/lib/editor/managers/ScribbleManager.js.map +0 -7
  208. package/dist-cjs/lib/editor/managers/Stack.js +0 -82
  209. package/dist-cjs/lib/editor/managers/Stack.js.map +0 -7
  210. package/dist-cjs/lib/editor/managers/TextManager.js.map +0 -7
  211. package/dist-cjs/lib/editor/managers/TickManager.js.map +0 -7
  212. package/dist-cjs/lib/editor/managers/UserPreferencesManager.js.map +0 -7
  213. package/dist-esm/lib/editor/managers/ClickManager.mjs.map +0 -7
  214. package/dist-esm/lib/editor/managers/EdgeScrollManager.mjs.map +0 -7
  215. package/dist-esm/lib/editor/managers/FocusManager.mjs.map +0 -7
  216. package/dist-esm/lib/editor/managers/FontManager.mjs.map +0 -7
  217. package/dist-esm/lib/editor/managers/HistoryManager.mjs.map +0 -7
  218. package/dist-esm/lib/editor/managers/ScribbleManager.mjs.map +0 -7
  219. package/dist-esm/lib/editor/managers/Stack.mjs +0 -62
  220. package/dist-esm/lib/editor/managers/Stack.mjs.map +0 -7
  221. package/dist-esm/lib/editor/managers/TextManager.mjs.map +0 -7
  222. package/dist-esm/lib/editor/managers/TickManager.mjs.map +0 -7
  223. package/dist-esm/lib/editor/managers/UserPreferencesManager.mjs.map +0 -7
  224. package/src/lib/editor/managers/ScribbleManager.test.ts +0 -32
  225. package/src/lib/editor/managers/Stack.ts +0 -71
  226. /package/dist-cjs/lib/editor/managers/{FocusManager.js → FocusManager/FocusManager.js} +0 -0
  227. /package/dist-esm/lib/editor/managers/{FocusManager.mjs → FocusManager/FocusManager.mjs} +0 -0
@@ -1,4 +1,4 @@
1
- import { TLAssetId } from '@tldraw/tlschema'
1
+ import { TLAssetId, TLShapeId } from '@tldraw/tlschema'
2
2
  import { VecLike } from '../../primitives/Vec'
3
3
  import { TLContent } from './clipboard-types'
4
4
 
@@ -52,7 +52,15 @@ export interface TLTextExternalContent extends TLBaseExternalContent {
52
52
  export interface TLFilesExternalContent extends TLBaseExternalContent {
53
53
  type: 'files'
54
54
  files: File[]
55
- ignoreParent: boolean
55
+ ignoreParent?: boolean
56
+ }
57
+
58
+ /** @public */
59
+ export interface TLFileReplaceExternalContent extends TLBaseExternalContent {
60
+ type: 'file-replace'
61
+ file: File
62
+ shapeId: TLShapeId
63
+ isImage: boolean
56
64
  }
57
65
 
58
66
  /** @public */
@@ -90,6 +98,7 @@ export interface TLExcalidrawExternalContent extends TLBaseExternalContent {
90
98
  export type TLExternalContent<EmbedDefinition> =
91
99
  | TLTextExternalContent
92
100
  | TLFilesExternalContent
101
+ | TLFileReplaceExternalContent
93
102
  | TLUrlExternalContent
94
103
  | TLSvgTextExternalContent
95
104
  | TLEmbedExternalContent<EmbedDefinition>
@@ -21,7 +21,7 @@ import { flushSync } from 'react-dom'
21
21
  import { ErrorBoundary } from '../components/ErrorBoundary'
22
22
  import { InnerShape, InnerShapeBackground } from '../components/Shape'
23
23
  import { Editor, TLRenderingShape } from '../editor/Editor'
24
- import { TLFontFace } from '../editor/managers/FontManager'
24
+ import { TLFontFace } from '../editor/managers/FontManager/FontManager'
25
25
  import { ShapeUtil } from '../editor/shapes/ShapeUtil'
26
26
  import {
27
27
  SvgExportContext,
@@ -137,7 +137,6 @@ export function useCanvasEvents() {
137
137
  type: 'files',
138
138
  files,
139
139
  point: editor.screenToPage({ x: e.clientX, y: e.clientY }),
140
- ignoreParent: false,
141
140
  })
142
141
  return
143
142
  }
@@ -2,9 +2,15 @@ import { Box } from './Box'
2
2
  import { Vec } from './Vec'
3
3
 
4
4
  describe('Box', () => {
5
+ let box: Box
6
+
7
+ beforeEach(() => {
8
+ box = new Box(10, 20, 100, 200)
9
+ })
10
+
5
11
  it('Creates a box', () => {
6
- const mat3 = new Box(0, 0, 100, 100)
7
- expect(mat3).toMatchObject({
12
+ const newBox = new Box(0, 0, 100, 100)
13
+ expect(newBox).toMatchObject({
8
14
  x: 0,
9
15
  y: 0,
10
16
  w: 100,
@@ -12,13 +18,588 @@ describe('Box', () => {
12
18
  })
13
19
  })
14
20
 
15
- it('can have the point set with a Vec', () => {
16
- const box = new Box(0, 0, 100, 100)
21
+ describe('Box.point', () => {
22
+ it('gets the point as a Vec', () => {
23
+ expect(box.point).toEqual(new Vec(10, 20))
24
+ })
25
+
26
+ it('sets the point with a Vec', () => {
27
+ box.point = new Vec(50, 60)
28
+ expect(box.x).toBe(50)
29
+ expect(box.y).toBe(60)
30
+ })
31
+ })
32
+
33
+ describe('Box.minX', () => {
34
+ it('gets the minimum X value', () => {
35
+ expect(box.minX).toBe(10)
36
+ })
37
+
38
+ it('sets the minimum X value', () => {
39
+ box.minX = 30
40
+ expect(box.x).toBe(30)
41
+ })
42
+ })
43
+
44
+ describe('Box.left', () => {
45
+ it('gets the left edge', () => {
46
+ expect(box.left).toBe(10)
47
+ })
48
+ })
49
+
50
+ describe('Box.midX', () => {
51
+ it('gets the middle X value', () => {
52
+ expect(box.midX).toBe(60) // 10 + 100/2
53
+ })
54
+ })
55
+
56
+ describe('Box.maxX', () => {
57
+ it('gets the maximum X value', () => {
58
+ expect(box.maxX).toBe(110) // 10 + 100
59
+ })
60
+ })
61
+
62
+ describe('Box.right', () => {
63
+ it('gets the right edge', () => {
64
+ expect(box.right).toBe(110) // 10 + 100
65
+ })
66
+ })
67
+
68
+ describe('Box.minY', () => {
69
+ it('gets the minimum Y value', () => {
70
+ expect(box.minY).toBe(20)
71
+ })
72
+
73
+ it('sets the minimum Y value', () => {
74
+ box.minY = 40
75
+ expect(box.y).toBe(40)
76
+ })
77
+ })
78
+
79
+ describe('Box.top', () => {
80
+ it('gets the top edge', () => {
81
+ expect(box.top).toBe(20)
82
+ })
83
+ })
84
+
85
+ describe('Box.midY', () => {
86
+ it('gets the middle Y value', () => {
87
+ expect(box.midY).toBe(120) // 20 + 200/2
88
+ })
89
+ })
90
+
91
+ describe('Box.maxY', () => {
92
+ it('gets the maximum Y value', () => {
93
+ expect(box.maxY).toBe(220) // 20 + 200
94
+ })
95
+ })
96
+
97
+ describe('Box.bottom', () => {
98
+ it('gets the bottom edge', () => {
99
+ expect(box.bottom).toBe(220) // 20 + 200
100
+ })
101
+ })
102
+
103
+ describe('Box.width', () => {
104
+ it('gets the width', () => {
105
+ expect(box.width).toBe(100)
106
+ })
107
+
108
+ it('sets the width', () => {
109
+ box.width = 150
110
+ expect(box.w).toBe(150)
111
+ })
112
+ })
113
+
114
+ describe('Box.height', () => {
115
+ it('gets the height', () => {
116
+ expect(box.height).toBe(200)
117
+ })
118
+
119
+ it('sets the height', () => {
120
+ box.height = 250
121
+ expect(box.h).toBe(250)
122
+ })
123
+ })
124
+
125
+ describe('Box.aspectRatio', () => {
126
+ it('gets the aspect ratio', () => {
127
+ expect(box.aspectRatio).toBe(0.5) // 100/200
128
+ })
129
+ })
130
+
131
+ describe('Box.center', () => {
132
+ it('gets the center point', () => {
133
+ expect(box.center).toEqual(new Vec(60, 120))
134
+ })
135
+
136
+ it('sets the center point', () => {
137
+ box.center = new Vec(100, 150)
138
+ expect(box.x).toBe(50) // 100 - 100/2
139
+ expect(box.y).toBe(50) // 150 - 200/2
140
+ })
141
+ })
142
+
143
+ describe('Box.corners', () => {
144
+ it('gets the corners', () => {
145
+ const corners = box.corners
146
+ expect(corners).toEqual([
147
+ new Vec(10, 20), // top-left
148
+ new Vec(110, 20), // top-right (fixed from buggy bottom-right)
149
+ new Vec(110, 220), // bottom-right
150
+ new Vec(10, 220), // bottom-left
151
+ ])
152
+ })
153
+ })
154
+
155
+ describe('Box.cornersAndCenter', () => {
156
+ it('gets the corners and center', () => {
157
+ const cornersAndCenter = box.cornersAndCenter
158
+ expect(cornersAndCenter).toEqual([
159
+ new Vec(10, 20), // top-left
160
+ new Vec(110, 20), // top-right (fixed from buggy bottom-right)
161
+ new Vec(110, 220), // bottom-right
162
+ new Vec(10, 220), // bottom-left
163
+ new Vec(60, 120), // center
164
+ ])
165
+ })
166
+ })
167
+
168
+ describe('Box.sides', () => {
169
+ it('gets the sides as pairs of vectors', () => {
170
+ const sides = box.sides
171
+ const corners = box.corners
172
+ expect(sides).toEqual([
173
+ [corners[0], corners[1]],
174
+ [corners[1], corners[2]],
175
+ [corners[2], corners[3]],
176
+ [corners[3], corners[0]],
177
+ ])
178
+ })
179
+ })
180
+
181
+ describe('Box.size', () => {
182
+ it('gets the size as a Vec', () => {
183
+ expect(box.size).toEqual(new Vec(100, 200))
184
+ })
185
+ })
186
+
187
+ describe('Box.toFixed', () => {
188
+ it('applies precision to all values', () => {
189
+ const impreciseBox = new Box(10.123456789, 20.987654321, 100.555555, 200.777777)
190
+ const result = impreciseBox.toFixed()
191
+ expect(result.x).toBeCloseTo(10.123456789, 6)
192
+ expect(result.y).toBeCloseTo(20.987654321, 6)
193
+ expect(result.w).toBeCloseTo(100.555555, 6)
194
+ expect(result.h).toBeCloseTo(200.777777, 6)
195
+ expect(result).toBe(impreciseBox) // returns self
196
+ })
197
+ })
198
+
199
+ describe('Box.setTo', () => {
200
+ it('copies values from another box', () => {
201
+ const otherBox = new Box(50, 60, 150, 250)
202
+ const result = box.setTo(otherBox)
203
+ expect(box.x).toBe(50)
204
+ expect(box.y).toBe(60)
205
+ expect(box.w).toBe(150)
206
+ expect(box.h).toBe(250)
207
+ expect(result).toBe(box) // returns self
208
+ })
209
+ })
210
+
211
+ describe('Box.set', () => {
212
+ it('sets all values', () => {
213
+ const result = box.set(30, 40, 120, 180)
214
+ expect(box.x).toBe(30)
215
+ expect(box.y).toBe(40)
216
+ expect(box.w).toBe(120)
217
+ expect(box.h).toBe(180)
218
+ expect(result).toBe(box) // returns self
219
+ })
220
+
221
+ it('uses default values when no parameters provided', () => {
222
+ const result = box.set()
223
+ expect(box.x).toBe(0)
224
+ expect(box.y).toBe(0)
225
+ expect(box.w).toBe(0)
226
+ expect(box.h).toBe(0)
227
+ expect(result).toBe(box) // returns self
228
+ })
229
+ })
230
+
231
+ describe('Box.expand', () => {
232
+ it('expands to include another box', () => {
233
+ const otherBox = new Box(5, 15, 120, 240) // overlaps and extends
234
+ const result = box.expand(otherBox)
235
+ expect(box.x).toBe(5) // min of 10 and 5
236
+ expect(box.y).toBe(15) // min of 20 and 15
237
+ expect(box.w).toBe(120) // max(110, 125) - min(10, 5) = 125 - 5
238
+ expect(box.h).toBe(240) // max(220, 255) - min(20, 15) = 255 - 15
239
+ expect(result).toBe(box) // returns self
240
+ })
241
+ })
242
+
243
+ describe('Box.expandBy', () => {
244
+ it('expands by a given amount', () => {
245
+ const result = box.expandBy(10)
246
+ expect(box.x).toBe(0) // 10 - 10
247
+ expect(box.y).toBe(10) // 20 - 10
248
+ expect(box.w).toBe(120) // 100 + 10*2
249
+ expect(box.h).toBe(220) // 200 + 10*2
250
+ expect(result).toBe(box) // returns self
251
+ })
252
+ })
253
+
254
+ describe('Box.scale', () => {
255
+ it('scales the box by a factor', () => {
256
+ const result = box.scale(2)
257
+ expect(box.x).toBe(5) // 10/2
258
+ expect(box.y).toBe(10) // 20/2
259
+ expect(box.w).toBe(50) // 100/2
260
+ expect(box.h).toBe(100) // 200/2
261
+ expect(result).toBe(box) // returns self
262
+ })
263
+ })
264
+
265
+ describe('Box.clone', () => {
266
+ it('creates a copy of the box', () => {
267
+ const cloned = box.clone()
268
+ expect(cloned).toEqual(box)
269
+ expect(cloned).not.toBe(box) // different object
270
+ })
271
+ })
272
+
273
+ describe('Box.translate', () => {
274
+ it('translates the box by a delta', () => {
275
+ const delta = new Vec(5, 10)
276
+ const result = box.translate(delta)
277
+ expect(box.x).toBe(15) // 10 + 5
278
+ expect(box.y).toBe(30) // 20 + 10
279
+ expect(box.w).toBe(100) // unchanged
280
+ expect(box.h).toBe(200) // unchanged
281
+ expect(result).toBe(box) // returns self
282
+ })
283
+ })
284
+
285
+ describe('Box.snapToGrid', () => {
286
+ it('snaps the box to a grid', () => {
287
+ const testBox = new Box(12, 18, 95, 185)
288
+ testBox.snapToGrid(10)
289
+ expect(testBox.minX).toBe(10) // rounded to nearest 10
290
+ expect(testBox.minY).toBe(20) // rounded to nearest 10
291
+ expect(testBox.width).toBe(100) // snapped width
292
+ expect(testBox.height).toBe(180) // snapped height
293
+ })
294
+
295
+ it('ensures minimum size of 1', () => {
296
+ const testBox = new Box(0, 0, 3, 3)
297
+ testBox.snapToGrid(10)
298
+ expect(testBox.width).toBe(1) // minimum 1
299
+ expect(testBox.height).toBe(1) // minimum 1
300
+ })
301
+ })
302
+
303
+ describe('Box.collides', () => {
304
+ it('returns true when boxes collide', () => {
305
+ const otherBox = new Box(50, 100, 100, 100) // overlaps
306
+ expect(box.collides(otherBox)).toBe(true)
307
+ })
308
+
309
+ it('returns false when boxes do not collide', () => {
310
+ const otherBox = new Box(200, 300, 100, 100) // no overlap
311
+ expect(box.collides(otherBox)).toBe(false)
312
+ })
313
+ })
314
+
315
+ describe('Box.contains', () => {
316
+ it('returns true when this box contains the other', () => {
317
+ const otherBox = new Box(20, 30, 50, 100) // inside box
318
+ expect(box.contains(otherBox)).toBe(true)
319
+ })
320
+
321
+ it('returns false when this box does not contain the other', () => {
322
+ const otherBox = new Box(5, 5, 200, 300) // larger than box
323
+ expect(box.contains(otherBox)).toBe(false)
324
+ })
325
+ })
326
+
327
+ describe('Box.includes', () => {
328
+ it('returns true when boxes collide or contain', () => {
329
+ const collidingBox = new Box(50, 100, 100, 100)
330
+ const containedBox = new Box(20, 30, 50, 100)
331
+ expect(box.includes(collidingBox)).toBe(true)
332
+ expect(box.includes(containedBox)).toBe(true)
333
+ })
334
+
335
+ it('returns false when boxes do not interact', () => {
336
+ const separateBox = new Box(200, 300, 100, 100)
337
+ expect(box.includes(separateBox)).toBe(false)
338
+ })
339
+ })
340
+
341
+ describe('Box.containsPoint', () => {
342
+ it('returns true when point is inside', () => {
343
+ const point = new Vec(50, 100)
344
+ expect(box.containsPoint(point)).toBe(true)
345
+ })
17
346
 
18
- expect(box).toMatchObject({ x: 0, y: 0 })
347
+ it('returns false when point is outside', () => {
348
+ const point = new Vec(150, 300)
349
+ expect(box.containsPoint(point)).toBe(false)
350
+ })
19
351
 
20
- box.point = new Vec(19, 23)
352
+ it('respects margin', () => {
353
+ const point = new Vec(5, 15) // just outside
354
+ expect(box.containsPoint(point)).toBe(false)
355
+ expect(box.containsPoint(point, 10)).toBe(true) // with margin
356
+ })
357
+ })
358
+
359
+ describe('Box.getHandlePoint', () => {
360
+ it('returns correct points for corners', () => {
361
+ expect(box.getHandlePoint('top_left')).toEqual(new Vec(10, 20))
362
+ expect(box.getHandlePoint('top_right')).toEqual(new Vec(110, 20))
363
+ expect(box.getHandlePoint('bottom_left')).toEqual(new Vec(10, 220))
364
+ expect(box.getHandlePoint('bottom_right')).toEqual(new Vec(110, 220))
365
+ })
366
+
367
+ it('returns correct points for edges', () => {
368
+ expect(box.getHandlePoint('top')).toEqual(new Vec(60, 20))
369
+ expect(box.getHandlePoint('right')).toEqual(new Vec(110, 120))
370
+ expect(box.getHandlePoint('bottom')).toEqual(new Vec(60, 220))
371
+ expect(box.getHandlePoint('left')).toEqual(new Vec(10, 120))
372
+ })
373
+ })
374
+
375
+ describe('Box.toJson', () => {
376
+ it('returns box model object', () => {
377
+ expect(box.toJson()).toEqual({
378
+ x: 10,
379
+ y: 20,
380
+ w: 100,
381
+ h: 200,
382
+ })
383
+ })
384
+ })
385
+
386
+ describe('Box.resize', () => {
387
+ it('resizes from top-left handle', () => {
388
+ box.resize('top_left', 10, 20)
389
+ expect(box.minX).toBe(20) // moved right
390
+ expect(box.minY).toBe(40) // moved down
391
+ expect(box.width).toBe(90) // reduced width
392
+ expect(box.height).toBe(180) // reduced height
393
+ })
394
+
395
+ it('resizes from bottom-right handle', () => {
396
+ box.resize('bottom_right', 10, 20)
397
+ expect(box.minX).toBe(10) // unchanged
398
+ expect(box.minY).toBe(20) // unchanged
399
+ expect(box.width).toBe(110) // increased width
400
+ expect(box.height).toBe(220) // increased height
401
+ })
402
+ })
403
+
404
+ describe('Box.union', () => {
405
+ it('creates union with another box', () => {
406
+ const otherBox = { x: 5, y: 15, w: 120, h: 240 }
407
+ const result = box.union(otherBox)
408
+ expect(box.x).toBe(5)
409
+ expect(box.y).toBe(15)
410
+ expect(box.width).toBe(120) // max(110, 125) - min(10, 5)
411
+ expect(box.height).toBe(240) // max(220, 255) - min(20, 15)
412
+ expect(result).toBe(box) // returns self
413
+ })
414
+ })
415
+
416
+ describe('Box.equals', () => {
417
+ it('returns true for equal boxes', () => {
418
+ const otherBox = new Box(10, 20, 100, 200)
419
+ expect(box.equals(otherBox)).toBe(true)
420
+ })
421
+
422
+ it('returns false for different boxes', () => {
423
+ const otherBox = new Box(10, 20, 100, 201)
424
+ expect(box.equals(otherBox)).toBe(false)
425
+ })
426
+ })
427
+
428
+ describe('Box.zeroFix', () => {
429
+ it('ensures minimum size of 1', () => {
430
+ const zeroBox = new Box(0, 0, 0, 0)
431
+ const result = zeroBox.zeroFix()
432
+ expect(zeroBox.w).toBe(1)
433
+ expect(zeroBox.h).toBe(1)
434
+ expect(result).toBe(zeroBox) // returns self
435
+ })
436
+ })
437
+
438
+ // Static method tests
439
+ describe('Box.From', () => {
440
+ it('creates box from box model', () => {
441
+ const boxModel = { x: 5, y: 10, w: 50, h: 100 }
442
+ const result = Box.From(boxModel)
443
+ expect(result).toEqual(new Box(5, 10, 50, 100))
444
+ })
445
+ })
446
+
447
+ describe('Box.FromCenter', () => {
448
+ it('creates box from center and size', () => {
449
+ const center = new Vec(50, 100)
450
+ const size = new Vec(20, 40)
451
+ const result = Box.FromCenter(center, size)
452
+ expect(result).toEqual(new Box(40, 80, 20, 40))
453
+ })
454
+ })
21
455
 
22
- expect(box).toMatchObject({ x: 19, y: 23 })
456
+ describe('Box.FromPoints', () => {
457
+ it('creates box from array of points', () => {
458
+ const points = [new Vec(10, 20), new Vec(110, 220), new Vec(50, 100)]
459
+ const result = Box.FromPoints(points)
460
+ expect(result).toEqual(new Box(10, 20, 100, 200))
461
+ })
462
+
463
+ it('returns empty box for empty array', () => {
464
+ const result = Box.FromPoints([])
465
+ expect(result).toEqual(new Box())
466
+ })
467
+ })
468
+
469
+ describe('Box.Expand', () => {
470
+ it('creates expanded box from two boxes', () => {
471
+ const boxA = new Box(10, 20, 100, 200)
472
+ const boxB = new Box(5, 15, 120, 240)
473
+ const result = Box.Expand(boxA, boxB)
474
+ expect(result).toEqual(new Box(5, 15, 120, 240))
475
+ })
476
+ })
477
+
478
+ describe('Box.ExpandBy', () => {
479
+ it('creates expanded box by amount', () => {
480
+ const result = Box.ExpandBy(box, 10)
481
+ expect(result).toEqual(new Box(0, 10, 120, 220))
482
+ })
483
+ })
484
+
485
+ describe('Box.Collides', () => {
486
+ it('returns true when boxes collide', () => {
487
+ const boxA = new Box(0, 0, 50, 50)
488
+ const boxB = new Box(25, 25, 50, 50)
489
+ expect(Box.Collides(boxA, boxB)).toBe(true)
490
+ })
491
+
492
+ it('returns false when boxes do not collide', () => {
493
+ const boxA = new Box(0, 0, 50, 50)
494
+ const boxB = new Box(100, 100, 50, 50)
495
+ expect(Box.Collides(boxA, boxB)).toBe(false)
496
+ })
497
+ })
498
+
499
+ describe('Box.Contains', () => {
500
+ it('returns true when first box contains second', () => {
501
+ const boxA = new Box(0, 0, 100, 100)
502
+ const boxB = new Box(10, 10, 50, 50)
503
+ expect(Box.Contains(boxA, boxB)).toBe(true)
504
+ })
505
+
506
+ it('returns false when first box does not contain second', () => {
507
+ const boxA = new Box(0, 0, 50, 50)
508
+ const boxB = new Box(10, 10, 100, 100)
509
+ expect(Box.Contains(boxA, boxB)).toBe(false)
510
+ })
511
+ })
512
+
513
+ describe('Box.Includes', () => {
514
+ it('returns true when boxes collide or contain', () => {
515
+ const boxA = new Box(0, 0, 50, 50)
516
+ const boxB = new Box(25, 25, 50, 50) // colliding
517
+ const boxC = new Box(10, 10, 20, 20) // contained
518
+ expect(Box.Includes(boxA, boxB)).toBe(true)
519
+ expect(Box.Includes(boxA, boxC)).toBe(true)
520
+ })
521
+
522
+ it('returns false when boxes do not interact', () => {
523
+ const boxA = new Box(0, 0, 50, 50)
524
+ const boxB = new Box(100, 100, 50, 50)
525
+ expect(Box.Includes(boxA, boxB)).toBe(false)
526
+ })
527
+ })
528
+
529
+ describe('Box.ContainsPoint', () => {
530
+ it('returns true when point is inside box', () => {
531
+ const testBox = new Box(0, 0, 100, 100)
532
+ const point = new Vec(50, 50)
533
+ expect(Box.ContainsPoint(testBox, point)).toBe(true)
534
+ })
535
+
536
+ it('returns false when point is outside box', () => {
537
+ const testBox = new Box(0, 0, 100, 100)
538
+ const point = new Vec(150, 150)
539
+ expect(Box.ContainsPoint(testBox, point)).toBe(false)
540
+ })
541
+
542
+ it('respects margin parameter', () => {
543
+ const testBox = new Box(0, 0, 100, 100)
544
+ const point = new Vec(-5, -5)
545
+ expect(Box.ContainsPoint(testBox, point)).toBe(false)
546
+ expect(Box.ContainsPoint(testBox, point, 10)).toBe(true)
547
+ })
548
+ })
549
+
550
+ describe('Box.Common', () => {
551
+ it('creates bounding box of multiple boxes', () => {
552
+ const boxes = [new Box(0, 0, 50, 50), new Box(75, 75, 50, 50), new Box(25, 25, 50, 50)]
553
+ const result = Box.Common(boxes)
554
+ expect(result).toEqual(new Box(0, 0, 125, 125))
555
+ })
556
+ })
557
+
558
+ describe('Box.Sides', () => {
559
+ it('returns sides as corner pairs', () => {
560
+ const testBox = new Box(0, 0, 100, 100)
561
+ const sides = Box.Sides(testBox)
562
+ expect(sides).toHaveLength(4)
563
+ expect(sides[0]).toEqual([new Vec(0, 0), new Vec(100, 0)])
564
+ })
565
+ })
566
+
567
+ describe('Box.Resize', () => {
568
+ it('resizes box and returns scaling info', () => {
569
+ const testBox = new Box(0, 0, 100, 100)
570
+ const result = Box.Resize(testBox, 'bottom_right', 50, 50)
571
+ expect(result.box).toEqual(new Box(0, 0, 150, 150))
572
+ expect(result.scaleX).toBe(1.5)
573
+ expect(result.scaleY).toBe(1.5)
574
+ })
575
+
576
+ it('handles aspect ratio locking', () => {
577
+ const testBox = new Box(0, 0, 100, 100)
578
+ const result = Box.Resize(testBox, 'bottom_right', 50, 25, true)
579
+ expect(result.box.width).toBeCloseTo(result.box.height) // maintains aspect ratio
580
+ })
581
+ })
582
+
583
+ describe('Box.Equals', () => {
584
+ it('returns true for equal boxes', () => {
585
+ const boxA = new Box(10, 20, 100, 200)
586
+ const boxB = new Box(10, 20, 100, 200)
587
+ expect(Box.Equals(boxA, boxB)).toBe(true)
588
+ })
589
+
590
+ it('returns false for different boxes', () => {
591
+ const boxA = new Box(10, 20, 100, 200)
592
+ const boxB = new Box(10, 20, 100, 201)
593
+ expect(Box.Equals(boxA, boxB)).toBe(false)
594
+ })
595
+ })
596
+
597
+ describe('Box.ZeroFix', () => {
598
+ it('creates new box with minimum size of 1', () => {
599
+ const zeroBox = new Box(0, 0, 0, 0)
600
+ const result = Box.ZeroFix(zeroBox)
601
+ expect(result).toEqual(new Box(0, 0, 1, 1))
602
+ expect(result).not.toBe(zeroBox) // different object
603
+ })
23
604
  })
24
605
  })