js-draw 0.18.2 → 0.20.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 (269) hide show
  1. package/.eslintrc.js +1 -0
  2. package/CHANGELOG.md +10 -0
  3. package/dist/bundle.js +2 -2
  4. package/dist/bundledStyles.js +1 -0
  5. package/dist/cjs/src/Color4.d.ts +8 -0
  6. package/dist/cjs/src/Color4.js +67 -0
  7. package/dist/cjs/src/Editor.d.ts +2 -2
  8. package/dist/cjs/src/Editor.js +7 -7
  9. package/dist/cjs/src/SVGLoader.js +77 -12
  10. package/dist/cjs/src/Viewport.d.ts +2 -0
  11. package/dist/cjs/src/Viewport.js +6 -2
  12. package/dist/cjs/src/components/AbstractComponent.d.ts +2 -2
  13. package/dist/cjs/src/components/AbstractComponent.js +3 -3
  14. package/dist/cjs/src/components/{ImageBackground.d.ts → BackgroundComponent.d.ts} +23 -3
  15. package/dist/cjs/src/components/BackgroundComponent.js +309 -0
  16. package/dist/cjs/src/components/RestylableComponent.d.ts +21 -2
  17. package/dist/cjs/src/components/Stroke.d.ts +35 -0
  18. package/dist/cjs/src/components/Stroke.js +37 -3
  19. package/dist/cjs/src/components/TextComponent.d.ts +27 -17
  20. package/dist/cjs/src/components/TextComponent.js +23 -1
  21. package/dist/cjs/src/components/lib.d.ts +4 -3
  22. package/dist/cjs/src/components/lib.js +2 -2
  23. package/dist/cjs/src/components/util/StrokeSmoother.js +25 -15
  24. package/dist/cjs/src/lib.d.ts +30 -0
  25. package/dist/cjs/src/lib.js +30 -0
  26. package/dist/cjs/src/localizations/de.js +1 -1
  27. package/dist/cjs/src/localizations/es.js +1 -1
  28. package/dist/cjs/src/math/Path.js +1 -1
  29. package/dist/cjs/src/math/polynomial/QuadraticBezier.d.ts +28 -0
  30. package/dist/cjs/src/math/polynomial/QuadraticBezier.js +115 -0
  31. package/dist/cjs/src/math/polynomial/solveQuadratic.d.ts +6 -0
  32. package/dist/cjs/src/math/polynomial/solveQuadratic.js +36 -0
  33. package/dist/cjs/src/rendering/RenderingStyle.d.ts +4 -4
  34. package/dist/cjs/src/rendering/TextRenderingStyle.d.ts +10 -10
  35. package/dist/cjs/src/rendering/lib.d.ts +2 -0
  36. package/dist/cjs/src/rendering/renderers/AbstractRenderer.d.ts +2 -2
  37. package/dist/cjs/src/rendering/renderers/CanvasRenderer.d.ts +2 -2
  38. package/dist/cjs/src/rendering/renderers/CanvasRenderer.js +5 -3
  39. package/dist/cjs/src/rendering/renderers/DummyRenderer.d.ts +2 -2
  40. package/dist/cjs/src/rendering/renderers/SVGRenderer.d.ts +2 -2
  41. package/dist/cjs/src/rendering/renderers/SVGRenderer.js +15 -6
  42. package/dist/cjs/src/rendering/renderers/TextOnlyRenderer.d.ts +2 -2
  43. package/dist/cjs/src/toolbar/IconProvider.d.ts +2 -2
  44. package/dist/cjs/src/toolbar/localization.d.ts +2 -1
  45. package/dist/cjs/src/toolbar/localization.js +3 -2
  46. package/dist/cjs/src/toolbar/widgets/BaseWidget.js +1 -1
  47. package/dist/cjs/src/toolbar/widgets/DocumentPropertiesWidget.d.ts +5 -0
  48. package/dist/cjs/src/toolbar/widgets/DocumentPropertiesWidget.js +77 -2
  49. package/dist/cjs/src/toolbar/widgets/PenToolWidget.js +1 -1
  50. package/dist/cjs/src/tools/FindTool.js +1 -1
  51. package/dist/cjs/src/tools/SoundUITool.d.ts +24 -0
  52. package/dist/cjs/src/tools/SoundUITool.js +164 -0
  53. package/dist/cjs/src/tools/TextTool.d.ts +2 -2
  54. package/dist/cjs/src/tools/ToolController.js +6 -1
  55. package/dist/cjs/src/tools/lib.d.ts +1 -0
  56. package/dist/cjs/src/tools/lib.js +3 -1
  57. package/dist/cjs/src/tools/localization.d.ts +3 -0
  58. package/dist/cjs/src/tools/localization.js +3 -0
  59. package/dist/mjs/src/Color4.d.ts +8 -0
  60. package/dist/mjs/src/Color4.mjs +64 -0
  61. package/dist/mjs/src/Editor.d.ts +2 -2
  62. package/dist/mjs/src/Editor.mjs +6 -6
  63. package/dist/mjs/src/SVGLoader.mjs +76 -11
  64. package/dist/mjs/src/Viewport.d.ts +2 -0
  65. package/dist/mjs/src/Viewport.mjs +6 -2
  66. package/dist/mjs/src/components/AbstractComponent.d.ts +2 -2
  67. package/dist/mjs/src/components/AbstractComponent.mjs +3 -3
  68. package/dist/mjs/src/components/{ImageBackground.d.ts → BackgroundComponent.d.ts} +23 -3
  69. package/dist/mjs/src/components/BackgroundComponent.mjs +279 -0
  70. package/dist/mjs/src/components/RestylableComponent.d.ts +21 -2
  71. package/dist/mjs/src/components/Stroke.d.ts +35 -0
  72. package/dist/mjs/src/components/Stroke.mjs +37 -3
  73. package/dist/mjs/src/components/TextComponent.d.ts +27 -17
  74. package/dist/mjs/src/components/TextComponent.mjs +23 -1
  75. package/dist/mjs/src/components/lib.d.ts +4 -3
  76. package/dist/mjs/src/components/lib.mjs +2 -2
  77. package/dist/mjs/src/components/util/StrokeSmoother.mjs +25 -15
  78. package/dist/mjs/src/lib.d.ts +30 -0
  79. package/dist/mjs/src/lib.mjs +30 -0
  80. package/dist/mjs/src/localizations/de.mjs +1 -1
  81. package/dist/mjs/src/localizations/es.mjs +1 -1
  82. package/dist/mjs/src/math/Path.mjs +1 -1
  83. package/dist/mjs/src/math/polynomial/QuadraticBezier.d.ts +28 -0
  84. package/dist/mjs/src/math/polynomial/QuadraticBezier.mjs +109 -0
  85. package/dist/mjs/src/math/polynomial/solveQuadratic.d.ts +6 -0
  86. package/dist/mjs/src/math/polynomial/solveQuadratic.mjs +34 -0
  87. package/dist/mjs/src/rendering/RenderingStyle.d.ts +4 -4
  88. package/dist/mjs/src/rendering/TextRenderingStyle.d.ts +10 -10
  89. package/dist/mjs/src/rendering/lib.d.ts +2 -0
  90. package/dist/mjs/src/rendering/renderers/AbstractRenderer.d.ts +2 -2
  91. package/dist/mjs/src/rendering/renderers/CanvasRenderer.d.ts +2 -2
  92. package/dist/mjs/src/rendering/renderers/CanvasRenderer.mjs +5 -3
  93. package/dist/mjs/src/rendering/renderers/DummyRenderer.d.ts +2 -2
  94. package/dist/mjs/src/rendering/renderers/SVGRenderer.d.ts +2 -2
  95. package/dist/mjs/src/rendering/renderers/SVGRenderer.mjs +15 -6
  96. package/dist/mjs/src/rendering/renderers/TextOnlyRenderer.d.ts +2 -2
  97. package/dist/mjs/src/toolbar/IconProvider.d.ts +2 -2
  98. package/dist/mjs/src/toolbar/localization.d.ts +2 -1
  99. package/dist/mjs/src/toolbar/localization.mjs +3 -2
  100. package/dist/mjs/src/toolbar/widgets/BaseWidget.mjs +1 -1
  101. package/dist/mjs/src/toolbar/widgets/DocumentPropertiesWidget.d.ts +5 -0
  102. package/dist/mjs/src/toolbar/widgets/DocumentPropertiesWidget.mjs +54 -2
  103. package/dist/mjs/src/toolbar/widgets/PenToolWidget.mjs +1 -1
  104. package/dist/mjs/src/tools/FindTool.mjs +1 -1
  105. package/dist/mjs/src/tools/SoundUITool.d.ts +24 -0
  106. package/dist/mjs/src/tools/SoundUITool.mjs +158 -0
  107. package/dist/mjs/src/tools/TextTool.d.ts +2 -2
  108. package/dist/mjs/src/tools/ToolController.mjs +6 -1
  109. package/dist/mjs/src/tools/lib.d.ts +1 -0
  110. package/dist/mjs/src/tools/lib.mjs +1 -0
  111. package/dist/mjs/src/tools/localization.d.ts +3 -0
  112. package/dist/mjs/src/tools/localization.mjs +3 -0
  113. package/jest.config.js +1 -1
  114. package/package.json +19 -17
  115. package/src/Editor.css +2 -2
  116. package/src/tools/SoundUITool.css +15 -0
  117. package/src/tools/tools.css +4 -0
  118. package/dist/cjs/src/components/ImageBackground.js +0 -146
  119. package/dist/mjs/src/components/ImageBackground.mjs +0 -139
  120. package/src/Color4.test.ts +0 -40
  121. package/src/Color4.ts +0 -236
  122. package/src/Editor.loadFrom.test.ts +0 -24
  123. package/src/Editor.toSVG.test.ts +0 -111
  124. package/src/Editor.ts +0 -1122
  125. package/src/EditorImage.test.ts +0 -120
  126. package/src/EditorImage.ts +0 -603
  127. package/src/EventDispatcher.test.ts +0 -123
  128. package/src/EventDispatcher.ts +0 -71
  129. package/src/Pointer.ts +0 -127
  130. package/src/SVGLoader.test.ts +0 -114
  131. package/src/SVGLoader.ts +0 -511
  132. package/src/UndoRedoHistory.test.ts +0 -33
  133. package/src/UndoRedoHistory.ts +0 -102
  134. package/src/Viewport.ts +0 -319
  135. package/src/bundle/bundled.ts +0 -7
  136. package/src/commands/Command.ts +0 -45
  137. package/src/commands/Duplicate.ts +0 -48
  138. package/src/commands/Erase.ts +0 -74
  139. package/src/commands/SerializableCommand.ts +0 -49
  140. package/src/commands/UnresolvedCommand.ts +0 -37
  141. package/src/commands/invertCommand.ts +0 -51
  142. package/src/commands/lib.ts +0 -16
  143. package/src/commands/localization.ts +0 -47
  144. package/src/commands/uniteCommands.test.ts +0 -23
  145. package/src/commands/uniteCommands.ts +0 -135
  146. package/src/components/AbstractComponent.transformBy.test.ts +0 -22
  147. package/src/components/AbstractComponent.ts +0 -364
  148. package/src/components/ImageBackground.test.ts +0 -35
  149. package/src/components/ImageBackground.ts +0 -176
  150. package/src/components/ImageComponent.ts +0 -171
  151. package/src/components/RestylableComponent.ts +0 -142
  152. package/src/components/SVGGlobalAttributesObject.ts +0 -81
  153. package/src/components/Stroke.test.ts +0 -139
  154. package/src/components/Stroke.ts +0 -245
  155. package/src/components/TextComponent.test.ts +0 -99
  156. package/src/components/TextComponent.ts +0 -315
  157. package/src/components/UnknownSVGObject.test.ts +0 -10
  158. package/src/components/UnknownSVGObject.ts +0 -60
  159. package/src/components/builders/ArrowBuilder.ts +0 -107
  160. package/src/components/builders/FreehandLineBuilder.ts +0 -212
  161. package/src/components/builders/LineBuilder.ts +0 -77
  162. package/src/components/builders/PressureSensitiveFreehandLineBuilder.ts +0 -454
  163. package/src/components/builders/RectangleBuilder.ts +0 -74
  164. package/src/components/builders/types.ts +0 -15
  165. package/src/components/lib.ts +0 -25
  166. package/src/components/localization.ts +0 -22
  167. package/src/components/util/StrokeSmoother.ts +0 -293
  168. package/src/components/util/describeComponentList.ts +0 -18
  169. package/src/lib.ts +0 -37
  170. package/src/localization.ts +0 -34
  171. package/src/localizations/de.ts +0 -98
  172. package/src/localizations/en.ts +0 -8
  173. package/src/localizations/es.ts +0 -74
  174. package/src/localizations/getLocalizationTable.test.ts +0 -27
  175. package/src/localizations/getLocalizationTable.ts +0 -55
  176. package/src/math/LineSegment2.test.ts +0 -99
  177. package/src/math/LineSegment2.ts +0 -160
  178. package/src/math/Mat33.test.ts +0 -244
  179. package/src/math/Mat33.ts +0 -437
  180. package/src/math/Path.fromString.test.ts +0 -223
  181. package/src/math/Path.test.ts +0 -198
  182. package/src/math/Path.toString.test.ts +0 -77
  183. package/src/math/Path.ts +0 -790
  184. package/src/math/Rect2.test.ts +0 -204
  185. package/src/math/Rect2.ts +0 -315
  186. package/src/math/Triangle.ts +0 -29
  187. package/src/math/Vec2.test.ts +0 -30
  188. package/src/math/Vec2.ts +0 -18
  189. package/src/math/Vec3.test.ts +0 -44
  190. package/src/math/Vec3.ts +0 -218
  191. package/src/math/lib.ts +0 -15
  192. package/src/math/rounding.test.ts +0 -65
  193. package/src/math/rounding.ts +0 -156
  194. package/src/rendering/Display.ts +0 -249
  195. package/src/rendering/RenderingStyle.test.ts +0 -68
  196. package/src/rendering/RenderingStyle.ts +0 -55
  197. package/src/rendering/TextRenderingStyle.ts +0 -45
  198. package/src/rendering/caching/CacheRecord.test.ts +0 -49
  199. package/src/rendering/caching/CacheRecord.ts +0 -77
  200. package/src/rendering/caching/CacheRecordManager.ts +0 -71
  201. package/src/rendering/caching/RenderingCache.test.ts +0 -44
  202. package/src/rendering/caching/RenderingCache.ts +0 -66
  203. package/src/rendering/caching/RenderingCacheNode.ts +0 -405
  204. package/src/rendering/caching/testUtils.ts +0 -35
  205. package/src/rendering/caching/types.ts +0 -34
  206. package/src/rendering/lib.ts +0 -6
  207. package/src/rendering/localization.ts +0 -20
  208. package/src/rendering/renderers/AbstractRenderer.ts +0 -222
  209. package/src/rendering/renderers/CanvasRenderer.ts +0 -296
  210. package/src/rendering/renderers/DummyRenderer.test.ts +0 -42
  211. package/src/rendering/renderers/DummyRenderer.ts +0 -136
  212. package/src/rendering/renderers/SVGRenderer.ts +0 -354
  213. package/src/rendering/renderers/TextOnlyRenderer.ts +0 -70
  214. package/src/testing/beforeEachFile.ts +0 -8
  215. package/src/testing/createEditor.ts +0 -11
  216. package/src/testing/global.d.ts +0 -17
  217. package/src/testing/lib.ts +0 -3
  218. package/src/testing/loadExpectExtensions.ts +0 -25
  219. package/src/testing/sendPenEvent.ts +0 -31
  220. package/src/testing/sendTouchEvent.ts +0 -78
  221. package/src/toolbar/HTMLToolbar.ts +0 -492
  222. package/src/toolbar/IconProvider.ts +0 -736
  223. package/src/toolbar/lib.ts +0 -4
  224. package/src/toolbar/localization.ts +0 -106
  225. package/src/toolbar/makeColorInput.ts +0 -145
  226. package/src/toolbar/types.ts +0 -5
  227. package/src/toolbar/widgets/ActionButtonWidget.ts +0 -39
  228. package/src/toolbar/widgets/BaseToolWidget.ts +0 -56
  229. package/src/toolbar/widgets/BaseWidget.ts +0 -377
  230. package/src/toolbar/widgets/DocumentPropertiesWidget.ts +0 -167
  231. package/src/toolbar/widgets/EraserToolWidget.ts +0 -85
  232. package/src/toolbar/widgets/HandToolWidget.ts +0 -250
  233. package/src/toolbar/widgets/InsertImageWidget.ts +0 -223
  234. package/src/toolbar/widgets/OverflowWidget.ts +0 -92
  235. package/src/toolbar/widgets/PenToolWidget.ts +0 -288
  236. package/src/toolbar/widgets/SelectionToolWidget.ts +0 -190
  237. package/src/toolbar/widgets/TextToolWidget.ts +0 -145
  238. package/src/toolbar/widgets/lib.ts +0 -13
  239. package/src/tools/BaseTool.ts +0 -76
  240. package/src/tools/Eraser.test.ts +0 -103
  241. package/src/tools/Eraser.ts +0 -139
  242. package/src/tools/FindTool.ts +0 -152
  243. package/src/tools/PanZoom.test.ts +0 -310
  244. package/src/tools/PanZoom.ts +0 -520
  245. package/src/tools/PasteHandler.ts +0 -95
  246. package/src/tools/Pen.test.ts +0 -194
  247. package/src/tools/Pen.ts +0 -226
  248. package/src/tools/PipetteTool.ts +0 -55
  249. package/src/tools/SelectionTool/SelectAllShortcutHandler.ts +0 -28
  250. package/src/tools/SelectionTool/Selection.ts +0 -607
  251. package/src/tools/SelectionTool/SelectionHandle.ts +0 -108
  252. package/src/tools/SelectionTool/SelectionTool.test.ts +0 -261
  253. package/src/tools/SelectionTool/SelectionTool.ts +0 -480
  254. package/src/tools/SelectionTool/TransformMode.ts +0 -114
  255. package/src/tools/SelectionTool/types.ts +0 -11
  256. package/src/tools/TextTool.ts +0 -326
  257. package/src/tools/ToolController.ts +0 -178
  258. package/src/tools/ToolEnabledGroup.ts +0 -14
  259. package/src/tools/ToolSwitcherShortcut.ts +0 -39
  260. package/src/tools/ToolbarShortcutHandler.ts +0 -34
  261. package/src/tools/UndoRedoShortcut.test.ts +0 -56
  262. package/src/tools/UndoRedoShortcut.ts +0 -25
  263. package/src/tools/lib.ts +0 -21
  264. package/src/tools/localization.ts +0 -66
  265. package/src/types.ts +0 -234
  266. package/src/util/assertions.ts +0 -55
  267. package/src/util/fileToBase64.ts +0 -18
  268. package/src/util/untilNextAnimationFrame.ts +0 -9
  269. package/src/util/waitForTimeout.ts +0 -9
