fabric 7.0.0 → 7.2.0

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 (323) hide show
  1. package/.husky/pre-commit +1 -0
  2. package/CHANGELOG.md +21 -0
  3. package/dist/extensions/cropping_controls/croppingControls.d.ts +16 -0
  4. package/dist/extensions/cropping_controls/croppingControls.d.ts.map +1 -0
  5. package/dist/extensions/cropping_controls/croppingHandlers.d.ts +39 -0
  6. package/dist/extensions/cropping_controls/croppingHandlers.d.ts.map +1 -0
  7. package/dist/extensions/cropping_controls/enterCropMode.d.ts +7 -0
  8. package/dist/extensions/cropping_controls/enterCropMode.d.ts.map +1 -0
  9. package/dist/extensions/cropping_controls/renderCornerControl.d.ts +14 -0
  10. package/dist/extensions/cropping_controls/renderCornerControl.d.ts.map +1 -0
  11. package/dist/extensions/index.d.ts +3 -0
  12. package/dist/extensions/index.d.ts.map +1 -1
  13. package/dist/fabric.d.ts +1 -0
  14. package/dist/fabric.d.ts.map +1 -1
  15. package/dist/index.js +628 -537
  16. package/dist/index.js.map +1 -1
  17. package/dist/index.min.js +1 -1
  18. package/dist/index.min.js.map +1 -1
  19. package/dist/index.min.mjs +1 -1
  20. package/dist/index.min.mjs.map +1 -1
  21. package/dist/index.mjs +628 -537
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/index.node.cjs +628 -537
  24. package/dist/index.node.cjs.map +1 -1
  25. package/dist/index.node.mjs +628 -537
  26. package/dist/index.node.mjs.map +1 -1
  27. package/dist/package.json.min.mjs +1 -1
  28. package/dist/package.json.mjs +1 -1
  29. package/dist/src/EventTypeDefs.d.ts +5 -0
  30. package/dist/src/EventTypeDefs.d.ts.map +1 -1
  31. package/dist/src/Pattern/Pattern.d.ts.map +1 -1
  32. package/dist/src/Pattern/Pattern.min.mjs +1 -1
  33. package/dist/src/Pattern/Pattern.min.mjs.map +1 -1
  34. package/dist/src/Pattern/Pattern.mjs +2 -1
  35. package/dist/src/Pattern/Pattern.mjs.map +1 -1
  36. package/dist/src/Shadow.d.ts +1 -1
  37. package/dist/src/Shadow.d.ts.map +1 -1
  38. package/dist/src/Shadow.min.mjs +1 -1
  39. package/dist/src/Shadow.min.mjs.map +1 -1
  40. package/dist/src/Shadow.mjs +5 -4
  41. package/dist/src/Shadow.mjs.map +1 -1
  42. package/dist/src/canvas/CanvasOptions.d.ts.map +1 -1
  43. package/dist/src/canvas/CanvasOptions.min.mjs.map +1 -1
  44. package/dist/src/canvas/CanvasOptions.mjs.map +1 -1
  45. package/dist/src/canvas/SelectableCanvas.d.ts +2 -0
  46. package/dist/src/canvas/SelectableCanvas.d.ts.map +1 -1
  47. package/dist/src/canvas/SelectableCanvas.min.mjs +1 -1
  48. package/dist/src/canvas/SelectableCanvas.min.mjs.map +1 -1
  49. package/dist/src/canvas/SelectableCanvas.mjs +33 -13
  50. package/dist/src/canvas/SelectableCanvas.mjs.map +1 -1
  51. package/dist/src/canvas/StaticCanvas.d.ts.map +1 -1
  52. package/dist/src/canvas/StaticCanvas.min.mjs +1 -1
  53. package/dist/src/canvas/StaticCanvas.min.mjs.map +1 -1
  54. package/dist/src/canvas/StaticCanvas.mjs +3 -1
  55. package/dist/src/canvas/StaticCanvas.mjs.map +1 -1
  56. package/dist/src/canvas/StaticCanvasOptions.d.ts.map +1 -1
  57. package/dist/src/canvas/StaticCanvasOptions.min.mjs.map +1 -1
  58. package/dist/src/canvas/StaticCanvasOptions.mjs.map +1 -1
  59. package/dist/src/constants.d.ts +1 -0
  60. package/dist/src/constants.d.ts.map +1 -1
  61. package/dist/src/constants.min.mjs.map +1 -1
  62. package/dist/src/constants.mjs.map +1 -1
  63. package/dist/src/controls/Control.d.ts +22 -1
  64. package/dist/src/controls/Control.d.ts.map +1 -1
  65. package/dist/src/controls/Control.min.mjs +1 -1
  66. package/dist/src/controls/Control.min.mjs.map +1 -1
  67. package/dist/src/controls/Control.mjs +45 -1
  68. package/dist/src/controls/Control.mjs.map +1 -1
  69. package/dist/src/controls/changeWidth.d.ts +22 -0
  70. package/dist/src/controls/changeWidth.d.ts.map +1 -1
  71. package/dist/src/controls/changeWidth.min.mjs +1 -1
  72. package/dist/src/controls/changeWidth.min.mjs.map +1 -1
  73. package/dist/src/controls/changeWidth.mjs +46 -18
  74. package/dist/src/controls/changeWidth.mjs.map +1 -1
  75. package/dist/src/controls/controlRendering.d.ts.map +1 -1
  76. package/dist/src/controls/controlRendering.min.mjs +1 -1
  77. package/dist/src/controls/controlRendering.min.mjs.map +1 -1
  78. package/dist/src/controls/controlRendering.mjs +18 -34
  79. package/dist/src/controls/controlRendering.mjs.map +1 -1
  80. package/dist/src/controls/index.d.ts +2 -1
  81. package/dist/src/controls/index.d.ts.map +1 -1
  82. package/dist/src/controls/index.min.mjs +1 -1
  83. package/dist/src/controls/index.mjs +1 -1
  84. package/dist/src/gradient/Gradient.d.ts.map +1 -1
  85. package/dist/src/gradient/Gradient.min.mjs +1 -1
  86. package/dist/src/gradient/Gradient.min.mjs.map +1 -1
  87. package/dist/src/gradient/Gradient.mjs +19 -6
  88. package/dist/src/gradient/Gradient.mjs.map +1 -1
  89. package/dist/src/shapes/Circle.d.ts.map +1 -1
  90. package/dist/src/shapes/Circle.min.mjs +1 -1
  91. package/dist/src/shapes/Circle.min.mjs.map +1 -1
  92. package/dist/src/shapes/Circle.mjs +10 -7
  93. package/dist/src/shapes/Circle.mjs.map +1 -1
  94. package/dist/src/shapes/Ellipse.d.ts.map +1 -1
  95. package/dist/src/shapes/Ellipse.min.mjs +1 -1
  96. package/dist/src/shapes/Ellipse.min.mjs.map +1 -1
  97. package/dist/src/shapes/Ellipse.mjs +2 -1
  98. package/dist/src/shapes/Ellipse.mjs.map +1 -1
  99. package/dist/src/shapes/Group.d.ts.map +1 -1
  100. package/dist/src/shapes/Group.min.mjs +1 -1
  101. package/dist/src/shapes/Group.min.mjs.map +1 -1
  102. package/dist/src/shapes/Group.mjs +2 -1
  103. package/dist/src/shapes/Group.mjs.map +1 -1
  104. package/dist/src/shapes/IText/IText.d.ts.map +1 -1
  105. package/dist/src/shapes/IText/IText.min.mjs.map +1 -1
  106. package/dist/src/shapes/IText/IText.mjs.map +1 -1
  107. package/dist/src/shapes/Image.d.ts +1 -1
  108. package/dist/src/shapes/Image.d.ts.map +1 -1
  109. package/dist/src/shapes/Image.min.mjs +1 -1
  110. package/dist/src/shapes/Image.min.mjs.map +1 -1
  111. package/dist/src/shapes/Image.mjs +4 -3
  112. package/dist/src/shapes/Image.mjs.map +1 -1
  113. package/dist/src/shapes/Line.d.ts.map +1 -1
  114. package/dist/src/shapes/Line.min.mjs +1 -1
  115. package/dist/src/shapes/Line.min.mjs.map +1 -1
  116. package/dist/src/shapes/Line.mjs +6 -10
  117. package/dist/src/shapes/Line.mjs.map +1 -1
  118. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.d.ts.map +1 -1
  119. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.min.mjs +1 -1
  120. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.min.mjs.map +1 -1
  121. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.mjs +5 -4
  122. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.mjs.map +1 -1
  123. package/dist/src/shapes/Object/InteractiveObject.d.ts.map +1 -1
  124. package/dist/src/shapes/Object/InteractiveObject.min.mjs.map +1 -1
  125. package/dist/src/shapes/Object/InteractiveObject.mjs.map +1 -1
  126. package/dist/src/shapes/Object/Object.d.ts.map +1 -1
  127. package/dist/src/shapes/Object/Object.min.mjs +1 -1
  128. package/dist/src/shapes/Object/Object.min.mjs.map +1 -1
  129. package/dist/src/shapes/Object/Object.mjs +3 -0
  130. package/dist/src/shapes/Object/Object.mjs.map +1 -1
  131. package/dist/src/shapes/Object/ObjectGeometry.min.mjs +1 -1
  132. package/dist/src/shapes/Object/ObjectGeometry.min.mjs.map +1 -1
  133. package/dist/src/shapes/Object/ObjectGeometry.mjs +1 -1
  134. package/dist/src/shapes/Object/ObjectGeometry.mjs.map +1 -1
  135. package/dist/src/shapes/Object/types/FabricObjectProps.d.ts.map +1 -1
  136. package/dist/src/shapes/Object/types/ObjectProps.d.ts.map +1 -1
  137. package/dist/src/shapes/Path.d.ts.map +1 -1
  138. package/dist/src/shapes/Path.min.mjs.map +1 -1
  139. package/dist/src/shapes/Path.mjs +1 -2
  140. package/dist/src/shapes/Path.mjs.map +1 -1
  141. package/dist/src/shapes/Polyline.d.ts.map +1 -1
  142. package/dist/src/shapes/Polyline.min.mjs +1 -1
  143. package/dist/src/shapes/Polyline.min.mjs.map +1 -1
  144. package/dist/src/shapes/Polyline.mjs +10 -6
  145. package/dist/src/shapes/Polyline.mjs.map +1 -1
  146. package/dist/src/shapes/Rect.d.ts.map +1 -1
  147. package/dist/src/shapes/Rect.min.mjs +1 -1
  148. package/dist/src/shapes/Rect.min.mjs.map +1 -1
  149. package/dist/src/shapes/Rect.mjs +2 -1
  150. package/dist/src/shapes/Rect.mjs.map +1 -1
  151. package/dist/src/shapes/Text/StyledText.d.ts.map +1 -1
  152. package/dist/src/shapes/Text/StyledText.min.mjs.map +1 -1
  153. package/dist/src/shapes/Text/StyledText.mjs +0 -3
  154. package/dist/src/shapes/Text/StyledText.mjs.map +1 -1
  155. package/dist/src/shapes/Text/Text.d.ts.map +1 -1
  156. package/dist/src/shapes/Text/Text.min.mjs.map +1 -1
  157. package/dist/src/shapes/Text/Text.mjs.map +1 -1
  158. package/dist/src/shapes/Text/TextSVGExportMixin.d.ts.map +1 -1
  159. package/dist/src/shapes/Text/TextSVGExportMixin.min.mjs +1 -1
  160. package/dist/src/shapes/Text/TextSVGExportMixin.min.mjs.map +1 -1
  161. package/dist/src/shapes/Text/TextSVGExportMixin.mjs +5 -6
  162. package/dist/src/shapes/Text/TextSVGExportMixin.mjs.map +1 -1
  163. package/dist/src/shapes/Textbox.d.ts.map +1 -1
  164. package/dist/src/shapes/Textbox.min.mjs.map +1 -1
  165. package/dist/src/shapes/Textbox.mjs.map +1 -1
  166. package/dist/src/shapes/Triangle.d.ts.map +1 -1
  167. package/dist/src/shapes/Triangle.min.mjs.map +1 -1
  168. package/dist/src/shapes/Triangle.mjs.map +1 -1
  169. package/dist/src/util/lang_string.d.ts +1 -1
  170. package/dist/src/util/lang_string.d.ts.map +1 -1
  171. package/dist/src/util/lang_string.min.mjs +1 -1
  172. package/dist/src/util/lang_string.min.mjs.map +1 -1
  173. package/dist/src/util/lang_string.mjs +1 -1
  174. package/dist/src/util/lang_string.mjs.map +1 -1
  175. package/dist/src/util/misc/svgParsing.d.ts.map +1 -1
  176. package/dist/src/util/misc/svgParsing.min.mjs +1 -1
  177. package/dist/src/util/misc/svgParsing.min.mjs.map +1 -1
  178. package/dist/src/util/misc/svgParsing.mjs +2 -1
  179. package/dist/src/util/misc/svgParsing.mjs.map +1 -1
  180. package/dist-extensions/cropping_controls/croppingControls.mjs +140 -0
  181. package/dist-extensions/cropping_controls/croppingControls.mjs.map +1 -0
  182. package/dist-extensions/cropping_controls/croppingHandlers.mjs +228 -0
  183. package/dist-extensions/cropping_controls/croppingHandlers.mjs.map +1 -0
  184. package/dist-extensions/cropping_controls/enterCropMode.mjs +38 -0
  185. package/dist-extensions/cropping_controls/enterCropMode.mjs.map +1 -0
  186. package/dist-extensions/cropping_controls/renderCornerControl.mjs +45 -0
  187. package/dist-extensions/cropping_controls/renderCornerControl.mjs.map +1 -0
  188. package/dist-extensions/extensions/cropping_controls/croppingControls.d.ts +16 -0
  189. package/dist-extensions/extensions/cropping_controls/croppingControls.d.ts.map +1 -0
  190. package/dist-extensions/extensions/cropping_controls/croppingHandlers.d.ts +39 -0
  191. package/dist-extensions/extensions/cropping_controls/croppingHandlers.d.ts.map +1 -0
  192. package/dist-extensions/extensions/cropping_controls/enterCropMode.d.ts +7 -0
  193. package/dist-extensions/extensions/cropping_controls/enterCropMode.d.ts.map +1 -0
  194. package/dist-extensions/extensions/cropping_controls/renderCornerControl.d.ts +14 -0
  195. package/dist-extensions/extensions/cropping_controls/renderCornerControl.d.ts.map +1 -0
  196. package/dist-extensions/extensions/index.d.ts +3 -0
  197. package/dist-extensions/extensions/index.d.ts.map +1 -1
  198. package/dist-extensions/fabric-extensions.min.js +1 -1
  199. package/dist-extensions/fabric-extensions.min.js.map +1 -1
  200. package/dist-extensions/fabric.d.ts +1 -0
  201. package/dist-extensions/fabric.d.ts.map +1 -1
  202. package/dist-extensions/index.mjs +3 -0
  203. package/dist-extensions/index.mjs.map +1 -1
  204. package/dist-extensions/src/EventTypeDefs.d.ts +5 -0
  205. package/dist-extensions/src/EventTypeDefs.d.ts.map +1 -1
  206. package/dist-extensions/src/Pattern/Pattern.d.ts.map +1 -1
  207. package/dist-extensions/src/Shadow.d.ts +1 -1
  208. package/dist-extensions/src/Shadow.d.ts.map +1 -1
  209. package/dist-extensions/src/canvas/CanvasOptions.d.ts.map +1 -1
  210. package/dist-extensions/src/canvas/SelectableCanvas.d.ts +2 -0
  211. package/dist-extensions/src/canvas/SelectableCanvas.d.ts.map +1 -1
  212. package/dist-extensions/src/canvas/StaticCanvas.d.ts.map +1 -1
  213. package/dist-extensions/src/canvas/StaticCanvasOptions.d.ts.map +1 -1
  214. package/dist-extensions/src/constants.d.ts +1 -0
  215. package/dist-extensions/src/constants.d.ts.map +1 -1
  216. package/dist-extensions/src/controls/Control.d.ts +22 -1
  217. package/dist-extensions/src/controls/Control.d.ts.map +1 -1
  218. package/dist-extensions/src/controls/changeWidth.d.ts +22 -0
  219. package/dist-extensions/src/controls/changeWidth.d.ts.map +1 -1
  220. package/dist-extensions/src/controls/controlRendering.d.ts.map +1 -1
  221. package/dist-extensions/src/controls/index.d.ts +2 -1
  222. package/dist-extensions/src/controls/index.d.ts.map +1 -1
  223. package/dist-extensions/src/gradient/Gradient.d.ts.map +1 -1
  224. package/dist-extensions/src/shapes/Circle.d.ts.map +1 -1
  225. package/dist-extensions/src/shapes/Ellipse.d.ts.map +1 -1
  226. package/dist-extensions/src/shapes/Group.d.ts.map +1 -1
  227. package/dist-extensions/src/shapes/IText/IText.d.ts.map +1 -1
  228. package/dist-extensions/src/shapes/Image.d.ts.map +1 -1
  229. package/dist-extensions/src/shapes/Line.d.ts.map +1 -1
  230. package/dist-extensions/src/shapes/Object/FabricObjectSVGExportMixin.d.ts.map +1 -1
  231. package/dist-extensions/src/shapes/Object/InteractiveObject.d.ts.map +1 -1
  232. package/dist-extensions/src/shapes/Object/Object.d.ts.map +1 -1
  233. package/dist-extensions/src/shapes/Object/types/FabricObjectProps.d.ts.map +1 -1
  234. package/dist-extensions/src/shapes/Object/types/ObjectProps.d.ts.map +1 -1
  235. package/dist-extensions/src/shapes/Path.d.ts +1 -1
  236. package/dist-extensions/src/shapes/Path.d.ts.map +1 -1
  237. package/dist-extensions/src/shapes/Polyline.d.ts.map +1 -1
  238. package/dist-extensions/src/shapes/Rect.d.ts.map +1 -1
  239. package/dist-extensions/src/shapes/Text/StyledText.d.ts.map +1 -1
  240. package/dist-extensions/src/shapes/Text/Text.d.ts.map +1 -1
  241. package/dist-extensions/src/shapes/Text/TextSVGExportMixin.d.ts.map +1 -1
  242. package/dist-extensions/src/shapes/Textbox.d.ts.map +1 -1
  243. package/dist-extensions/src/shapes/Triangle.d.ts.map +1 -1
  244. package/dist-extensions/src/util/lang_string.d.ts +1 -1
  245. package/dist-extensions/src/util/lang_string.d.ts.map +1 -1
  246. package/dist-extensions/src/util/misc/svgParsing.d.ts.map +1 -1
  247. package/eslint.config.mjs +2 -0
  248. package/extensions/cropping_controls/croppingControls.spec.ts +115 -0
  249. package/extensions/cropping_controls/croppingControls.ts +150 -0
  250. package/extensions/cropping_controls/croppingHandlers.spec.ts +579 -0
  251. package/extensions/cropping_controls/croppingHandlers.ts +285 -0
  252. package/extensions/cropping_controls/enterCropMode.ts +30 -0
  253. package/extensions/cropping_controls/renderCornerControl.ts +53 -0
  254. package/extensions/index.ts +9 -0
  255. package/fabric.ts +1 -0
  256. package/package.json +17 -8
  257. package/src/ClassRegistry.spec.ts +18 -19
  258. package/src/EventTypeDefs.ts +15 -11
  259. package/src/Pattern/Pattern.spec.ts +12 -0
  260. package/src/Pattern/Pattern.ts +3 -2
  261. package/src/Shadow.ts +9 -8
  262. package/src/brushes/PencilBrush.spec.ts +11 -11
  263. package/src/canvas/Canvas-dispose.spec.ts +8 -7
  264. package/src/canvas/Canvas.spec.ts +27 -29
  265. package/src/canvas/CanvasOptions.ts +2 -1
  266. package/src/canvas/SelectableCanvas.ts +38 -15
  267. package/src/canvas/StaticCanvas.spec.ts +20 -0
  268. package/src/canvas/StaticCanvas.ts +7 -4
  269. package/src/canvas/StaticCanvasOptions.ts +1 -3
  270. package/src/constants.ts +1 -0
  271. package/src/controls/Control.spec.ts +102 -0
  272. package/src/controls/Control.ts +71 -2
  273. package/src/controls/changeHeight.spec.ts +147 -0
  274. package/src/controls/changeWidth.ts +68 -35
  275. package/src/controls/controlRendering.ts +20 -48
  276. package/src/controls/index.ts +7 -1
  277. package/src/gradient/Gradient.spec.ts +101 -46
  278. package/src/gradient/Gradient.ts +27 -14
  279. package/src/shapes/Circle.spec.ts +10 -39
  280. package/src/shapes/Circle.ts +11 -11
  281. package/src/shapes/Ellipse.spec.ts +8 -37
  282. package/src/shapes/Ellipse.ts +7 -7
  283. package/src/shapes/Group.ts +3 -3
  284. package/src/shapes/IText/IText-click-behavior.spec.ts +36 -49
  285. package/src/shapes/IText/IText.ts +5 -6
  286. package/src/shapes/IText/ITextKeyBehavior.test.ts +0 -1
  287. package/src/shapes/IText/__snapshots__/ITextBehavior.test.ts.snap +6 -6
  288. package/src/shapes/Image.spec.ts +17 -33
  289. package/src/shapes/Image.ts +15 -11
  290. package/src/shapes/Line.spec.ts +4 -30
  291. package/src/shapes/Line.ts +11 -16
  292. package/src/shapes/Object/FabricObjectSVGExportMixin.ts +11 -4
  293. package/src/shapes/Object/InteractiveObject.ts +4 -4
  294. package/src/shapes/Object/Object.ts +6 -5
  295. package/src/shapes/Object/ObjectGeometry.spec.ts +15 -0
  296. package/src/shapes/Object/ObjectGeometry.ts +1 -1
  297. package/src/shapes/Object/objectSvgExport.spec.ts +112 -0
  298. package/src/shapes/Object/types/FabricObjectProps.ts +1 -4
  299. package/src/shapes/Object/types/ObjectProps.ts +1 -3
  300. package/src/shapes/Path.spec.ts +4 -27
  301. package/src/shapes/Path.ts +2 -4
  302. package/src/shapes/Polygon.spec.ts +4 -31
  303. package/src/shapes/Polyline.spec.ts +4 -31
  304. package/src/shapes/Polyline.ts +11 -12
  305. package/src/shapes/Rect.spec.ts +25 -33
  306. package/src/shapes/Rect.ts +7 -7
  307. package/src/shapes/Text/StyledText.ts +0 -3
  308. package/src/shapes/Text/Text.spec.ts +3 -32
  309. package/src/shapes/Text/Text.ts +5 -6
  310. package/src/shapes/Text/TextSVGExportMixin.spec.ts +9 -0
  311. package/src/shapes/Text/TextSVGExportMixin.ts +14 -16
  312. package/src/shapes/Text/__snapshots__/Text.spec.ts.snap +1 -1
  313. package/src/shapes/Text/__snapshots__/TextSVGExportMixin.spec.ts.snap +1 -1
  314. package/src/shapes/Textbox.spec.ts +5 -5
  315. package/src/shapes/Textbox.ts +6 -5
  316. package/src/shapes/Triangle.ts +4 -4
  317. package/src/shapes/__snapshots__/Image.spec.ts.snap +4 -4
  318. package/src/shapes/__snapshots__/Textbox.spec.ts.snap +5 -5
  319. package/src/util/lang_string.ts +3 -2
  320. package/src/util/misc/svgParsing.ts +2 -1
  321. package/tsconfig.spec.json +1 -0
  322. package/vitest.config.ts +12 -2
  323. package/vitest.extend.ts +6 -2
