js-draw 0.1.11 → 0.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 (220) hide show
  1. package/.eslintrc.js +1 -0
  2. package/.firebaserc +5 -0
  3. package/.github/workflows/firebase-hosting-merge.yml +25 -0
  4. package/.github/workflows/firebase-hosting-pull-request.yml +22 -0
  5. package/.github/workflows/github-pages.yml +52 -0
  6. package/CHANGELOG.md +13 -0
  7. package/README.md +11 -6
  8. package/dist/bundle.js +1 -1
  9. package/dist/src/Color4.d.ts +19 -0
  10. package/dist/src/Color4.js +24 -3
  11. package/dist/src/Editor.d.ts +133 -4
  12. package/dist/src/Editor.js +124 -27
  13. package/dist/src/EditorImage.d.ts +8 -3
  14. package/dist/src/EditorImage.js +42 -26
  15. package/dist/src/EventDispatcher.d.ts +18 -0
  16. package/dist/src/EventDispatcher.js +19 -4
  17. package/dist/src/Pointer.d.ts +1 -1
  18. package/dist/src/Pointer.js +4 -3
  19. package/dist/src/SVGLoader.d.ts +1 -1
  20. package/dist/src/SVGLoader.js +14 -6
  21. package/dist/src/UndoRedoHistory.js +15 -2
  22. package/dist/src/Viewport.d.ts +8 -25
  23. package/dist/src/Viewport.js +18 -10
  24. package/dist/src/bundle/bundled.d.ts +1 -2
  25. package/dist/src/bundle/bundled.js +1 -2
  26. package/dist/src/commands/Command.d.ts +2 -2
  27. package/dist/src/commands/Command.js +4 -4
  28. package/dist/src/commands/Duplicate.d.ts +2 -2
  29. package/dist/src/commands/Duplicate.js +4 -5
  30. package/dist/src/commands/Erase.d.ts +2 -2
  31. package/dist/src/commands/Erase.js +7 -6
  32. package/dist/src/commands/SerializableCommand.d.ts +4 -5
  33. package/dist/src/commands/SerializableCommand.js +12 -4
  34. package/dist/src/commands/invertCommand.d.ts +4 -0
  35. package/dist/src/commands/invertCommand.js +44 -0
  36. package/dist/src/commands/lib.d.ts +6 -0
  37. package/dist/src/commands/lib.js +6 -0
  38. package/dist/src/commands/localization.d.ts +2 -1
  39. package/dist/src/commands/localization.js +1 -0
  40. package/dist/src/components/AbstractComponent.d.ts +16 -11
  41. package/dist/src/components/AbstractComponent.js +28 -17
  42. package/dist/src/components/SVGGlobalAttributesObject.d.ts +4 -4
  43. package/dist/src/components/SVGGlobalAttributesObject.js +8 -2
  44. package/dist/src/components/Stroke.d.ts +16 -6
  45. package/dist/src/components/Stroke.js +12 -9
  46. package/dist/src/components/Text.d.ts +5 -5
  47. package/dist/src/components/Text.js +9 -9
  48. package/dist/src/components/UnknownSVGObject.d.ts +4 -4
  49. package/dist/src/components/UnknownSVGObject.js +7 -2
  50. package/dist/src/components/builders/ArrowBuilder.d.ts +1 -1
  51. package/dist/src/components/builders/ArrowBuilder.js +1 -1
  52. package/dist/src/components/builders/FreehandLineBuilder.d.ts +8 -3
  53. package/dist/src/components/builders/FreehandLineBuilder.js +142 -71
  54. package/dist/src/components/builders/LineBuilder.d.ts +1 -1
  55. package/dist/src/components/builders/LineBuilder.js +1 -1
  56. package/dist/src/components/builders/RectangleBuilder.d.ts +1 -1
  57. package/dist/src/components/builders/RectangleBuilder.js +3 -3
  58. package/dist/src/components/builders/types.d.ts +1 -1
  59. package/dist/src/components/lib.d.ts +4 -0
  60. package/dist/src/components/lib.js +4 -0
  61. package/dist/src/lib.d.ts +25 -0
  62. package/dist/src/lib.js +25 -0
  63. package/dist/src/localization.d.ts +1 -0
  64. package/dist/src/localization.js +5 -1
  65. package/dist/src/localizations/es.js +1 -1
  66. package/dist/src/{geometry → math}/LineSegment2.d.ts +0 -0
  67. package/dist/src/{geometry → math}/LineSegment2.js +0 -0
  68. package/dist/src/math/Mat33.d.ts +78 -0
  69. package/dist/src/{geometry → math}/Mat33.js +48 -20
  70. package/dist/src/{geometry → math}/Path.d.ts +2 -1
  71. package/dist/src/{geometry → math}/Path.js +59 -52
  72. package/dist/src/{geometry → math}/Rect2.d.ts +2 -2
  73. package/dist/src/{geometry → math}/Rect2.js +0 -0
  74. package/dist/src/{geometry → math}/Vec2.d.ts +0 -0
  75. package/dist/src/{geometry → math}/Vec2.js +0 -0
  76. package/dist/src/math/Vec3.d.ts +96 -0
  77. package/dist/src/{geometry → math}/Vec3.js +63 -15
  78. package/dist/src/math/lib.d.ts +7 -0
  79. package/dist/src/math/lib.js +7 -0
  80. package/dist/src/math/rounding.d.ts +3 -0
  81. package/dist/src/math/rounding.js +121 -0
  82. package/dist/src/rendering/Display.d.ts +47 -1
  83. package/dist/src/rendering/Display.js +60 -15
  84. package/dist/src/rendering/caching/CacheRecord.d.ts +3 -2
  85. package/dist/src/rendering/caching/CacheRecord.js +4 -1
  86. package/dist/src/rendering/caching/CacheRecordManager.d.ts +5 -4
  87. package/dist/src/rendering/caching/CacheRecordManager.js +16 -4
  88. package/dist/src/rendering/caching/RenderingCache.d.ts +2 -3
  89. package/dist/src/rendering/caching/RenderingCache.js +10 -11
  90. package/dist/src/rendering/caching/RenderingCacheNode.d.ts +2 -1
  91. package/dist/src/rendering/caching/RenderingCacheNode.js +18 -7
  92. package/dist/src/rendering/caching/testUtils.js +1 -1
  93. package/dist/src/rendering/caching/types.d.ts +2 -4
  94. package/dist/src/rendering/localization.d.ts +2 -0
  95. package/dist/src/rendering/localization.js +2 -0
  96. package/dist/src/rendering/renderers/AbstractRenderer.d.ts +4 -4
  97. package/dist/src/rendering/renderers/AbstractRenderer.js +2 -2
  98. package/dist/src/rendering/renderers/CanvasRenderer.d.ts +4 -4
  99. package/dist/src/rendering/renderers/CanvasRenderer.js +2 -2
  100. package/dist/src/rendering/renderers/DummyRenderer.d.ts +4 -4
  101. package/dist/src/rendering/renderers/DummyRenderer.js +1 -1
  102. package/dist/src/rendering/renderers/SVGRenderer.d.ts +3 -3
  103. package/dist/src/rendering/renderers/SVGRenderer.js +8 -2
  104. package/dist/src/rendering/renderers/TextOnlyRenderer.d.ts +5 -3
  105. package/dist/src/rendering/renderers/TextOnlyRenderer.js +13 -3
  106. package/dist/src/toolbar/HTMLToolbar.js +1 -0
  107. package/dist/src/toolbar/icons.d.ts +3 -0
  108. package/dist/src/toolbar/icons.js +142 -132
  109. package/dist/src/toolbar/localization.d.ts +2 -1
  110. package/dist/src/toolbar/localization.js +2 -1
  111. package/dist/src/toolbar/makeColorInput.js +3 -2
  112. package/dist/src/toolbar/widgets/ActionButtonWidget.d.ts +13 -0
  113. package/dist/src/toolbar/widgets/ActionButtonWidget.js +21 -0
  114. package/dist/src/toolbar/widgets/BaseWidget.js +2 -0
  115. package/dist/src/toolbar/widgets/HandToolWidget.js +3 -3
  116. package/dist/src/toolbar/widgets/PenWidget.js +1 -0
  117. package/dist/src/toolbar/widgets/SelectionWidget.d.ts +0 -1
  118. package/dist/src/toolbar/widgets/SelectionWidget.js +23 -30
  119. package/dist/src/tools/Eraser.js +1 -1
  120. package/dist/src/tools/PanZoom.d.ts +1 -1
  121. package/dist/src/tools/PanZoom.js +24 -14
  122. package/dist/src/tools/Pen.d.ts +1 -2
  123. package/dist/src/tools/Pen.js +8 -1
  124. package/dist/src/tools/PipetteTool.js +1 -0
  125. package/dist/src/tools/SelectionTool.d.ts +3 -3
  126. package/dist/src/tools/SelectionTool.js +51 -28
  127. package/dist/src/tools/TextTool.js +1 -1
  128. package/dist/src/types.d.ts +21 -10
  129. package/dist/src/types.js +7 -5
  130. package/firebase.json +16 -0
  131. package/package.json +118 -101
  132. package/src/Color4.ts +23 -2
  133. package/src/Editor.ts +181 -37
  134. package/src/EditorImage.test.ts +2 -4
  135. package/src/EditorImage.ts +46 -28
  136. package/src/EventDispatcher.ts +21 -6
  137. package/src/Pointer.ts +4 -3
  138. package/src/SVGLoader.ts +14 -6
  139. package/src/UndoRedoHistory.ts +18 -2
  140. package/src/Viewport.ts +23 -18
  141. package/src/bundle/bundled.ts +1 -2
  142. package/src/commands/Command.ts +5 -5
  143. package/src/commands/Duplicate.ts +4 -5
  144. package/src/commands/Erase.ts +7 -6
  145. package/src/commands/SerializableCommand.ts +17 -9
  146. package/src/commands/invertCommand.ts +51 -0
  147. package/src/commands/lib.ts +14 -0
  148. package/src/commands/localization.ts +3 -1
  149. package/src/components/AbstractComponent.ts +35 -24
  150. package/src/components/SVGGlobalAttributesObject.ts +11 -4
  151. package/src/components/Stroke.test.ts +4 -6
  152. package/src/components/Stroke.ts +15 -11
  153. package/src/components/Text.test.ts +2 -2
  154. package/src/components/Text.ts +9 -10
  155. package/src/components/UnknownSVGObject.ts +10 -4
  156. package/src/components/builders/ArrowBuilder.ts +2 -2
  157. package/src/components/builders/FreehandLineBuilder.ts +190 -80
  158. package/src/components/builders/LineBuilder.ts +2 -2
  159. package/src/components/builders/RectangleBuilder.ts +3 -3
  160. package/src/components/builders/types.ts +1 -1
  161. package/src/components/lib.ts +9 -0
  162. package/src/lib.ts +28 -0
  163. package/src/localization.ts +6 -0
  164. package/src/localizations/es.ts +2 -1
  165. package/src/{geometry → math}/LineSegment2.test.ts +0 -0
  166. package/src/{geometry → math}/LineSegment2.ts +0 -0
  167. package/src/{geometry → math}/Mat33.test.ts +0 -0
  168. package/src/{geometry → math}/Mat33.ts +48 -20
  169. package/src/{geometry → math}/Path.fromString.test.ts +0 -0
  170. package/src/{geometry → math}/Path.test.ts +0 -0
  171. package/src/{geometry → math}/Path.toString.test.ts +11 -2
  172. package/src/{geometry → math}/Path.ts +61 -58
  173. package/src/{geometry → math}/Rect2.test.ts +0 -0
  174. package/src/{geometry → math}/Rect2.ts +2 -2
  175. package/src/{geometry → math}/Vec2.test.ts +0 -0
  176. package/src/{geometry → math}/Vec2.ts +0 -0
  177. package/src/{geometry → math}/Vec3.test.ts +0 -0
  178. package/src/{geometry → math}/Vec3.ts +64 -16
  179. package/src/math/lib.ts +15 -0
  180. package/src/math/rounding.test.ts +40 -0
  181. package/src/math/rounding.ts +147 -0
  182. package/src/rendering/Display.ts +63 -15
  183. package/src/rendering/caching/CacheRecord.test.ts +3 -3
  184. package/src/rendering/caching/CacheRecord.ts +6 -2
  185. package/src/rendering/caching/CacheRecordManager.ts +34 -8
  186. package/src/rendering/caching/RenderingCache.test.ts +3 -3
  187. package/src/rendering/caching/RenderingCache.ts +11 -16
  188. package/src/rendering/caching/RenderingCacheNode.ts +23 -7
  189. package/src/rendering/caching/testUtils.ts +1 -1
  190. package/src/rendering/caching/types.ts +2 -7
  191. package/src/rendering/localization.ts +4 -0
  192. package/src/rendering/renderers/AbstractRenderer.ts +4 -4
  193. package/src/rendering/renderers/CanvasRenderer.ts +5 -5
  194. package/src/rendering/renderers/DummyRenderer.test.ts +2 -2
  195. package/src/rendering/renderers/DummyRenderer.ts +4 -4
  196. package/src/rendering/renderers/SVGRenderer.ts +10 -4
  197. package/src/rendering/renderers/TextOnlyRenderer.ts +17 -6
  198. package/src/toolbar/HTMLToolbar.ts +1 -0
  199. package/src/toolbar/icons.ts +157 -137
  200. package/src/toolbar/localization.ts +4 -2
  201. package/src/toolbar/makeColorInput.ts +3 -2
  202. package/src/toolbar/toolbar.css +1 -1
  203. package/src/toolbar/widgets/ActionButtonWidget.ts +31 -0
  204. package/src/toolbar/widgets/BaseWidget.ts +2 -0
  205. package/src/toolbar/widgets/HandToolWidget.ts +3 -3
  206. package/src/toolbar/widgets/PenWidget.ts +2 -0
  207. package/src/toolbar/widgets/SelectionWidget.ts +46 -41
  208. package/src/tools/Eraser.ts +2 -2
  209. package/src/tools/PanZoom.ts +28 -17
  210. package/src/tools/Pen.ts +11 -2
  211. package/src/tools/PipetteTool.ts +2 -0
  212. package/src/tools/SelectionTool.test.ts +2 -4
  213. package/src/tools/SelectionTool.ts +52 -24
  214. package/src/tools/TextTool.ts +2 -2
  215. package/src/tools/UndoRedoShortcut.test.ts +1 -1
  216. package/src/types.ts +23 -7
  217. package/tsconfig.json +4 -1
  218. package/typedoc.json +20 -0
  219. package/dist/src/geometry/Mat33.d.ts +0 -32
  220. package/dist/src/geometry/Vec3.d.ts +0 -34