@@ -8,7 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import Color4 from './Color4.mjs';
11
- import ImageBackground, { BackgroundType, imageBackgroundCSSClassName } from './components/ImageBackground.mjs';
11
+ import BackgroundComponent, { BackgroundType, backgroundTypeToClassNameMap, imageBackgroundCSSClassName, imageBackgroundGridSizeCSSPrefix, imageBackgroundNonAutomaticSecondaryColorCSSClassName } from './components/BackgroundComponent.mjs';
12
12
  import ImageComponent from './components/ImageComponent.mjs';
13
13
  import Stroke from './components/Stroke.mjs';
14
14
  import SVGGlobalAttributesObject from './components/SVGGlobalAttributesObject.mjs';
@@ -39,14 +39,13 @@ export default class SVGLoader {
39
39
  // If [computedStyles] is given, it is preferred to directly accessing node's style object.
40
40
  getStyle(node, computedStyles) {
41
41
  var _a, _b, _c, _d, _f, _g;
42
- const style = {
43
- fill: Color4.transparent,
44
- };
42
+ let fill = Color4.transparent;
43
+ let stroke;
45
44
  // If possible, use computedStyles (allows property inheritance).
46
45
  const fillAttribute = (_b = (_a = node.getAttribute('fill')) !== null && _a !== void 0 ? _a : computedStyles === null || computedStyles === void 0 ? void 0 : computedStyles.fill) !== null && _b !== void 0 ? _b : node.style.fill;
47
46
  if (fillAttribute) {
48
47
  try {
49
- style.fill = Color4.fromString(fillAttribute);
48
+ fill = Color4.fromString(fillAttribute);
50
49
  }
51
50
  catch (e) {
52
51
  console.error('Unknown fill color,', fillAttribute);
@@ -62,7 +61,7 @@ export default class SVGLoader {
62
61
  }
63
62
  const strokeColor = Color4.fromString(strokeAttribute);
64
63
  if (strokeColor.a > 0) {
65
- style.stroke = {
64
+ stroke = {
66
65
  width,
67
66
  color: strokeColor,
68
67
  };
@@ -72,6 +71,10 @@ export default class SVGLoader {
72
71
  console.error('Error parsing stroke data:', e);
73
72
  }
74
73
  }
74
+ const style = {
75
+ fill,
76
+ stroke,
77
+ };
75
78
  return style;
76
79
  }
77
80
  strokeDataFromElem(node) {
@@ -147,11 +150,69 @@ export default class SVGLoader {
147
150
  });
148
151
  }
149
152
  addBackground(node) {
150
- var _a, _b, _c;
153
+ var _a, _b, _c, _d;
151
154
  return __awaiter(this, void 0, void 0, function* () {
152
- const fill = Color4.fromString((_b = (_a = node.getAttribute('fill')) !== null && _a !== void 0 ? _a : node.style.fill) !== null && _b !== void 0 ? _b : 'black');
153
- const elem = new ImageBackground(BackgroundType.SolidColor, fill);
154
- yield ((_c = this.onAddComponent) === null || _c === void 0 ? void 0 : _c.call(this, elem));
155
+ // If a grid background,
156
+ if (node.classList.contains(backgroundTypeToClassNameMap[BackgroundType.Grid])) {
157
+ let foregroundStr;
158
+ let backgroundStr;
159
+ let gridStrokeWidthStr;
160
+ // If a group,
161
+ if (node.tagName.toLowerCase() === 'g') {
162
+ // We expect exactly two children. One of these is the solid
163
+ // background of the grid
164
+ if (node.children.length !== 2) {
165
+ yield this.addUnknownNode(node);
166
+ return;
167
+ }
168
+ const background = node.children[0];
169
+ const grid = node.children[1];
170
+ backgroundStr = background.getAttribute('fill');
171
+ foregroundStr = grid.getAttribute('stroke');
172
+ gridStrokeWidthStr = grid.getAttribute('stroke-width');
173
+ }
174
+ else {
175
+ backgroundStr = node.getAttribute('fill');
176
+ foregroundStr = node.getAttribute('stroke');
177
+ gridStrokeWidthStr = node.getAttribute('stroke-width');
178
+ }
179
+ // Default to a transparent background.
180
+ backgroundStr !== null && backgroundStr !== void 0 ? backgroundStr : (backgroundStr = Color4.transparent.toHexString());
181
+ // A grid must have a foreground color specified.
182
+ if (!foregroundStr) {
183
+ yield this.addUnknownNode(node);
184
+ return;
185
+ }
186
+ // Extract the grid size from the class name
187
+ let gridSize = undefined;
188
+ for (const className of node.classList) {
189
+ if (className.startsWith(imageBackgroundGridSizeCSSPrefix)) {
190
+ const sizeStr = className.substring(imageBackgroundGridSizeCSSPrefix.length);
191
+ gridSize = parseFloat(sizeStr.replace(/p/g, '.'));
192
+ }
193
+ }
194
+ let gridStrokeWidth = undefined;
195
+ if (gridStrokeWidthStr) {
196
+ gridStrokeWidth = parseFloat(gridStrokeWidthStr);
197
+ }
198
+ const backgroundColor = Color4.fromString(backgroundStr);
199
+ let foregroundColor = Color4.fromString(foregroundStr);
200
+ // Should the foreground color be determined automatically?
201
+ if (!node.classList.contains(imageBackgroundNonAutomaticSecondaryColorCSSClassName)) {
202
+ foregroundColor = undefined;
203
+ }
204
+ const elem = BackgroundComponent.ofGrid(backgroundColor, gridSize, foregroundColor, gridStrokeWidth);
205
+ yield ((_a = this.onAddComponent) === null || _a === void 0 ? void 0 : _a.call(this, elem));
206
+ }
207
+ // Otherwise, if just a <path/>, it's a solid color background.
208
+ else if (node.tagName.toLowerCase() === 'path') {
209
+ const fill = Color4.fromString((_c = (_b = node.getAttribute('fill')) !== null && _b !== void 0 ? _b : node.style.fill) !== null && _c !== void 0 ? _c : 'black');
210
+ const elem = new BackgroundComponent(BackgroundType.SolidColor, fill);
211
+ yield ((_d = this.onAddComponent) === null || _d === void 0 ? void 0 : _d.call(this, elem));
212
+ }
213
+ else {
214
+ yield this.addUnknownNode(node);
215
+ }
155
216
  });
156
217
  }
157
218
  // If given, 'supportedAttrs' will have x, y, etc. attributes that were used in computing the transform added to it,
@@ -319,7 +380,11 @@ export default class SVGLoader {
319
380
  let visitChildren = true;
320
381
  switch (node.tagName.toLowerCase()) {
321
382
  case 'g':
322
- // Continue -- visit the node's children.
383
+ if (node.classList.contains(imageBackgroundCSSClassName)) {
384
+ yield this.addBackground(node);
385
+ visitChildren = false;
386
+ }
387
+ // Otherwise, continue -- visit the node's children.
323
388
  break;
324
389
  case 'path':
325
390
  if (node.classList.contains(imageBackgroundCSSClassName)) {
@@ -50,6 +50,8 @@ export declare class Viewport {
50
50
  */
51
51
  getScaleFactorToNearestPowerOfTen(): number;
52
52
  private getScaleFactorToNearestPowerOf;
53
+ /** Returns the size of a grid cell (in canvas units) as used by {@link snapToGrid}. */
54
+ static getGridSize(scaleFactor: number): number;
53
55
  snapToGrid(canvasPos: Point2): Vec3;
54
56
  /** Returns the size of one screen pixel in canvas units. */
55
57
  getSizeOfPixelOnCanvas(): number;
@@ -97,10 +97,14 @@ export class Viewport {
97
97
  const scaleFactor = this.getScaleFactor();
98
98
  return Math.pow(powerOf, Math.round(Math.log(scaleFactor) / Math.log(powerOf)));
99
99
  }
100
+ /** Returns the size of a grid cell (in canvas units) as used by {@link snapToGrid}. */
101
+ static getGridSize(scaleFactor) {
102
+ return 50 / scaleFactor;
103
+ }
100
104
  snapToGrid(canvasPos) {
105
+ const scaleFactor = this.getScaleFactorToNearestPowerOf(2);
101
106
  const snapCoordinate = (coordinate) => {
102
- const scaleFactor = this.getScaleFactorToNearestPowerOf(2);
103
- const roundFactor = scaleFactor / 50;
107
+ const roundFactor = 1 / Viewport.getGridSize(scaleFactor);
104
108
  const snapped = Math.round(coordinate * roundFactor) / roundFactor;
105
109
  return snapped;
106
110
  };
@@ -26,13 +26,13 @@ export default abstract class AbstractComponent {
26
26
  /**
27
27
  * Attach data that can be used while exporting the component (e.g. to SVG).
28
28
  *
29
- * This is intended for use by a {@link ImageLoader}.
29
+ * This is intended for use by an {@link ImageLoader}.
30
30
  */
31
31
  attachLoadSaveData(key: string, data: LoadSaveData): void;
32
32
  /** See {@link attachLoadSaveData} */
33
33
  getLoadSaveData(): LoadSaveDataTable;
34
34
  getZIndex(): number;
35
- /** @returns the bounding box of */
35
+ /** @returns the bounding box of this. */
36
36
  getBBox(): Rect2;
37
37
  /** Called when this component is added to the given image. */
38
38
  onAddToImage(_image: EditorImage): void;
@@ -27,7 +27,7 @@ export default class AbstractComponent {
27
27
  }
28
28
  }
29
29
  // Returns a unique ID for this element.
30
- // @see { @link lib!EditorImage.lookupElement }
30
+ // @see { @link EditorImage.lookupElement }
31
31
  getId() {
32
32
  return this.id;
33
33
  }
@@ -40,7 +40,7 @@ export default class AbstractComponent {
40
40
  /**
41
41
  * Attach data that can be used while exporting the component (e.g. to SVG).
42
42
  *
43
- * This is intended for use by a {@link ImageLoader}.
43
+ * This is intended for use by an {@link ImageLoader}.
44
44
  */
45
45
  attachLoadSaveData(key, data) {
46
46
  if (!this.loadSaveData[key]) {
@@ -55,7 +55,7 @@ export default class AbstractComponent {
55
55
  getZIndex() {
56
56
  return this.zIndex;
57
57
  }
58
- /** @returns the bounding box of */
58
+ /** @returns the bounding box of this. */
59
59
  getBBox() {
60
60
  return this.contentBBox;
61
61
  }
@@ -11,32 +11,52 @@ import { ImageComponentLocalization } from './localization';
11
11
  import RestyleableComponent, { ComponentStyle } from './RestylableComponent';
12
12
  export declare enum BackgroundType {
13
13
  SolidColor = 0,
14
- None = 1
14
+ Grid = 1,
15
+ None = 2
15
16
  }
16
17
  export declare const imageBackgroundCSSClassName = "js-draw-image-background";
17
- export default class ImageBackground extends AbstractComponent implements RestyleableComponent {
18
+ export declare const imageBackgroundGridSizeCSSPrefix = "js-draw-image-background-grid-";
19
+ export declare const imageBackgroundNonAutomaticSecondaryColorCSSClassName = "js-draw-image-background-non-automatic-secondary-color";
20
+ export declare const backgroundTypeToClassNameMap: {
21
+ 1: string;
22
+ 0: string;
23
+ 2: string;
24
+ };
25
+ export default class BackgroundComponent extends AbstractComponent implements RestyleableComponent {
18
26
  private backgroundType;
19
27
  private mainColor;
20
28
  protected contentBBox: Rect2;
21
29
  private viewportSizeChangeListener;
30
+ private gridSize;
31
+ private gridStrokeWidth;
32
+ private secondaryColor;
22
33
  readonly isRestylableComponent: true;
23
34
  constructor(backgroundType: BackgroundType, mainColor: Color4);
35
+ static ofGrid(backgroundColor: Color4, gridSize?: number, gridColor?: Color4, gridStrokeWidth?: number): BackgroundComponent;
36
+ getBackgroundType(): BackgroundType;
37
+ getMainColor(): Color4;
38
+ getSecondaryColor(): Color4 | null;
39
+ getGridSize(): number;
24
40
  getStyle(): ComponentStyle;
25
41
  updateStyle(style: ComponentStyle): SerializableCommand;
26
42
  forceStyle(style: ComponentStyle, editor: Editor | null): void;
27
43
  onAddToImage(image: EditorImage): void;
28
44
  onRemoveFromImage(): void;
29
45
  private recomputeBBox;
46
+ private generateGridPath;
30
47
  render(canvas: AbstractRenderer, visibleRect?: Rect2): void;
31
48
  intersects(lineSegment: LineSegment2): boolean;
32
49
  isSelectable(): boolean;
33
50
  isBackground(): boolean;
34
51
  protected serializeToJSON(): {
35
52
  mainColor: string;
53
+ secondaryColor: string | undefined;
36
54
  backgroundType: BackgroundType;
55
+ gridSize: number;
56
+ gridStrokeWidth: number;
37
57
  };
38
58
  protected applyTransformation(_affineTransfm: Mat33): void;
39
59
  description(localizationTable: ImageComponentLocalization): string;
40
60
  protected createClone(): AbstractComponent;
41
- static deserializeFromJSON(json: any): ImageBackground;
61
+ static deserializeFromJSON(json: any): BackgroundComponent;
42
62
  }
@@ -0,0 +1,279 @@
1
+ import Color4 from '../Color4.mjs';
2
+ import { EditorImageEventType } from '../EditorImage.mjs';
3
+ import Rect2 from '../math/Rect2.mjs';
4
+ import AbstractComponent from './AbstractComponent.mjs';
5
+ import { createRestyleComponentCommand } from './RestylableComponent.mjs';
6
+ import Path, { PathCommandType } from '../math/Path.mjs';
7
+ import { Vec2 } from '../math/Vec2.mjs';
8
+ import Viewport from '../Viewport.mjs';
9
+ import { toRoundedString } from '../math/rounding.mjs';
10
+ export var BackgroundType;
11
+ (function (BackgroundType) {
12
+ BackgroundType[BackgroundType["SolidColor"] = 0] = "SolidColor";
13
+ BackgroundType[BackgroundType["Grid"] = 1] = "Grid";
14
+ BackgroundType[BackgroundType["None"] = 2] = "None";
15
+ })(BackgroundType || (BackgroundType = {}));
16
+ export const imageBackgroundCSSClassName = 'js-draw-image-background';
17
+ // Class name prefix indicating the size of the background's grid cells (if present).
18
+ export const imageBackgroundGridSizeCSSPrefix = 'js-draw-image-background-grid-';
19
+ // Flag included in rendered SVGs (etc) that indicates that the secondary color of the
20
+ // background has been manually set.
21
+ export const imageBackgroundNonAutomaticSecondaryColorCSSClassName = 'js-draw-image-background-non-automatic-secondary-color';
22
+ export const backgroundTypeToClassNameMap = {
23
+ [BackgroundType.Grid]: 'js-draw-image-background-grid',
24
+ [BackgroundType.SolidColor]: imageBackgroundCSSClassName,
25
+ [BackgroundType.None]: '',
26
+ };
27
+ // Represents the background of the editor's canvas.
28
+ export default class BackgroundComponent extends AbstractComponent {
29
+ constructor(backgroundType, mainColor) {
30
+ super('image-background', 0);
31
+ this.backgroundType = backgroundType;
32
+ this.mainColor = mainColor;
33
+ this.viewportSizeChangeListener = null;
34
+ this.gridSize = Viewport.getGridSize(2);
35
+ this.gridStrokeWidth = 0.7;
36
+ this.secondaryColor = null;
37
+ // eslint-disable-next-line @typescript-eslint/prefer-as-const
38
+ this.isRestylableComponent = true;
39
+ this.contentBBox = Rect2.empty;
40
+ }
41
+ static ofGrid(backgroundColor, gridSize, gridColor, gridStrokeWidth) {
42
+ const background = new BackgroundComponent(BackgroundType.Grid, backgroundColor);
43
+ if (gridSize !== undefined) {
44
+ background.gridSize = gridSize;
45
+ }
46
+ if (gridColor !== undefined) {
47
+ background.secondaryColor = gridColor;
48
+ }
49
+ if (gridStrokeWidth !== undefined) {
50
+ background.gridStrokeWidth = gridStrokeWidth;
51
+ }
52
+ return background;
53
+ }
54
+ getBackgroundType() {
55
+ return this.backgroundType;
56
+ }
57
+ // @internal
58
+ getMainColor() {
59
+ return this.mainColor;
60
+ }
61
+ // @internal
62
+ getSecondaryColor() {
63
+ return this.secondaryColor;
64
+ }
65
+ // @internal
66
+ getGridSize() {
67
+ return this.gridSize;
68
+ }
69
+ getStyle() {
70
+ let color = this.mainColor;
71
+ if (this.backgroundType === BackgroundType.None) {
72
+ color = undefined;
73
+ }
74
+ return {
75
+ color,
76
+ };
77
+ }
78
+ updateStyle(style) {
79
+ return createRestyleComponentCommand(this.getStyle(), style, this);
80
+ }
81
+ // @internal
82
+ forceStyle(style, editor) {
83
+ const fill = style.color;
84
+ if (!fill) {
85
+ return;
86
+ }
87
+ this.mainColor = fill;
88
+ // A solid background and transparent fill is equivalent to no background.
89
+ if (fill.eq(Color4.transparent) && this.backgroundType === BackgroundType.SolidColor) {
90
+ this.backgroundType = BackgroundType.None;
91
+ }
92
+ else if (this.backgroundType === BackgroundType.None) {
93
+ this.backgroundType = BackgroundType.SolidColor;
94
+ }
95
+ if (editor) {
96
+ editor.image.queueRerenderOf(this);
97
+ editor.queueRerender();
98
+ }
99
+ }
100
+ onAddToImage(image) {
101
+ if (this.viewportSizeChangeListener) {
102
+ console.warn('onAddToImage called when background is already in an image');
103
+ this.onRemoveFromImage();
104
+ }
105
+ this.viewportSizeChangeListener = image.notifier.on(EditorImageEventType.ExportViewportChanged, () => {
106
+ this.recomputeBBox(image);
107
+ });
108
+ this.recomputeBBox(image);
109
+ }
110
+ onRemoveFromImage() {
111
+ var _a;
112
+ (_a = this.viewportSizeChangeListener) === null || _a === void 0 ? void 0 : _a.remove();
113
+ this.viewportSizeChangeListener = null;
114
+ }
115
+ recomputeBBox(image) {
116
+ const importExportRect = image.getImportExportViewport().visibleRect;
117
+ if (!this.contentBBox.eq(importExportRect)) {
118
+ this.contentBBox = importExportRect;
119
+ // Re-render this if already added to the EditorImage.
120
+ image.queueRerenderOf(this);
121
+ }
122
+ }
123
+ generateGridPath(visibleRect) {
124
+ var _a, _b;
125
+ const targetRect = (_b = (_a = visibleRect === null || visibleRect === void 0 ? void 0 : visibleRect.grownBy(this.gridStrokeWidth)) === null || _a === void 0 ? void 0 : _a.intersection(this.contentBBox)) !== null && _b !== void 0 ? _b : this.contentBBox;
126
+ const roundDownToGrid = (coord) => Math.floor(coord / this.gridSize) * this.gridSize;
127
+ const roundUpToGrid = (coord) => Math.ceil(coord / this.gridSize) * this.gridSize;
128
+ const startY = roundUpToGrid(targetRect.y);
129
+ const endY = roundDownToGrid(targetRect.y + targetRect.h);
130
+ const startX = roundUpToGrid(targetRect.x);
131
+ const endX = roundDownToGrid(targetRect.x + targetRect.w);
132
+ const result = [];
133
+ // Don't generate grids with a huge number of rows/columns -- such grids
134
+ // take a long time to render and are likely invisible due to the number of
135
+ // cells.
136
+ const rowCount = (endY - startY) / this.gridSize;
137
+ const colCount = (endX - startX) / this.gridSize;
138
+ const maxGridCols = 1000;
139
+ const maxGridRows = 1000;
140
+ if (rowCount > maxGridRows || colCount > maxGridCols) {
141
+ return Path.empty;
142
+ }
143
+ const startPoint = Vec2.of(targetRect.x, startY);
144
+ for (let y = startY; y <= endY; y += this.gridSize) {
145
+ result.push({
146
+ kind: PathCommandType.MoveTo,
147
+ point: Vec2.of(targetRect.x, y),
148
+ });
149
+ result.push({
150
+ kind: PathCommandType.LineTo,
151
+ point: Vec2.of(targetRect.x + targetRect.w, y),
152
+ });
153
+ }
154
+ for (let x = startX; x <= endX; x += this.gridSize) {
155
+ result.push({
156
+ kind: PathCommandType.MoveTo,
157
+ point: Vec2.of(x, targetRect.y),
158
+ });
159
+ result.push({
160
+ kind: PathCommandType.LineTo,
161
+ point: Vec2.of(x, targetRect.y + targetRect.h)
162
+ });
163
+ }
164
+ return new Path(startPoint, result);
165
+ }
166
+ render(canvas, visibleRect) {
167
+ if (this.backgroundType === BackgroundType.None) {
168
+ return;
169
+ }
170
+ const clip = true;
171
+ canvas.startObject(this.contentBBox, clip);
172
+ if (this.backgroundType === BackgroundType.SolidColor || this.backgroundType === BackgroundType.Grid) {
173
+ // If the rectangle for this region contains the visible rect,
174
+ // we can fill the entire visible rectangle (which may be more efficient than
175
+ // filling the entire region for this.)
176
+ if (visibleRect) {
177
+ const intersection = visibleRect.intersection(this.contentBBox);
178
+ if (intersection) {
179
+ canvas.fillRect(intersection, this.mainColor);
180
+ }
181
+ }
182
+ else {
183
+ canvas.fillRect(this.contentBBox, this.mainColor);
184
+ }
185
+ }
186
+ if (this.backgroundType === BackgroundType.Grid) {
187
+ let gridColor = this.secondaryColor;
188
+ gridColor !== null && gridColor !== void 0 ? gridColor : (gridColor = Color4.ofRGBA(1 - this.mainColor.r, 1 - this.mainColor.g, 1 - this.mainColor.b, 0.2));
189
+ // If the background fill is completely transparent, ensure visibility on otherwise light
190
+ // and dark backgrounds.
191
+ if (this.mainColor.a === 0) {
192
+ gridColor = Color4.ofRGBA(0.5, 0.5, 0.5, 0.2);
193
+ }
194
+ const style = {
195
+ fill: Color4.transparent,
196
+ stroke: { width: this.gridStrokeWidth, color: gridColor }
197
+ };
198
+ canvas.drawPath(this.generateGridPath(visibleRect).toRenderable(style));
199
+ }
200
+ const backgroundTypeCSSClass = backgroundTypeToClassNameMap[this.backgroundType];
201
+ const classNames = [imageBackgroundCSSClassName];
202
+ if (backgroundTypeCSSClass !== imageBackgroundCSSClassName) {
203
+ classNames.push(backgroundTypeCSSClass);
204
+ const gridSizeStr = toRoundedString(this.gridSize).replace(/[.]/g, 'p');
205
+ classNames.push(imageBackgroundGridSizeCSSPrefix + gridSizeStr);
206
+ }
207
+ if (this.secondaryColor !== null) {
208
+ classNames.push(imageBackgroundNonAutomaticSecondaryColorCSSClassName);
209
+ }
210
+ canvas.endObject(this.getLoadSaveData(), classNames);
211
+ }
212
+ intersects(lineSegment) {
213
+ return this.contentBBox.getEdges().some(edge => edge.intersects(lineSegment));
214
+ }
215
+ isSelectable() {
216
+ return false;
217
+ }
218
+ isBackground() {
219
+ return true;
220
+ }
221
+ serializeToJSON() {
222
+ var _a;
223
+ return {
224
+ mainColor: this.mainColor.toHexString(),
225
+ secondaryColor: (_a = this.secondaryColor) === null || _a === void 0 ? void 0 : _a.toHexString(),
226
+ backgroundType: this.backgroundType,
227
+ gridSize: this.gridSize,
228
+ gridStrokeWidth: this.gridStrokeWidth,
229
+ };
230
+ }
231
+ applyTransformation(_affineTransfm) {
232
+ // Do nothing — it doesn't make sense to transform the background.
233
+ }
234
+ description(localizationTable) {
235
+ if (this.backgroundType === BackgroundType.SolidColor) {
236
+ return localizationTable.filledBackgroundWithColor(this.mainColor.toString());
237
+ }
238
+ else {
239
+ return localizationTable.emptyBackground;
240
+ }
241
+ }
242
+ createClone() {
243
+ return new BackgroundComponent(this.backgroundType, this.mainColor);
244
+ }
245
+ // @internal
246
+ static deserializeFromJSON(json) {
247
+ var _a, _b;
248
+ if (typeof json === 'string') {
249
+ json = JSON.parse(json);
250
+ }
251
+ if (typeof json.mainColor !== 'string') {
252
+ throw new Error('Error deserializing — mainColor must be of type string.');
253
+ }
254
+ let backgroundType;
255
+ const jsonBackgroundType = json.backgroundType;
256
+ if (jsonBackgroundType === BackgroundType.None || jsonBackgroundType === BackgroundType.Grid
257
+ || jsonBackgroundType === BackgroundType.SolidColor) {
258
+ backgroundType = jsonBackgroundType;
259
+ }
260
+ else {
261
+ const exhaustivenessCheck = jsonBackgroundType;
262
+ return exhaustivenessCheck;
263
+ }
264
+ const mainColor = Color4.fromHex(json.mainColor);
265
+ const secondaryColor = json.secondaryColor ? Color4.fromHex(json.secondaryColor) : null;
266
+ const gridSize = (_a = json.gridSize) !== null && _a !== void 0 ? _a : undefined;
267
+ const gridStrokeWidth = (_b = json.gridStrokeWidth) !== null && _b !== void 0 ? _b : undefined;
268
+ const result = new BackgroundComponent(backgroundType, mainColor);
269
+ result.secondaryColor = secondaryColor;
270
+ if (gridSize) {
271
+ result.gridSize = gridSize;
272
+ }
273
+ if (gridStrokeWidth) {
274
+ result.gridStrokeWidth = gridStrokeWidth;
275
+ }
276
+ return result;
277
+ }
278
+ }
279
+ AbstractComponent.registerComponent('image-background', BackgroundComponent.deserializeFromJSON);
@@ -1,16 +1,35 @@
1
1
  import Color4 from '../Color4';
2
2
  import SerializableCommand from '../commands/SerializableCommand';
3
3
  import Editor from '../Editor';
4
- import TextStyle from '../rendering/TextRenderingStyle';
4
+ import TextRenderingStyle from '../rendering/TextRenderingStyle';
5
5
  import AbstractComponent from './AbstractComponent';
6
6
  export interface ComponentStyle {
7
7
  color?: Color4;
8
- textStyle?: TextStyle;
8
+ textStyle?: TextRenderingStyle;
9
9
  }
10
10
  export declare const createRestyleComponentCommand: (initialStyle: ComponentStyle, newStyle: ComponentStyle, component: RestyleableComponent) => SerializableCommand;
11
11
  export declare const isRestylableComponent: (component: AbstractComponent) => component is RestyleableComponent;
12
+ /**
13
+ * An interface to be implemented by components with a changable color or {@link TextRenderingStyle}.
14
+ *
15
+ * All such classes must have a member variable, `isRestylableComponent` that is set to `true`
16
+ * to allow testing whether the class is a `RestylableComponent` (see {@link isRestylableComponent}).
17
+ */
12
18
  export interface RestyleableComponent extends AbstractComponent {
19
+ /**
20
+ * @returns a partial representation of this component's style.
21
+ */
13
22
  getStyle(): ComponentStyle;
23
+ /**
24
+ * Returns a {@link Command} that updates portions of this component's style
25
+ * to the given `style`.
26
+ *
27
+ * @example
28
+ * For some component and editor,
29
+ * ```ts
30
+ * editor.dispatch(component.updateStyle({ color: Color4.red }));
31
+ * ```
32
+ */
14
33
  updateStyle(style: ComponentStyle): SerializableCommand;
15
34
  /**
16
35
  * Set the style of this component in a way that can't be undone/redone
@@ -8,11 +8,43 @@ import AbstractRenderer, { RenderablePathSpec } from '../rendering/renderers/Abs
8
8
  import AbstractComponent from './AbstractComponent';
9
9
  import { ImageComponentLocalization } from './localization';
10
10
  import RestyleableComponent, { ComponentStyle } from './RestylableComponent';
11
+ /**
12
+ * Represents an {@link AbstractComponent} made up of one or more {@link Path}s.
13
+ *
14
+ * @example
15
+ * For some {@link Editor} editor and `Stroke` stroke,
16
+ *
17
+ * **Restyling**:
18
+ * ```ts
19
+ * editor.dispatch(stroke.updateStyle({ color: Color4.red }));
20
+ * ```
21
+ *
22
+ * **Transforming**:
23
+ * ```ts
24
+ * editor.dispatch(stroke.transformBy(Mat33.translation(Vec2.of(10, 0))));
25
+ * ```
26
+ */
11
27
  export default class Stroke extends AbstractComponent implements RestyleableComponent {
12
28
  private parts;
13
29
  protected contentBBox: Rect2;
14
30
  readonly isRestylableComponent: true;
15
31
  private approximateRenderingTime;
32
+ /**
33
+ * Creates a `Stroke` from the given `parts`. All parts should have the
34
+ * same color.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * // A path that starts at (1,1), moves to the right by (2, 0),
39
+ * // then moves down and right by (3, 3)
40
+ * const path = Path.fromString('m1,1 2,0 3,3');
41
+ *
42
+ * const stroke = new Stroke([
43
+ * // Fill with red
44
+ * path.toRenderable({ fill: Color4.red })
45
+ * ]);
46
+ * ```
47
+ */
16
48
  constructor(parts: RenderablePathSpec[]);
17
49
  getStyle(): ComponentStyle;
18
50
  updateStyle(style: ComponentStyle): SerializableCommand;
@@ -22,6 +54,9 @@ export default class Stroke extends AbstractComponent implements RestyleableComp
22
54
  getProportionalRenderingTime(): number;
23
55
  private bboxForPart;
24
56
  protected applyTransformation(affineTransfm: Mat33): void;
57
+ /**
58
+ * @returns the {@link Path.union} of all paths that make up this stroke.
59
+ */
25
60
  getPath(): Path;
26
61
  description(localization: ImageComponentLocalization): string;
27
62
  protected createClone(): AbstractComponent;