@@ -0,0 +1,579 @@
1
+ import type { Transform } from 'fabric';
2
+ import { FabricImage, Canvas, Control, Point } from 'fabric';
3
+ import { createImageCroppingControls } from './croppingControls';
4
+ import {
5
+ changeImageWidth,
6
+ changeImageHeight,
7
+ changeImageCropX,
8
+ changeImageCropY,
9
+ cropPanMoveHandler,
10
+ ghostScalePositionHandler,
11
+ scaleEquallyCropGenerator,
12
+ renderGhostImage,
13
+ } from './croppingHandlers';
14
+
15
+ import { describe, expect, test, beforeEach, afterEach, vi } from 'vitest';
16
+
17
+ describe('croppingHandlers', () => {
18
+ let canvas: Canvas;
19
+ let image: FabricImage;
20
+ let transform: Transform;
21
+ let eventData: any;
22
+
23
+ function prepareTransform(target: FabricImage, corner: string): Transform {
24
+ const origin = canvas._getOriginFromCorner(target, corner);
25
+ return {
26
+ target,
27
+ corner,
28
+ originX: origin.x,
29
+ originY: origin.y,
30
+ } as unknown as Transform;
31
+ }
32
+
33
+ function createMockImage(
34
+ options: Partial<{
35
+ width: number;
36
+ height: number;
37
+ cropX: number;
38
+ cropY: number;
39
+ elementWidth: number;
40
+ elementHeight: number;
41
+ }> = {},
42
+ ): FabricImage {
43
+ const {
44
+ width = 100,
45
+ height = 100,
46
+ cropX = 0,
47
+ cropY = 0,
48
+ elementWidth = 200,
49
+ elementHeight = 200,
50
+ } = options;
51
+
52
+ const imgElement = new Image(elementWidth, elementHeight);
53
+ const img = new FabricImage(imgElement, {
54
+ left: 50,
55
+ top: 50,
56
+ width,
57
+ height,
58
+ cropX,
59
+ cropY,
60
+ });
61
+ img.controls = createImageCroppingControls();
62
+
63
+ return img;
64
+ }
65
+
66
+ beforeEach(() => {
67
+ canvas = new Canvas();
68
+ image = createMockImage();
69
+ canvas.add(image);
70
+ eventData = {};
71
+ transform = prepareTransform(image, 'mrc');
72
+ });
73
+
74
+ afterEach(() => {
75
+ canvas.off();
76
+ canvas.clear();
77
+ });
78
+
79
+ describe('changeImageWidth', () => {
80
+ test('changes width normally when within bounds', () => {
81
+ expect(image.width).toBe(100);
82
+ const changed = changeImageWidth(eventData, transform, 180, 50);
83
+ expect(changed).toBe(true);
84
+ expect(image.width).toBe(180);
85
+ });
86
+
87
+ test('constrains width to available width (upper limit)', () => {
88
+ // Image element is 200px wide, cropX is 0, so max available is 200
89
+ image = createMockImage({ width: 100, cropX: 50, elementWidth: 200 });
90
+ canvas.add(image);
91
+ transform = prepareTransform(image, 'mrc');
92
+
93
+ // Try to set width beyond available (200 - 50 = 150 available)
94
+ changeImageWidth(eventData, transform, 500, 50);
95
+ expect(image.width).toBe(150);
96
+ });
97
+
98
+ test('constrains width to minimum of 1 (lower limit)', () => {
99
+ image = createMockImage({ width: 100, cropX: 50, elementWidth: 200 });
100
+ transform = prepareTransform(image, 'mrc');
101
+ changeImageWidth(eventData, transform, 0.1, 50);
102
+ expect(image.width).toBe(1);
103
+ });
104
+
105
+ test('returns false when no modification occurred', () => {
106
+ image = createMockImage({
107
+ width: 100,
108
+ cropX: 50,
109
+ elementWidth: 200,
110
+ });
111
+ transform = prepareTransform(image, 'mrc');
112
+ const changed = changeImageWidth(eventData, transform, 200, 50);
113
+ expect(changed).toBe(true);
114
+ const changed2 = changeImageWidth(eventData, transform, 200, 50);
115
+ expect(changed2).toBe(false);
116
+ });
117
+ });
118
+
119
+ describe('changeImageHeight', () => {
120
+ beforeEach(() => {
121
+ image = createMockImage({
122
+ height: 100,
123
+ cropY: 50,
124
+ elementHeight: 200,
125
+ });
126
+ transform = prepareTransform(image, 'mbc');
127
+ });
128
+
129
+ test('changes height normally when within bounds', () => {
130
+ expect(image.height).toBe(100);
131
+ const changed = changeImageHeight(eventData, transform, 50, 130);
132
+ expect(changed).toBe(true);
133
+ expect(image.height).toBe(130);
134
+ });
135
+
136
+ test('constrains height to available height (upper limit)', () => {
137
+ // Try to set height beyond available (200 - 50 = 150 available
138
+ changeImageHeight(eventData, transform, 50, 500);
139
+ expect(image.height).toBeLessThanOrEqual(150);
140
+ });
141
+
142
+ test('constrains height to minimum of 1 (lower limit)', () => {
143
+ // Mock to simulate setting negative height
144
+ changeImageHeight(eventData, transform, 50, 0.1);
145
+ expect(image.height).toBe(1);
146
+ });
147
+
148
+ test('returns false when no modification occurred', () => {
149
+ const changed = changeImageHeight(eventData, transform, 50, 200);
150
+ expect(changed).toBe(true);
151
+ const changed2 = changeImageHeight(eventData, transform, 50, 200);
152
+ expect(changed2).toBe(false);
153
+ });
154
+ });
155
+
156
+ describe('changeImageCropX', () => {
157
+ beforeEach(() => {
158
+ image = createMockImage({
159
+ width: 100,
160
+ cropX: 50,
161
+ elementWidth: 200,
162
+ });
163
+ // Use 'ml' corner for cropX - changing left side moves cropX
164
+ transform = prepareTransform(image, 'mlc');
165
+ });
166
+
167
+ test('changes cropX and width together', () => {
168
+ const changed = changeImageCropX(eventData, transform, 20, 50);
169
+ expect(image.cropX).toBe(70);
170
+ expect(image.width).toBe(80);
171
+ expect(changed).toBe(true);
172
+ });
173
+
174
+ test('constrains cropX to minimum of 0 and adjusts width accordingly', () => {
175
+ image = createMockImage({ width: 100, cropX: 10, elementWidth: 200 });
176
+ transform = prepareTransform(image, 'mlc');
177
+
178
+ changeImageCropX(eventData, transform, -10, 50);
179
+
180
+ // newCropX is clamped to 0 (was -10)
181
+ expect(image.cropX).toBe(0);
182
+ // width = 100 + 10 - 0 = 110
183
+ expect(image.width).toBe(110);
184
+ });
185
+
186
+ test('constrains cropX so image stays within element bounds and adjusts width accordingly', () => {
187
+ changeImageCropX(eventData, transform, 50, 50);
188
+ // newCropX = 100, but clamped to elementWidth - width = 200 - 100 = 100 (stays 100)
189
+ expect(image.cropX).toBe(100);
190
+ // width = 100 + 50 - 100 = 50
191
+ expect(image.width).toBe(50);
192
+ // cropX + width should not exceed element width (200)
193
+ expect(image.cropX + image.width).toBeLessThanOrEqual(200);
194
+ });
195
+
196
+ test('returns false when no modification occurred', () => {
197
+ const changed = changeImageCropX(eventData, transform, 0, 50);
198
+ expect(changed).toBe(false);
199
+ });
200
+ });
201
+
202
+ describe('changeImageCropY', () => {
203
+ beforeEach(() => {
204
+ image = createMockImage({
205
+ height: 100,
206
+ cropY: 50,
207
+ elementHeight: 200,
208
+ });
209
+ // Use 'mt' corner for cropY - changing top side moves cropY
210
+ transform = prepareTransform(image, 'mtc');
211
+ });
212
+
213
+ test('changes cropY and height together', () => {
214
+ const changed = changeImageCropY(eventData, transform, 50, 20);
215
+ // newCropY = 50 + 100 - 80 = 70
216
+ // height = 100 + 50 - 70 = 80
217
+ expect(image.cropY).toBe(70);
218
+ expect(image.height).toBe(80);
219
+ expect(changed).toBe(true);
220
+ });
221
+
222
+ test('constrains cropY to minimum of 0 and adjusts height accordingly', () => {
223
+ image = createMockImage({ height: 100, cropY: 10, elementHeight: 200 });
224
+ canvas.add(image);
225
+ transform = prepareTransform(image, 'mtc');
226
+
227
+ changeImageCropY(eventData, transform, 50, -30);
228
+
229
+ // newCropY is clamped to 0 (was -10)
230
+ expect(image.cropY).toBe(0);
231
+ // height = 100 + 10 - 0 = 110
232
+ expect(image.height).toBe(110);
233
+ });
234
+
235
+ test('returns false when no modification occurred', () => {
236
+ const changed = changeImageCropY(eventData, transform, 50, 0);
237
+ expect(changed).toBe(false);
238
+ });
239
+ });
240
+
241
+ describe('cropPanMoveHandler', () => {
242
+ beforeEach(() => {
243
+ image = createMockImage({
244
+ width: 100,
245
+ height: 100,
246
+ cropX: 50,
247
+ cropY: 50,
248
+ elementWidth: 300,
249
+ elementHeight: 300,
250
+ });
251
+ canvas.add(image);
252
+ });
253
+
254
+ test('pans the image by adjusting cropX and cropY', () => {
255
+ const original = {
256
+ left: image.left,
257
+ top: image.top,
258
+ cropX: image.cropX,
259
+ cropY: image.cropY,
260
+ };
261
+
262
+ // Simulate moving the image 10px to the right and 10px down
263
+ image.left = original.left + 10;
264
+ image.top = original.top + 10;
265
+
266
+ const moveEvent = {
267
+ transform: {
268
+ target: image,
269
+ original,
270
+ } as unknown as Transform,
271
+ };
272
+
273
+ cropPanMoveHandler(moveEvent as any);
274
+
275
+ // cropX should decrease (panning right means showing more of the left side)
276
+ expect(image.cropX).toBeLessThan(original.cropX);
277
+ // cropY should decrease (panning down means showing more of the top)
278
+ expect(image.cropY).toBeLessThan(original.cropY);
279
+ // Position should be restored to original
280
+ expect(image.left).toBe(original.left);
281
+ expect(image.top).toBe(original.top);
282
+ });
283
+
284
+ test('constrains cropX to minimum of 0', () => {
285
+ const original = {
286
+ left: image.left,
287
+ top: image.top,
288
+ cropX: 10,
289
+ cropY: 50,
290
+ };
291
+ image.cropX = 10;
292
+
293
+ // Move far right to try to get negative cropX
294
+ image.left = original.left + 100;
295
+ image.top = original.top;
296
+
297
+ const moveEvent = {
298
+ transform: {
299
+ target: image,
300
+ original,
301
+ } as unknown as Transform,
302
+ };
303
+
304
+ cropPanMoveHandler(moveEvent as any);
305
+
306
+ expect(image.cropX).toBeGreaterThanOrEqual(0);
307
+ });
308
+
309
+ test('constrains cropY to minimum of 0', () => {
310
+ const original = {
311
+ left: image.left,
312
+ top: image.top,
313
+ cropX: 50,
314
+ cropY: 10,
315
+ };
316
+ image.cropY = 10;
317
+
318
+ // Move far down to try to get negative cropY
319
+ image.left = original.left;
320
+ image.top = original.top + 100;
321
+
322
+ const moveEvent = {
323
+ transform: {
324
+ target: image,
325
+ original,
326
+ } as unknown as Transform,
327
+ };
328
+
329
+ cropPanMoveHandler(moveEvent as any);
330
+
331
+ expect(image.cropY).toBeGreaterThanOrEqual(0);
332
+ });
333
+
334
+ test('constrains cropX so crop area stays within element bounds', () => {
335
+ const original = {
336
+ left: image.left,
337
+ top: image.top,
338
+ cropX: 150, // Near the right edge (element is 300px wide)
339
+ cropY: 50,
340
+ };
341
+ image.cropX = 150;
342
+
343
+ // Move far left to try to exceed element width
344
+ image.left = original.left - 200;
345
+ image.top = original.top;
346
+
347
+ const moveEvent = {
348
+ transform: {
349
+ target: image,
350
+ original,
351
+ } as unknown as Transform,
352
+ };
353
+
354
+ cropPanMoveHandler(moveEvent as any);
355
+
356
+ // cropX + width should not exceed element width
357
+ expect(image.cropX + image.width).toBeLessThanOrEqual(300);
358
+ });
359
+
360
+ test('constrains cropY so crop area stays within element bounds', () => {
361
+ const original = {
362
+ left: image.left,
363
+ top: image.top,
364
+ cropX: 50,
365
+ cropY: 150, // Near the bottom edge (element is 300px tall)
366
+ };
367
+ image.cropY = 150;
368
+
369
+ // Move far up to try to exceed element height
370
+ image.left = original.left;
371
+ image.top = original.top - 200;
372
+
373
+ const moveEvent = {
374
+ transform: {
375
+ target: image,
376
+ original,
377
+ } as unknown as Transform,
378
+ };
379
+
380
+ cropPanMoveHandler(moveEvent as any);
381
+
382
+ // cropY + height should not exceed element height
383
+ expect(image.cropY + image.height).toBeLessThanOrEqual(300);
384
+ });
385
+ });
386
+
387
+ describe('ghostScalePositionHandler', () => {
388
+ beforeEach(() => {
389
+ image = createMockImage({
390
+ width: 100,
391
+ height: 100,
392
+ cropX: 50,
393
+ cropY: 50,
394
+ elementWidth: 300,
395
+ elementHeight: 300,
396
+ });
397
+ canvas.add(image);
398
+ });
399
+
400
+ test('positions top-left corner control correctly', () => {
401
+ const control = new Control({ x: -0.5, y: -0.5 });
402
+ const result = ghostScalePositionHandler.call(
403
+ control,
404
+ new Point(100, 100),
405
+ [1, 2, 3, 4, 5, 6], // this matrix is not used
406
+ image,
407
+ );
408
+
409
+ expect(result).toEqual({ x: -50, y: -50 });
410
+ });
411
+
412
+ test('positions bottom-right corner control correctly', () => {
413
+ const control = new Control({ x: 0.5, y: 0.5 });
414
+ const result = ghostScalePositionHandler.call(
415
+ control,
416
+ new Point(100, 100),
417
+ [1, 2, 3, 4, 5, 6], // this matrix is not used
418
+ image,
419
+ );
420
+
421
+ expect(result).toEqual({ x: 250, y: 250 });
422
+ });
423
+
424
+ test('positions top-right corner control correctly', () => {
425
+ const control = new Control({ x: 0.5, y: -0.5 });
426
+ const result = ghostScalePositionHandler.call(
427
+ control,
428
+ new Point(100, 100),
429
+ [1, 2, 3, 4, 5, 6], // this matrix is not used
430
+ image,
431
+ );
432
+
433
+ expect(result).toEqual({ x: 250, y: -50 });
434
+ });
435
+
436
+ test('positions bottom-left corner control correctly', () => {
437
+ const control = new Control({ x: -0.5, y: 0.5 });
438
+ const result = ghostScalePositionHandler.call(
439
+ control,
440
+ new Point(100, 100),
441
+ [1, 2, 3, 4, 5, 6], // this matrix is not used
442
+ image,
443
+ );
444
+
445
+ expect(result).toEqual({ x: -50, y: 250 });
446
+ });
447
+ });
448
+
449
+ describe('scaleEquallyCropGenerator', () => {
450
+ beforeEach(() => {
451
+ image = createMockImage({
452
+ width: 100,
453
+ height: 100,
454
+ cropX: 50,
455
+ cropY: 50,
456
+ elementWidth: 300,
457
+ elementHeight: 300,
458
+ });
459
+ canvas.add(image);
460
+ });
461
+
462
+ test('returns a TransformActionHandler function', () => {
463
+ const handler = scaleEquallyCropGenerator(-0.5, -0.5);
464
+ expect(typeof handler).toBe('function');
465
+ });
466
+
467
+ test('scales image uniformly from top-left corner', () => {
468
+ const handler = scaleEquallyCropGenerator(-0.5, -0.5);
469
+ transform = prepareTransform(image, 'tls');
470
+ expect(image.scaleX).toBe(1);
471
+ // Simulate dragging to scale up
472
+ const result = handler(eventData, transform, -400, -400);
473
+
474
+ // The handler should return a boolean
475
+ expect(result).toBe(true);
476
+ expect(image.scaleX.toFixed(2)).toBe('2.17');
477
+ expect(image.scaleX).toBe(image.scaleY);
478
+ });
479
+
480
+ test('scales image uniformly from bottom-right corner', () => {
481
+ const handler = scaleEquallyCropGenerator(0.5, 0.5);
482
+ transform = prepareTransform(image, 'brs');
483
+ expect(image.scaleX).toBe(1);
484
+ const result = handler(eventData, transform, 400, 400);
485
+ expect(result).toBe(true);
486
+ expect(image.scaleX).toBe(1.5);
487
+ expect(image.scaleX).toBe(image.scaleY);
488
+ });
489
+
490
+ test('returns false when scaling would exceed element bounds', () => {
491
+ // Set up image near the edge of element
492
+ image = createMockImage({
493
+ width: 250,
494
+ height: 250,
495
+ cropX: 25,
496
+ cropY: 25,
497
+ elementWidth: 300,
498
+ elementHeight: 300,
499
+ });
500
+ canvas.add(image);
501
+
502
+ const handler = scaleEquallyCropGenerator(-0.5, -0.5);
503
+ transform = prepareTransform(image, 'tls');
504
+
505
+ // Try to scale down significantly which might push bounds
506
+ const result = handler(eventData, transform, 10, 10);
507
+
508
+ expect(result).toBe(false);
509
+ });
510
+
511
+ test('adjusts cropX and cropY when scaling from negative corner', () => {
512
+ image = createMockImage({
513
+ width: 90,
514
+ height: 90,
515
+ cropX: 25,
516
+ cropY: 25,
517
+ elementWidth: 300,
518
+ elementHeight: 300,
519
+ });
520
+ canvas.add(image);
521
+ const handler = scaleEquallyCropGenerator(-0.5, -0.5);
522
+ transform = prepareTransform(image, 'tls');
523
+ expect(image.cropX).toBe(25);
524
+ expect(image.cropY).toBe(25);
525
+ const result = handler(eventData, transform, 5, 5);
526
+ expect(result).toBe(true);
527
+ // When scaling from top-left, cropX and cropY should be recalculated
528
+ expect(image.cropX).toBe(0);
529
+ expect(image.cropY).toBe(0);
530
+ });
531
+ });
532
+
533
+ describe('renderGhostImage', () => {
534
+ beforeEach(() => {
535
+ image = createMockImage({
536
+ width: 100,
537
+ height: 100,
538
+ cropX: 50,
539
+ cropY: 50,
540
+ elementWidth: 300,
541
+ elementHeight: 300,
542
+ });
543
+ });
544
+
545
+ test('draws image at correct position based on crop values', () => {
546
+ const mockCtx = {
547
+ globalAlpha: 1,
548
+ drawImage: vi.fn(),
549
+ } as unknown as CanvasRenderingContext2D;
550
+
551
+ renderGhostImage.call(image, { ctx: mockCtx });
552
+
553
+ // Should draw at (-width/2 - cropX, -height/2 - cropY)
554
+ // = (-50 - 50, -50 - 50) = (-100, -100)
555
+ expect(mockCtx.drawImage).toHaveBeenCalledWith(
556
+ image._element,
557
+ -100,
558
+ -100,
559
+ );
560
+ });
561
+
562
+ test('temporarily reduces globalAlpha by 50%', () => {
563
+ let alphaWhenDrawing: number | undefined;
564
+ const mockCtx = {
565
+ globalAlpha: 0.8,
566
+ drawImage: vi.fn(() => {
567
+ alphaWhenDrawing = mockCtx.globalAlpha;
568
+ }),
569
+ } as unknown as CanvasRenderingContext2D;
570
+
571
+ renderGhostImage.call(image, { ctx: mockCtx });
572
+
573
+ // During draw, alpha should be 0.8 * 0.5 = 0.4
574
+ expect(alphaWhenDrawing).toBe(0.4);
575
+ // After render, alpha should be restored
576
+ expect(mockCtx.globalAlpha).toBe(0.8);
577
+ });
578
+ });
579
+ });