@@ -1,5 +1,5 @@
1
1
  import EventDispatcher from '../EventDispatcher';
2
- import { Vec2 } from '../geometry/Vec2';
2
+ import { Vec2 } from '../math/Vec2';
3
3
  import SVGRenderer from '../rendering/renderers/SVGRenderer';
4
4
  import Viewport from '../Viewport';
5
5
  const svgNamespace = 'http://www.w3.org/2000/svg';
@@ -88,147 +88,130 @@ export const makeSelectionIcon = () => {
88
88
  icon.setAttribute('viewBox', '0 0 100 100');
89
89
  return icon;
90
90
  };
91
- export const makeHandToolIcon = () => {
91
+ const pathIcon = (pathData, fill = 'var(--icon-color)', strokeColor = 'none', strokeWidth = '0px') => {
92
92
  const icon = document.createElementNS(svgNamespace, 'svg');
93
- // Draw a cursor-like shape (like some of the other icons, made with Inkscape)
94
- icon.innerHTML = `
95
- <g>
96
- <path d='
97
- m 10,60
98
- 5,30
99
- H 90
100
- V 30
101
- C 90,20 75,20 75,30
102
- V 60
103
- 20
104
- C 75,10 60,10 60,20
105
- V 60
106
- 15
107
- C 60,5 45,5 45,15
108
- V 60
109
- 25
110
- C 45,15 30,15 30,25
111
- V 60
112
- 75
113
- L 25,60
114
- C 20,45 10,50 10,60
115
- Z'
116
-
117
- fill='none'
118
- style='
119
- stroke: var(--icon-color);
120
- stroke-width: 2;
121
- '
122
- />
123
- </g>
124
- `;
93
+ const path = document.createElementNS(svgNamespace, 'path');
94
+ path.setAttribute('d', pathData);
95
+ path.style.fill = fill;
96
+ path.style.stroke = strokeColor;
97
+ path.style.strokeWidth = strokeWidth;
98
+ icon.appendChild(path);
125
99
  icon.setAttribute('viewBox', '0 0 100 100');
126
100
  return icon;
127
101
  };
102
+ export const makeHandToolIcon = () => {
103
+ const fill = 'none';
104
+ const strokeColor = 'var(--icon-color)';
105
+ const strokeWidth = '3';
106
+ // Draw a cursor-like shape (like some of the other icons, made with Inkscape)
107
+ return pathIcon(`
108
+ m 10,60
109
+ 5,30
110
+ H 90
111
+ V 30
112
+ C 90,20 75,20 75,30
113
+ V 60
114
+ 20
115
+ C 75,10 60,10 60,20
116
+ V 60
117
+ 15
118
+ C 60,5 45,5 45,15
119
+ V 60
120
+ 25
121
+ C 45,15 30,15 30,25
122
+ V 60
123
+ 75
124
+ L 25,60
125
+ C 20,45 10,50 10,60
126
+ Z
127
+ `, fill, strokeColor, strokeWidth);
128
+ };
128
129
  export const makeTouchPanningIcon = () => {
129
- const icon = document.createElementNS(svgNamespace, 'svg');
130
- icon.innerHTML = `
131
- <path
132
- d='
133
- M 5,5.5
134
- V 17.2
135
- L 16.25,5.46
136
- Z
130
+ const fill = 'none';
131
+ const strokeColor = 'var(--icon-color)';
132
+ const strokeWidth = '3';
133
+ return pathIcon(`
134
+ M 5,5.5
135
+ V 17.2
136
+ L 16.25,5.46
137
+ Z
137
138
 
138
- m 33.75,0
139
- L 50,17
140
- V 5.5
141
- Z
139
+ m 33.75,0
140
+ L 50,17
141
+ V 5.5
142
+ Z
142
143
 
143
- M 5,40.7
144
- v 11.7
145
- h 11.25
146
- z
147
-
148
- M 26,19
149
- C 19.8,19.4 17.65,30.4 21.9,34.8
150
- L 50,70
151
- H 27.5
152
- c -11.25,0 -11.25,17.6 0,17.6
153
- H 61.25
154
- C 94.9,87.8 95,87.6 95,40.7 78.125,23 67,29 55.6,46.5
155
- L 33.1,23
156
- C 30.3125,20.128192 27.9,19 25.830078,19.119756
157
- Z
158
- '
159
- fill='none'
160
- style='
161
- stroke: var(--icon-color);
162
- stroke-width: 2;
163
- '
164
- />
165
- `;
166
- icon.setAttribute('viewBox', '0 0 100 100');
167
- return icon;
144
+ M 5,40.7
145
+ v 11.7
146
+ h 11.25
147
+ z
148
+
149
+ M 26,19
150
+ C 19.8,19.4 17.65,30.4 21.9,34.8
151
+ L 50,70
152
+ H 27.5
153
+ c -11.25,0 -11.25,17.6 0,17.6
154
+ H 61.25
155
+ C 94.9,87.8 95,87.6 95,40.7 78.125,23 67,29 55.6,46.5
156
+ L 33.1,23
157
+ C 30.3125,20.128192 27.9,19 25.830078,19.119756
158
+ Z
159
+ `, fill, strokeColor, strokeWidth);
168
160
  };
169
161
  export const makeAllDevicePanningIcon = () => {
170
- const icon = document.createElementNS(svgNamespace, 'svg');
171
- icon.innerHTML = `
172
- <path
173
- d='
174
- M 5 5
175
- L 5 17.5
176
- 17.5 5
177
- 5 5
178
- z
179
-
180
- M 42.5 5
181
- L 55 17.5
182
- 55 5
183
- 42.5 5
184
- z
185
-
186
- M 70 10
187
- L 70 21
188
- 61 15
189
- 55.5 23
190
- 66 30
191
- 56 37
192
- 61 45
193
- 70 39
194
- 70 50
195
- 80 50
196
- 80 39
197
- 89 45
198
- 95 36
199
- 84 30
200
- 95 23
201
- 89 15
202
- 80 21
203
- 80 10
204
- 70 10
205
- z
162
+ const fill = 'none';
163
+ const strokeColor = 'var(--icon-color)';
164
+ const strokeWidth = '3';
165
+ return pathIcon(`
166
+ M 5 5
167
+ L 5 17.5
168
+ 17.5 5
169
+ 5 5
170
+ z
206
171
 
207
- M 27.5 26.25
208
- L 27.5 91.25
209
- L 43.75 83.125
210
- L 52 99
211
- L 68 91
212
- L 60 75
213
- L 76.25 66.875
214
- L 27.5 26.25
215
- z
216
-
217
- M 5 42.5
218
- L 5 55
219
- L 17.5 55
220
- L 5 42.5
221
- z
222
- '
223
- fill='none'
224
- style='
225
- stroke: var(--icon-color);
226
- stroke-width: 2;
227
- '
228
- />
229
- `;
230
- icon.setAttribute('viewBox', '0 0 100 100');
231
- return icon;
172
+ M 42.5 5
173
+ L 55 17.5
174
+ 55 5
175
+ 42.5 5
176
+ z
177
+
178
+ M 70 10
179
+ L 70 21
180
+ 61 15
181
+ 55.5 23
182
+ 66 30
183
+ 56 37
184
+ 61 45
185
+ 70 39
186
+ 70 50
187
+ 80 50
188
+ 80 39
189
+ 89 45
190
+ 95 36
191
+ 84 30
192
+ 95 23
193
+ 89 15
194
+ 80 21
195
+ 80 10
196
+ 70 10
197
+ z
198
+
199
+ M 27.5 26.25
200
+ L 27.5 91.25
201
+ L 43.75 83.125
202
+ L 52 99
203
+ L 68 91
204
+ L 60 75
205
+ L 76.25 66.875
206
+ L 27.5 26.25
207
+ z
208
+
209
+ M 5 42.5
210
+ L 5 55
211
+ L 17.5 55
212
+ L 5 42.5
213
+ z
214
+ `, fill, strokeColor, strokeWidth);
232
215
  };
233
216
  export const makeZoomIcon = () => {
234
217
  const icon = document.createElementNS(svgNamespace, 'svg');
@@ -369,3 +352,30 @@ export const makePipetteIcon = (color) => {
369
352
  icon.setAttribute('viewBox', '0 0 100 100');
370
353
  return icon;
371
354
  };
355
+ export const makeResizeViewportIcon = () => {
356
+ return pathIcon(`
357
+ M 75 5 75 10 90 10 90 25 95 25 95 5 75 5 z
358
+ M 15 15 15 30 20 30 20 20 30 20 30 15 15 15 z
359
+ M 84 15 82 17 81 16 81 20 85 20 84 19 86 17 84 15 z
360
+ M 26 24 24 26 26 28 25 29 29 29 29 25 28 26 26 24 z
361
+ M 25 71 26 72 24 74 26 76 28 74 29 75 29 71 25 71 z
362
+ M 15 75 15 85 25 85 25 80 20 80 20 75 15 75 z
363
+ M 90 75 90 90 75 90 75 95 95 95 95 75 90 75 z
364
+ M 81 81 81 85 82 84 84 86 86 84 84 82 85 81 81 81 z
365
+ `);
366
+ };
367
+ export const makeDuplicateSelectionIcon = () => {
368
+ return pathIcon(`
369
+ M 45,10 45,55 90,55 90,10 45,10 z
370
+ M 10,25 10,90 70,90 70,60 40,60 40,25 10,25 z
371
+ `);
372
+ };
373
+ export const makeDeleteSelectionIcon = () => {
374
+ const strokeWidth = '5px';
375
+ const strokeColor = 'var(--icon-color)';
376
+ const fillColor = 'none';
377
+ return pathIcon(`
378
+ M 10,10 90,90
379
+ M 10,90 90,10
380
+ `, fillColor, strokeColor, strokeWidth);
381
+ };
@@ -17,7 +17,8 @@ export interface ToolbarLocalization {
17
17
  resizeImageToSelection: string;
18
18
  deleteSelection: string;
19
19
  duplicateSelection: string;
20
- pickColorFronScreen: string;
20
+ pickColorFromScreen: string;
21
+ clickToPickColorAnnouncement: string;
21
22
  undo: string;
22
23
  redo: string;
23
24
  zoom: string;
@@ -14,7 +14,8 @@ export const defaultToolbarLocalization = {
14
14
  undo: 'Undo',
15
15
  redo: 'Redo',
16
16
  selectObjectType: 'Object type: ',
17
- pickColorFronScreen: 'Pick color from screen',
17
+ pickColorFromScreen: 'Pick color from screen',
18
+ clickToPickColorAnnouncement: 'Click on the screen to pick a color',
18
19
  selectionToolKeyboardShortcuts: 'Selection tool: Use arrow keys to move selected items, lowercase/uppercase ‘i’ and ‘o’ to resize.',
19
20
  touchPanning: 'Touchscreen panning',
20
21
  anyDevicePanning: 'Any device panning',
@@ -2,7 +2,7 @@ import Color4 from '../Color4';
2
2
  import { ToolType } from '../tools/ToolController';
3
3
  import { EditorEventType } from '../types';
4
4
  import { makePipetteIcon } from './icons';
5
- // Returns [ input, container ].
5
+ // Returns [ color input, input container ].
6
6
  export const makeColorInput = (editor, onColorChange) => {
7
7
  const colorInputContainer = document.createElement('span');
8
8
  const colorInput = document.createElement('input');
@@ -53,7 +53,7 @@ export const makeColorInput = (editor, onColorChange) => {
53
53
  const addPipetteTool = (editor, container, onColorChange) => {
54
54
  const pipetteButton = document.createElement('button');
55
55
  pipetteButton.classList.add('pipetteButton');
56
- pipetteButton.title = editor.localization.pickColorFronScreen;
56
+ pipetteButton.title = editor.localization.pickColorFromScreen;
57
57
  pipetteButton.setAttribute('alt', pipetteButton.title);
58
58
  const updatePipetteIcon = (color) => {
59
59
  pipetteButton.replaceChildren(makePipetteIcon(color));
@@ -88,6 +88,7 @@ const addPipetteTool = (editor, container, onColorChange) => {
88
88
  pipetteTool === null || pipetteTool === void 0 ? void 0 : pipetteTool.setColorListener(pipetteColorPreview, pipetteColorSelect);
89
89
  if (pipetteTool) {
90
90
  pipetteButton.classList.add('active');
91
+ editor.announceForAccessibility(editor.localization.clickToPickColorAnnouncement);
91
92
  }
92
93
  };
93
94
  container.appendChild(pipetteButton);
@@ -0,0 +1,13 @@
1
+ import Editor from '../../Editor';
2
+ import { ToolbarLocalization } from '../localization';
3
+ import BaseWidget from './BaseWidget';
4
+ export default class ActionButtonWidget extends BaseWidget {
5
+ protected makeIcon: () => Element;
6
+ protected title: string;
7
+ protected clickAction: () => void;
8
+ constructor(editor: Editor, localizationTable: ToolbarLocalization, makeIcon: () => Element, title: string, clickAction: () => void);
9
+ protected handleClick(): void;
10
+ protected getTitle(): string;
11
+ protected createIcon(): Element;
12
+ protected fillDropdown(_dropdown: HTMLElement): boolean;
13
+ }
@@ -0,0 +1,21 @@
1
+ import BaseWidget from './BaseWidget';
2
+ export default class ActionButtonWidget extends BaseWidget {
3
+ constructor(editor, localizationTable, makeIcon, title, clickAction) {
4
+ super(editor, localizationTable);
5
+ this.makeIcon = makeIcon;
6
+ this.title = title;
7
+ this.clickAction = clickAction;
8
+ }
9
+ handleClick() {
10
+ this.clickAction();
11
+ }
12
+ getTitle() {
13
+ return this.title;
14
+ }
15
+ createIcon() {
16
+ return this.makeIcon();
17
+ }
18
+ fillDropdown(_dropdown) {
19
+ return false;
20
+ }
21
+ }
@@ -114,9 +114,11 @@ export default class BaseWidget {
114
114
  this.disabled = disabled;
115
115
  if (this.disabled) {
116
116
  this.button.classList.add('disabled');
117
+ this.button.setAttribute('aria-disabled', 'true');
117
118
  }
118
119
  else {
119
120
  this.button.classList.remove('disabled');
121
+ this.button.removeAttribute('aria-disabled');
120
122
  }
121
123
  }
122
124
  setSelected(selected) {
@@ -1,4 +1,4 @@
1
- import Mat33 from '../../geometry/Mat33';
1
+ import Mat33 from '../../math/Mat33';
2
2
  import { PanZoomMode } from '../../tools/PanZoom';
3
3
  import { EditorEventType } from '../../types';
4
4
  import Viewport from '../../Viewport';
@@ -43,7 +43,7 @@ const makeZoomControl = (localizationTable, editor) => {
43
43
  const zoomBy = (factor) => {
44
44
  const screenCenter = editor.viewport.visibleRect.center;
45
45
  const transformUpdate = Mat33.scaling2D(factor, screenCenter);
46
- editor.dispatch(new Viewport.ViewportTransform(transformUpdate), false);
46
+ editor.dispatch(Viewport.transformBy(transformUpdate), false);
47
47
  };
48
48
  increaseButton.onclick = () => {
49
49
  zoomBy(5.0 / 4);
@@ -52,7 +52,7 @@ const makeZoomControl = (localizationTable, editor) => {
52
52
  zoomBy(4.0 / 5);
53
53
  };
54
54
  resetViewButton.onclick = () => {
55
- editor.dispatch(new Viewport.ViewportTransform(editor.viewport.canvasToScreenTransform.inverse()), true);
55
+ editor.dispatch(Viewport.transformBy(editor.viewport.canvasToScreenTransform.inverse()), true);
56
56
  };
57
57
  return zoomLevelRow;
58
58
  };
@@ -1,3 +1,4 @@
1
+ // @internal @packageDocumentation
1
2
  import { makeArrowBuilder } from '../../components/builders/ArrowBuilder';
2
3
  import { makeFreehandLineBuilder } from '../../components/builders/FreehandLineBuilder';
3
4
  import { makeLineBuilder } from '../../components/builders/LineBuilder';
@@ -7,5 +7,4 @@ export declare class SelectionWidget extends BaseToolWidget {
7
7
  constructor(editor: Editor, tool: SelectionTool, localization: ToolbarLocalization);
8
8
  protected getTitle(): string;
9
9
  protected createIcon(): Element;
10
- protected fillDropdown(dropdown: HTMLElement): boolean;
11
10
  }
@@ -1,41 +1,33 @@
1
1
  import { EditorEventType } from '../../types';
2
- import { makeSelectionIcon } from '../icons';
2
+ import { makeDeleteSelectionIcon, makeDuplicateSelectionIcon, makeResizeViewportIcon, makeSelectionIcon } from '../icons';
3
+ import ActionButtonWidget from './ActionButtonWidget';
3
4
  import BaseToolWidget from './BaseToolWidget';
4
5
  export class SelectionWidget extends BaseToolWidget {
5
6
  constructor(editor, tool, localization) {
6
7
  super(editor, tool, localization);
7
8
  this.tool = tool;
8
- }
9
- getTitle() {
10
- return this.localizationTable.select;
11
- }
12
- createIcon() {
13
- return makeSelectionIcon();
14
- }
15
- fillDropdown(dropdown) {
16
- const container = document.createElement('div');
17
- const resizeButton = document.createElement('button');
18
- const duplicateButton = document.createElement('button');
19
- const deleteButton = document.createElement('button');
20
- resizeButton.innerText = this.localizationTable.resizeImageToSelection;
21
- resizeButton.disabled = true;
22
- deleteButton.innerText = this.localizationTable.deleteSelection;
23
- deleteButton.disabled = true;
24
- duplicateButton.innerText = this.localizationTable.duplicateSelection;
25
- duplicateButton.disabled = true;
26
- resizeButton.onclick = () => {
9
+ const resizeButton = new ActionButtonWidget(editor, localization, makeResizeViewportIcon, this.localizationTable.resizeImageToSelection, () => {
27
10
  const selection = this.tool.getSelection();
28
11
  this.editor.dispatch(this.editor.setImportExportRect(selection.region));
29
- };
30
- deleteButton.onclick = () => {
12
+ });
13
+ const deleteButton = new ActionButtonWidget(editor, localization, makeDeleteSelectionIcon, this.localizationTable.deleteSelection, () => {
31
14
  const selection = this.tool.getSelection();
32
15
  this.editor.dispatch(selection.deleteSelectedObjects());
33
16
  this.tool.clearSelection();
34
- };
35
- duplicateButton.onclick = () => {
17
+ });
18
+ const duplicateButton = new ActionButtonWidget(editor, localization, makeDuplicateSelectionIcon, this.localizationTable.duplicateSelection, () => {
36
19
  const selection = this.tool.getSelection();
37
20
  this.editor.dispatch(selection.duplicateSelectedObjects());
21
+ });
22
+ this.addSubWidget(resizeButton);
23
+ this.addSubWidget(deleteButton);
24
+ this.addSubWidget(duplicateButton);
25
+ const updateDisabled = (disabled) => {
26
+ resizeButton.setDisabled(disabled);
27
+ deleteButton.setDisabled(disabled);
28
+ duplicateButton.setDisabled(disabled);
38
29
  };
30
+ updateDisabled(true);
39
31
  // Enable/disable actions based on whether items are selected
40
32
  this.editor.notifier.on(EditorEventType.ToolUpdated, toolEvt => {
41
33
  if (toolEvt.kind !== EditorEventType.ToolUpdated) {
@@ -44,13 +36,14 @@ export class SelectionWidget extends BaseToolWidget {
44
36
  if (toolEvt.tool === this.tool) {
45
37
  const selection = this.tool.getSelection();
46
38
  const hasSelection = selection && selection.region.area > 0;
47
- resizeButton.disabled = !hasSelection;
48
- deleteButton.disabled = resizeButton.disabled;
49
- duplicateButton.disabled = resizeButton.disabled;
39
+ updateDisabled(!hasSelection);
50
40
  }
51
41
  });
52
- container.replaceChildren(resizeButton, duplicateButton, deleteButton);
53
- dropdown.appendChild(container);
54
- return true;
42
+ }
43
+ getTitle() {
44
+ return this.localizationTable.select;
45
+ }
46
+ createIcon() {
47
+ return makeSelectionIcon();
55
48
  }
56
49
  }
@@ -1,5 +1,5 @@
1
1
  import BaseTool from './BaseTool';
2
- import LineSegment2 from '../geometry/LineSegment2';
2
+ import LineSegment2 from '../math/LineSegment2';
3
3
  import Erase from '../commands/Erase';
4
4
  import { ToolType } from './ToolController';
5
5
  import { PointerDevice } from '../Pointer';
@@ -1,5 +1,5 @@
1
1
  import { Editor } from '../Editor';
2
- import { Point2 } from '../geometry/Vec2';
2
+ import { Point2 } from '../math/Vec2';
3
3
  import Pointer from '../Pointer';
4
4
  import { KeyPressEvent, PointerEvt, WheelEvt } from '../types';
5
5
  import BaseTool from './BaseTool';
@@ -1,6 +1,6 @@
1
- import Mat33 from '../geometry/Mat33';
2
- import { Vec2 } from '../geometry/Vec2';
3
- import Vec3 from '../geometry/Vec3';
1
+ import Mat33 from '../math/Mat33';
2
+ import { Vec2 } from '../math/Vec2';
3
+ import Vec3 from '../math/Vec3';
4
4
  import { PointerDevice } from '../Pointer';
5
5
  import { EditorEventType } from '../types';
6
6
  import { Viewport } from '../Viewport';
@@ -53,7 +53,7 @@ export default class PanZoom extends BaseTool {
53
53
  handlingGesture = true;
54
54
  }
55
55
  if (handlingGesture) {
56
- (_a = this.transform) !== null && _a !== void 0 ? _a : (this.transform = new Viewport.ViewportTransform(Mat33.identity));
56
+ (_a = this.transform) !== null && _a !== void 0 ? _a : (this.transform = Viewport.transformBy(Mat33.identity));
57
57
  this.editor.display.setDraftMode(true);
58
58
  }
59
59
  return handlingGesture;
@@ -74,16 +74,16 @@ export default class PanZoom extends BaseTool {
74
74
  this.lastScreenCenter = screenCenter;
75
75
  this.lastDist = dist;
76
76
  this.lastAngle = angle;
77
- this.transform = new Viewport.ViewportTransform(this.transform.transform.rightMul(transformUpdate));
77
+ this.transform = Viewport.transformBy(this.transform.transform.rightMul(transformUpdate));
78
78
  }
79
79
  handleOneFingerMove(pointer) {
80
80
  const delta = this.getCenterDelta(pointer.screenPos);
81
- this.transform = new Viewport.ViewportTransform(this.transform.transform.rightMul(Mat33.translation(delta)));
81
+ this.transform = Viewport.transformBy(this.transform.transform.rightMul(Mat33.translation(delta)));
82
82
  this.lastScreenCenter = pointer.screenPos;
83
83
  }
84
84
  onPointerMove({ allPointers }) {
85
85
  var _a;
86
- (_a = this.transform) !== null && _a !== void 0 ? _a : (this.transform = new Viewport.ViewportTransform(Mat33.identity));
86
+ (_a = this.transform) !== null && _a !== void 0 ? _a : (this.transform = Viewport.transformBy(Mat33.identity));
87
87
  const lastTransform = this.transform;
88
88
  if (allPointers.length === 2) {
89
89
  this.handleTwoFingerMove(allPointers);
@@ -110,27 +110,30 @@ export default class PanZoom extends BaseTool {
110
110
  }
111
111
  // Applies [transformUpdate] to the editor. This stacks on top of the
112
112
  // current transformation, if it exists.
113
- updateTransform(transformUpdate) {
113
+ updateTransform(transformUpdate, announce = false) {
114
114
  var _a;
115
115
  let newTransform = transformUpdate;
116
116
  if (this.transform) {
117
117
  newTransform = this.transform.transform.rightMul(transformUpdate);
118
118
  }
119
119
  (_a = this.transform) === null || _a === void 0 ? void 0 : _a.unapply(this.editor);
120
- this.transform = new Viewport.ViewportTransform(newTransform);
120
+ this.transform = Viewport.transformBy(newTransform);
121
121
  this.transform.apply(this.editor);
122
+ if (announce) {
123
+ this.editor.announceForAccessibility(this.transform.description(this.editor, this.editor.localization));
124
+ }
122
125
  }
123
126
  onWheel({ delta, screenPos }) {
124
127
  // Reset the transformation -- wheel events are individual events, so we don't
125
128
  // need to unapply/reapply.
126
- this.transform = new Viewport.ViewportTransform(Mat33.identity);
129
+ this.transform = Viewport.transformBy(Mat33.identity);
127
130
  const canvasPos = this.editor.viewport.screenToCanvas(screenPos);
128
131
  const toCanvas = this.editor.viewport.screenToCanvasTransform;
129
132
  // Transform without including translation
130
133
  const translation = toCanvas.transformVec3(Vec3.of(-delta.x, -delta.y, 0));
131
134
  const pinchZoomScaleFactor = 1.04;
132
135
  const transformUpdate = Mat33.scaling2D(Math.max(0.25, Math.min(Math.pow(pinchZoomScaleFactor, -delta.z), 4)), canvasPos).rightMul(Mat33.translation(translation));
133
- this.updateTransform(transformUpdate);
136
+ this.updateTransform(transformUpdate, true);
134
137
  return true;
135
138
  }
136
139
  onKeyPress({ key }) {
@@ -138,7 +141,7 @@ export default class PanZoom extends BaseTool {
138
141
  return false;
139
142
  }
140
143
  // No need to keep the same the transform for keyboard events.
141
- this.transform = new Viewport.ViewportTransform(Mat33.identity);
144
+ this.transform = Viewport.transformBy(Mat33.identity);
142
145
  let translation = Vec2.zero;
143
146
  let scale = 1;
144
147
  let rotation = 0;
@@ -154,10 +157,12 @@ export default class PanZoom extends BaseTool {
154
157
  case 'ArrowRight':
155
158
  translation = Vec2.of(1, 0);
156
159
  break;
160
+ case 'q':
157
161
  case 'k':
158
162
  case 'ArrowUp':
159
163
  translation = Vec2.of(0, -1);
160
164
  break;
165
+ case 'e':
161
166
  case 'j':
162
167
  case 'ArrowDown':
163
168
  translation = Vec2.of(0, 1);
@@ -179,11 +184,16 @@ export default class PanZoom extends BaseTool {
179
184
  }
180
185
  // For each keypress,
181
186
  translation = translation.times(30); // Move at most 30 units
182
- rotation *= Math.PI / 8; // Rotate at most a sixteenth of a rotation
187
+ rotation *= Math.PI / 8; // Rotate at least a sixteenth of a rotation
183
188
  // Transform the canvas, not the viewport:
184
189
  translation = translation.times(-1);
185
190
  rotation = rotation * -1;
186
191
  scale = 1 / scale;
192
+ // Work around an issue that seems to be related to rotation matricies losing precision on inversion.
193
+ // TODO: Figure out why and implement a better solution.
194
+ if (rotation !== 0) {
195
+ rotation += 0.0001;
196
+ }
187
197
  const toCanvas = this.editor.viewport.screenToCanvasTransform;
188
198
  // Transform without translating (treat toCanvas as a linear instead of
189
199
  // an affine transformation).
@@ -191,7 +201,7 @@ export default class PanZoom extends BaseTool {
191
201
  // Rotate/scale about the center of the canvas
192
202
  const transformCenter = this.editor.viewport.visibleRect.center;
193
203
  const transformUpdate = Mat33.scaling2D(scale, transformCenter).rightMul(Mat33.zRotation(rotation, transformCenter)).rightMul(Mat33.translation(translation));
194
- this.updateTransform(transformUpdate);
204
+ this.updateTransform(transformUpdate, true);
195
205
  return true;
196
206
  }
197
207
  setMode(mode) {
@@ -4,7 +4,7 @@ import { PointerEvt } from '../types';
4
4
  import BaseTool from './BaseTool';
5
5
  import { ToolType } from './ToolController';
6
6
  import { ComponentBuilderFactory } from '../components/builders/types';
7
- interface PenStyle {
7
+ export interface PenStyle {
8
8
  color: Color4;
9
9
  thickness: number;
10
10
  }
@@ -32,4 +32,3 @@ export default class Pen extends BaseTool {
32
32
  getColor(): Color4;
33
33
  getStrokeFactory(): ComponentBuilderFactory;
34
34
  }
35
- export {};