js-draw 0.1.9 → 0.1.12

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 (193) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +15 -3
  3. package/dist/bundle.js +1 -1
  4. package/dist/src/Editor.d.ts +7 -2
  5. package/dist/src/Editor.js +74 -25
  6. package/dist/src/EditorImage.d.ts +1 -1
  7. package/dist/src/EditorImage.js +2 -2
  8. package/dist/src/Pointer.d.ts +1 -1
  9. package/dist/src/Pointer.js +1 -1
  10. package/dist/src/SVGLoader.d.ts +1 -1
  11. package/dist/src/SVGLoader.js +14 -6
  12. package/dist/src/UndoRedoHistory.js +3 -0
  13. package/dist/src/Viewport.d.ts +8 -25
  14. package/dist/src/Viewport.js +17 -10
  15. package/dist/src/bundle/bundled.d.ts +2 -1
  16. package/dist/src/bundle/bundled.js +2 -1
  17. package/dist/src/commands/Command.d.ts +2 -2
  18. package/dist/src/commands/Command.js +4 -4
  19. package/dist/src/commands/Duplicate.d.ts +1 -1
  20. package/dist/src/commands/Duplicate.js +1 -1
  21. package/dist/src/commands/Erase.d.ts +1 -1
  22. package/dist/src/commands/Erase.js +1 -1
  23. package/dist/src/commands/localization.d.ts +1 -1
  24. package/dist/src/components/AbstractComponent.d.ts +3 -3
  25. package/dist/src/components/AbstractComponent.js +2 -2
  26. package/dist/src/components/SVGGlobalAttributesObject.d.ts +3 -3
  27. package/dist/src/components/SVGGlobalAttributesObject.js +1 -1
  28. package/dist/src/components/Stroke.d.ts +4 -4
  29. package/dist/src/components/Stroke.js +2 -2
  30. package/dist/src/components/Text.d.ts +3 -3
  31. package/dist/src/components/Text.js +3 -3
  32. package/dist/src/components/UnknownSVGObject.d.ts +3 -3
  33. package/dist/src/components/UnknownSVGObject.js +1 -1
  34. package/dist/src/components/builders/ArrowBuilder.d.ts +1 -1
  35. package/dist/src/components/builders/ArrowBuilder.js +1 -1
  36. package/dist/src/components/builders/FreehandLineBuilder.d.ts +8 -3
  37. package/dist/src/components/builders/FreehandLineBuilder.js +142 -71
  38. package/dist/src/components/builders/LineBuilder.d.ts +1 -1
  39. package/dist/src/components/builders/LineBuilder.js +1 -1
  40. package/dist/src/components/builders/RectangleBuilder.d.ts +1 -1
  41. package/dist/src/components/builders/RectangleBuilder.js +3 -3
  42. package/dist/src/components/builders/types.d.ts +1 -1
  43. package/dist/src/localization.d.ts +1 -0
  44. package/dist/src/localization.js +5 -1
  45. package/dist/src/localizations/en.d.ts +3 -0
  46. package/dist/src/localizations/en.js +4 -0
  47. package/dist/src/localizations/es.d.ts +3 -0
  48. package/dist/src/localizations/es.js +18 -0
  49. package/dist/src/localizations/getLocalizationTable.d.ts +3 -0
  50. package/dist/src/localizations/getLocalizationTable.js +43 -0
  51. package/dist/src/{geometry → math}/LineSegment2.d.ts +1 -0
  52. package/dist/src/{geometry → math}/LineSegment2.js +16 -0
  53. package/dist/src/{geometry → math}/Mat33.d.ts +0 -0
  54. package/dist/src/{geometry → math}/Mat33.js +0 -0
  55. package/dist/src/{geometry → math}/Path.d.ts +2 -1
  56. package/dist/src/{geometry → math}/Path.js +58 -51
  57. package/dist/src/{geometry → math}/Rect2.d.ts +1 -0
  58. package/dist/src/{geometry → math}/Rect2.js +16 -0
  59. package/dist/src/{geometry → math}/Vec2.d.ts +0 -0
  60. package/dist/src/{geometry → math}/Vec2.js +0 -0
  61. package/dist/src/{geometry → math}/Vec3.d.ts +1 -1
  62. package/dist/src/{geometry → math}/Vec3.js +1 -1
  63. package/dist/src/math/rounding.d.ts +3 -0
  64. package/dist/src/math/rounding.js +120 -0
  65. package/dist/src/rendering/Display.d.ts +3 -1
  66. package/dist/src/rendering/Display.js +16 -10
  67. package/dist/src/rendering/caching/CacheRecord.d.ts +2 -2
  68. package/dist/src/rendering/caching/CacheRecord.js +1 -1
  69. package/dist/src/rendering/caching/CacheRecordManager.d.ts +1 -1
  70. package/dist/src/rendering/caching/RenderingCache.js +1 -1
  71. package/dist/src/rendering/caching/RenderingCacheNode.d.ts +2 -1
  72. package/dist/src/rendering/caching/RenderingCacheNode.js +18 -7
  73. package/dist/src/rendering/caching/testUtils.js +1 -1
  74. package/dist/src/rendering/caching/types.d.ts +1 -1
  75. package/dist/src/rendering/localization.d.ts +2 -0
  76. package/dist/src/rendering/localization.js +2 -0
  77. package/dist/src/rendering/renderers/AbstractRenderer.d.ts +4 -4
  78. package/dist/src/rendering/renderers/AbstractRenderer.js +2 -2
  79. package/dist/src/rendering/renderers/CanvasRenderer.d.ts +4 -4
  80. package/dist/src/rendering/renderers/CanvasRenderer.js +1 -1
  81. package/dist/src/rendering/renderers/DummyRenderer.d.ts +4 -4
  82. package/dist/src/rendering/renderers/DummyRenderer.js +1 -1
  83. package/dist/src/rendering/renderers/SVGRenderer.d.ts +3 -3
  84. package/dist/src/rendering/renderers/SVGRenderer.js +8 -2
  85. package/dist/src/rendering/renderers/TextOnlyRenderer.d.ts +5 -3
  86. package/dist/src/rendering/renderers/TextOnlyRenderer.js +13 -3
  87. package/dist/src/toolbar/HTMLToolbar.js +5 -7
  88. package/dist/src/toolbar/icons.d.ts +3 -0
  89. package/dist/src/toolbar/icons.js +152 -142
  90. package/dist/src/toolbar/localization.d.ts +4 -1
  91. package/dist/src/toolbar/localization.js +4 -1
  92. package/dist/src/toolbar/makeColorInput.js +2 -1
  93. package/dist/src/toolbar/widgets/ActionButtonWidget.d.ts +13 -0
  94. package/dist/src/toolbar/widgets/ActionButtonWidget.js +21 -0
  95. package/dist/src/toolbar/widgets/BaseWidget.js +31 -0
  96. package/dist/src/toolbar/widgets/HandToolWidget.js +10 -3
  97. package/dist/src/toolbar/widgets/SelectionWidget.d.ts +0 -1
  98. package/dist/src/toolbar/widgets/SelectionWidget.js +23 -30
  99. package/dist/src/tools/BaseTool.d.ts +2 -1
  100. package/dist/src/tools/BaseTool.js +3 -0
  101. package/dist/src/tools/Eraser.js +1 -1
  102. package/dist/src/tools/PanZoom.d.ts +3 -2
  103. package/dist/src/tools/PanZoom.js +32 -16
  104. package/dist/src/tools/SelectionTool.d.ts +11 -4
  105. package/dist/src/tools/SelectionTool.js +135 -23
  106. package/dist/src/tools/TextTool.js +1 -1
  107. package/dist/src/tools/ToolController.js +6 -2
  108. package/dist/src/tools/localization.d.ts +1 -0
  109. package/dist/src/tools/localization.js +1 -0
  110. package/dist/src/types.d.ts +13 -6
  111. package/dist/src/types.js +1 -0
  112. package/package.json +9 -1
  113. package/src/Editor.ts +86 -24
  114. package/src/EditorImage.test.ts +2 -4
  115. package/src/EditorImage.ts +2 -2
  116. package/src/Pointer.ts +1 -1
  117. package/src/SVGLoader.ts +14 -6
  118. package/src/UndoRedoHistory.ts +4 -0
  119. package/src/Viewport.ts +21 -17
  120. package/src/bundle/bundled.ts +2 -1
  121. package/src/commands/Command.ts +5 -5
  122. package/src/commands/Duplicate.ts +1 -1
  123. package/src/commands/Erase.ts +1 -1
  124. package/src/commands/localization.ts +1 -1
  125. package/src/components/AbstractComponent.ts +4 -4
  126. package/src/components/SVGGlobalAttributesObject.ts +3 -3
  127. package/src/components/Stroke.test.ts +3 -5
  128. package/src/components/Stroke.ts +4 -4
  129. package/src/components/Text.test.ts +2 -2
  130. package/src/components/Text.ts +3 -3
  131. package/src/components/UnknownSVGObject.ts +3 -3
  132. package/src/components/builders/ArrowBuilder.ts +2 -2
  133. package/src/components/builders/FreehandLineBuilder.ts +190 -80
  134. package/src/components/builders/LineBuilder.ts +2 -2
  135. package/src/components/builders/RectangleBuilder.ts +3 -3
  136. package/src/components/builders/types.ts +1 -1
  137. package/src/localization.ts +6 -0
  138. package/src/localizations/en.ts +8 -0
  139. package/src/localizations/es.ts +63 -0
  140. package/src/localizations/getLocalizationTable.test.ts +27 -0
  141. package/src/localizations/getLocalizationTable.ts +53 -0
  142. package/src/{geometry → math}/LineSegment2.test.ts +15 -0
  143. package/src/{geometry → math}/LineSegment2.ts +20 -0
  144. package/src/{geometry → math}/Mat33.test.ts +0 -0
  145. package/src/{geometry → math}/Mat33.ts +0 -0
  146. package/src/{geometry → math}/Path.fromString.test.ts +0 -0
  147. package/src/{geometry → math}/Path.test.ts +0 -0
  148. package/src/{geometry → math}/Path.toString.test.ts +11 -2
  149. package/src/{geometry → math}/Path.ts +60 -57
  150. package/src/{geometry → math}/Rect2.test.ts +20 -7
  151. package/src/{geometry → math}/Rect2.ts +19 -1
  152. package/src/{geometry → math}/Vec2.test.ts +0 -0
  153. package/src/{geometry → math}/Vec2.ts +0 -0
  154. package/src/{geometry → math}/Vec3.test.ts +0 -0
  155. package/src/{geometry → math}/Vec3.ts +2 -2
  156. package/src/math/rounding.test.ts +40 -0
  157. package/src/math/rounding.ts +145 -0
  158. package/src/rendering/Display.ts +18 -10
  159. package/src/rendering/caching/CacheRecord.test.ts +2 -2
  160. package/src/rendering/caching/CacheRecord.ts +2 -2
  161. package/src/rendering/caching/CacheRecordManager.ts +1 -1
  162. package/src/rendering/caching/RenderingCache.test.ts +3 -3
  163. package/src/rendering/caching/RenderingCache.ts +1 -1
  164. package/src/rendering/caching/RenderingCacheNode.ts +23 -7
  165. package/src/rendering/caching/testUtils.ts +1 -1
  166. package/src/rendering/caching/types.ts +1 -1
  167. package/src/rendering/localization.ts +4 -0
  168. package/src/rendering/renderers/AbstractRenderer.ts +4 -4
  169. package/src/rendering/renderers/CanvasRenderer.ts +4 -4
  170. package/src/rendering/renderers/DummyRenderer.test.ts +2 -2
  171. package/src/rendering/renderers/DummyRenderer.ts +4 -4
  172. package/src/rendering/renderers/SVGRenderer.ts +10 -4
  173. package/src/rendering/renderers/TextOnlyRenderer.ts +17 -6
  174. package/src/toolbar/HTMLToolbar.ts +5 -8
  175. package/src/toolbar/icons.ts +167 -147
  176. package/src/toolbar/localization.ts +8 -2
  177. package/src/toolbar/makeColorInput.ts +2 -1
  178. package/src/toolbar/toolbar.css +7 -3
  179. package/src/toolbar/widgets/ActionButtonWidget.ts +31 -0
  180. package/src/toolbar/widgets/BaseWidget.ts +36 -0
  181. package/src/toolbar/widgets/HandToolWidget.ts +14 -3
  182. package/src/toolbar/widgets/SelectionWidget.ts +46 -41
  183. package/src/tools/BaseTool.ts +5 -1
  184. package/src/tools/Eraser.ts +2 -2
  185. package/src/tools/PanZoom.ts +39 -18
  186. package/src/tools/SelectionTool.test.ts +26 -5
  187. package/src/tools/SelectionTool.ts +162 -27
  188. package/src/tools/TextTool.ts +2 -2
  189. package/src/tools/ToolController.ts +6 -2
  190. package/src/tools/UndoRedoShortcut.test.ts +1 -1
  191. package/src/tools/localization.ts +2 -0
  192. package/src/types.ts +14 -5
  193. package/dist-test/test-dist-bundle.html +0 -42
@@ -1,5 +1,5 @@
1
1
  // Renderer that outputs nothing. Useful for automated tests.
2
- import { Vec2 } from '../../geometry/Vec2';
2
+ import { Vec2 } from '../../math/Vec2';
3
3
  import AbstractRenderer from './AbstractRenderer';
4
4
  export default class DummyRenderer extends AbstractRenderer {
5
5
  constructor(viewport) {
@@ -1,8 +1,8 @@
1
1
  import { LoadSaveDataTable } from '../../components/AbstractComponent';
2
2
  import { TextStyle } from '../../components/Text';
3
- import Mat33 from '../../geometry/Mat33';
4
- import Rect2 from '../../geometry/Rect2';
5
- import { Point2, Vec2 } from '../../geometry/Vec2';
3
+ import Mat33 from '../../math/Mat33';
4
+ import Rect2 from '../../math/Rect2';
5
+ import { Point2, Vec2 } from '../../math/Vec2';
6
6
  import Viewport from '../../Viewport';
7
7
  import RenderingStyle from '../RenderingStyle';
8
8
  import AbstractRenderer from './AbstractRenderer';
@@ -1,5 +1,7 @@
1
- import Path, { PathCommandType } from '../../geometry/Path';
2
- import { Vec2 } from '../../geometry/Vec2';
1
+ import Mat33 from '../../math/Mat33';
2
+ import Path, { PathCommandType } from '../../math/Path';
3
+ import { toRoundedString } from '../../math/rounding';
4
+ import { Vec2 } from '../../math/Vec2';
3
5
  import { svgAttributesDataKey, svgStyleAttributesDataKey } from '../../SVGLoader';
4
6
  import AbstractRenderer from './AbstractRenderer';
5
7
  const svgNameSpace = 'http://www.w3.org/2000/svg';
@@ -89,6 +91,8 @@ export default class SVGRenderer extends AbstractRenderer {
89
91
  drawText(text, transform, style) {
90
92
  var _a, _b, _c;
91
93
  transform = this.getCanvasToScreenTransform().rightMul(transform);
94
+ const translation = transform.transformVec2(Vec2.zero);
95
+ transform = transform.rightMul(Mat33.translation(translation.times(-1)));
92
96
  const textElem = document.createElementNS(svgNameSpace, 'text');
93
97
  textElem.appendChild(document.createTextNode(text));
94
98
  textElem.style.transform = `matrix(
@@ -101,6 +105,8 @@ export default class SVGRenderer extends AbstractRenderer {
101
105
  textElem.style.fontWeight = (_b = style.fontWeight) !== null && _b !== void 0 ? _b : '';
102
106
  textElem.style.fontSize = style.size + 'px';
103
107
  textElem.style.fill = style.renderingStyle.fill.toHexString();
108
+ textElem.setAttribute('x', `${toRoundedString(translation.x)}`);
109
+ textElem.setAttribute('y', `${toRoundedString(translation.y)}`);
104
110
  if (style.renderingStyle.stroke) {
105
111
  const strokeStyle = style.renderingStyle.stroke;
106
112
  textElem.style.stroke = strokeStyle.color.toHexString();
@@ -1,7 +1,7 @@
1
1
  import { TextStyle } from '../../components/Text';
2
- import Mat33 from '../../geometry/Mat33';
3
- import Rect2 from '../../geometry/Rect2';
4
- import Vec3 from '../../geometry/Vec3';
2
+ import Mat33 from '../../math/Mat33';
3
+ import Rect2 from '../../math/Rect2';
4
+ import Vec3 from '../../math/Vec3';
5
5
  import Viewport from '../../Viewport';
6
6
  import { TextRendererLocalization } from '../localization';
7
7
  import RenderingStyle from '../RenderingStyle';
@@ -9,6 +9,8 @@ import AbstractRenderer from './AbstractRenderer';
9
9
  export default class TextOnlyRenderer extends AbstractRenderer {
10
10
  private localizationTable;
11
11
  private descriptionBuilder;
12
+ private pathCount;
13
+ private textNodeCount;
12
14
  constructor(viewport: Viewport, localizationTable: TextRendererLocalization);
13
15
  displaySize(): Vec3;
14
16
  clear(): void;
@@ -1,4 +1,4 @@
1
- import { Vec2 } from '../../geometry/Vec2';
1
+ import { Vec2 } from '../../math/Vec2';
2
2
  import AbstractRenderer from './AbstractRenderer';
3
3
  // Outputs a description of what was rendered.
4
4
  export default class TextOnlyRenderer extends AbstractRenderer {
@@ -6,6 +6,8 @@ export default class TextOnlyRenderer extends AbstractRenderer {
6
6
  super(viewport);
7
7
  this.localizationTable = localizationTable;
8
8
  this.descriptionBuilder = [];
9
+ this.pathCount = 0;
10
+ this.textNodeCount = 0;
9
11
  }
10
12
  displaySize() {
11
13
  // We don't have a graphical display, export a reasonable size.
@@ -13,13 +15,20 @@ export default class TextOnlyRenderer extends AbstractRenderer {
13
15
  }
14
16
  clear() {
15
17
  this.descriptionBuilder = [];
18
+ this.pathCount = 0;
19
+ this.textNodeCount = 0;
16
20
  }
17
21
  getDescription() {
18
- return this.descriptionBuilder.join('\n');
22
+ return [
23
+ this.localizationTable.pathNodeCount(this.pathCount),
24
+ this.localizationTable.textNodeCount(this.textNodeCount),
25
+ ...this.descriptionBuilder
26
+ ].join('\n');
19
27
  }
20
28
  beginPath(_startPoint) {
21
29
  }
22
30
  endPath(_style) {
31
+ this.pathCount++;
23
32
  }
24
33
  lineTo(_point) {
25
34
  }
@@ -31,9 +40,10 @@ export default class TextOnlyRenderer extends AbstractRenderer {
31
40
  }
32
41
  drawText(text, _transform, _style) {
33
42
  this.descriptionBuilder.push(this.localizationTable.textNode(text));
43
+ this.textNodeCount++;
34
44
  }
35
45
  isTooSmallToRender(rect) {
36
- return rect.maxDimension < 10 / this.getSizeOfCanvasPixelOnScreen();
46
+ return rect.maxDimension < 15 / this.getSizeOfCanvasPixelOnScreen();
37
47
  }
38
48
  drawPoints(..._points) {
39
49
  }
@@ -108,13 +108,13 @@ export default class HTMLToolbar {
108
108
  const undoRedoGroup = document.createElement('div');
109
109
  undoRedoGroup.classList.add(`${toolbarCSSPrefix}buttonGroup`);
110
110
  const undoButton = this.addActionButton({
111
- label: 'Undo',
111
+ label: this.localizationTable.undo,
112
112
  icon: makeUndoIcon()
113
113
  }, () => {
114
114
  this.editor.history.undo();
115
115
  }, undoRedoGroup);
116
116
  const redoButton = this.addActionButton({
117
- label: 'Redo',
117
+ label: this.localizationTable.redo,
118
118
  icon: makeRedoIcon(),
119
119
  }, () => {
120
120
  this.editor.history.redo();
@@ -157,11 +157,9 @@ export default class HTMLToolbar {
157
157
  }
158
158
  (new TextToolWidget(this.editor, tool, this.localizationTable)).addTo(this.container);
159
159
  }
160
- for (const tool of toolController.getMatchingTools(ToolType.PanZoom)) {
161
- if (!(tool instanceof PanZoom)) {
162
- throw new Error('All SelectionTools must have kind === ToolType.PanZoom');
163
- }
164
- (new HandToolWidget(this.editor, tool, this.localizationTable)).addTo(this.container);
160
+ const panZoomTool = toolController.getMatchingTools(ToolType.PanZoom)[0];
161
+ if (panZoomTool && panZoomTool instanceof PanZoom) {
162
+ (new HandToolWidget(this.editor, panZoomTool, this.localizationTable)).addTo(this.container);
165
163
  }
166
164
  this.setupColorPickers();
167
165
  }
@@ -15,3 +15,6 @@ export declare const makeTextIcon: (textStyle: TextStyle) => SVGSVGElement;
15
15
  export declare const makePenIcon: (tipThickness: number, color: string) => SVGSVGElement;
16
16
  export declare const makeIconFromFactory: (pen: Pen, factory: ComponentBuilderFactory) => SVGSVGElement;
17
17
  export declare const makePipetteIcon: (color?: Color4) => SVGSVGElement;
18
+ export declare const makeResizeViewportIcon: () => SVGSVGElement;
19
+ export declare const makeDuplicateSelectionIcon: () => SVGSVGElement;
20
+ export declare const makeDeleteSelectionIcon: () => SVGSVGElement;
@@ -1,13 +1,13 @@
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';
6
- const primaryForegroundFill = `
7
- style='fill: var(--primary-foreground-color);'
6
+ const iconColorFill = `
7
+ style='fill: var(--icon-color);'
8
8
  `;
9
- const primaryForegroundStrokeFill = `
10
- style='fill: var(--primary-foreground-color); stroke: var(--primary-foreground-color);'
9
+ const iconColorStrokeFill = `
10
+ style='fill: var(--icon-color); stroke: var(--icon-color);'
11
11
  `;
12
12
  const checkerboardPatternDef = `
13
13
  <pattern
@@ -31,7 +31,7 @@ export const makeRedoIcon = (mirror = false) => {
31
31
  icon.innerHTML = `
32
32
  <style>
33
33
  .toolbar-svg-undo-redo-icon {
34
- stroke: var(--primary-foreground-color);
34
+ stroke: var(--icon-color);
35
35
  stroke-width: 12;
36
36
  stroke-linejoin: round;
37
37
  stroke-linecap: round;
@@ -54,7 +54,7 @@ export const makeDropdownIcon = () => {
54
54
  <g>
55
55
  <path
56
56
  d='M5,10 L50,90 L95,10 Z'
57
- ${primaryForegroundFill}
57
+ ${iconColorFill}
58
58
  />
59
59
  </g>
60
60
  `;
@@ -69,7 +69,7 @@ export const makeEraserIcon = () => {
69
69
  <rect x=10 y=50 width=80 height=30 rx=10 fill='pink' />
70
70
  <rect
71
71
  x=10 y=10 width=80 height=50
72
- ${primaryForegroundFill}
72
+ ${iconColorFill}
73
73
  />
74
74
  </g>
75
75
  `;
@@ -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(--primary-foreground-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(--primary-foreground-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(--primary-foreground-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');
@@ -241,7 +224,7 @@ export const makeZoomIcon = () => {
241
224
  textNode.style.textAlign = 'center';
242
225
  textNode.style.textAnchor = 'middle';
243
226
  textNode.style.fontSize = '55px';
244
- textNode.style.fill = 'var(--primary-foreground-color)';
227
+ textNode.style.fill = 'var(--icon-color)';
245
228
  textNode.style.fontFamily = 'monospace';
246
229
  icon.appendChild(textNode);
247
230
  };
@@ -282,7 +265,7 @@ export const makePenIcon = (tipThickness, color) => {
282
265
  <!-- Pen grip -->
283
266
  <path
284
267
  d='M10,10 L90,10 L90,60 L${50 + halfThickness},80 L${50 - halfThickness},80 L10,60 Z'
285
- ${primaryForegroundStrokeFill}
268
+ ${iconColorStrokeFill}
286
269
  />
287
270
  </g>
288
271
  <g>
@@ -348,7 +331,7 @@ export const makePipetteIcon = (color) => {
348
331
  65,15 65,5 47,6
349
332
  Z
350
333
  `);
351
- pipette.style.fill = 'var(--primary-foreground-color)';
334
+ pipette.style.fill = 'var(--icon-color)';
352
335
  if (color) {
353
336
  const defs = document.createElementNS(svgNamespace, 'defs');
354
337
  defs.innerHTML = checkerboardPatternDef;
@@ -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,10 +17,13 @@ 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;
25
+ resetView: string;
26
+ selectionToolKeyboardShortcuts: string;
24
27
  dropdownShown: (toolName: string) => string;
25
28
  dropdownHidden: (toolName: string) => string;
26
29
  zoomLevel: (zoomPercentage: number) => string;
@@ -4,6 +4,7 @@ export const defaultToolbarLocalization = {
4
4
  select: 'Select',
5
5
  handTool: 'Pan',
6
6
  zoom: 'Zoom',
7
+ resetView: 'Reset view',
7
8
  thicknessLabel: 'Thickness: ',
8
9
  colorLabel: 'Color: ',
9
10
  fontLabel: 'Font: ',
@@ -13,7 +14,9 @@ export const defaultToolbarLocalization = {
13
14
  undo: 'Undo',
14
15
  redo: 'Redo',
15
16
  selectObjectType: 'Object type: ',
16
- pickColorFronScreen: 'Pick color from screen',
17
+ pickColorFromScreen: 'Pick color from screen',
18
+ clickToPickColorAnnouncement: 'Click on the screen to pick a color',
19
+ selectionToolKeyboardShortcuts: 'Selection tool: Use arrow keys to move selected items, lowercase/uppercase ‘i’ and ‘o’ to resize.',
17
20
  touchPanning: 'Touchscreen panning',
18
21
  anyDevicePanning: 'Any device panning',
19
22
  freehandPen: 'Freehand',
@@ -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
+ }
@@ -10,6 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
12
  var _BaseWidget_hasDropdown;
13
+ import { InputEvtType } from '../../types';
13
14
  import { toolbarCSSPrefix } from '../HTMLToolbar';
14
15
  import { makeDropdownIcon } from '../icons';
15
16
  export default class BaseWidget {
@@ -44,6 +45,34 @@ export default class BaseWidget {
44
45
  return true;
45
46
  }
46
47
  setupActionBtnClickListener(button) {
48
+ const clickTriggers = { Enter: true, ' ': true, };
49
+ button.onkeydown = (evt) => {
50
+ let handled = false;
51
+ if (evt.key in clickTriggers) {
52
+ if (!this.disabled) {
53
+ this.handleClick();
54
+ handled = true;
55
+ }
56
+ }
57
+ // If we didn't do anything with the event, send it to the editor.
58
+ if (!handled) {
59
+ this.editor.toolController.dispatchInputEvent({
60
+ kind: InputEvtType.KeyPressEvent,
61
+ key: evt.key,
62
+ ctrlKey: evt.ctrlKey,
63
+ });
64
+ }
65
+ };
66
+ button.onkeyup = evt => {
67
+ if (evt.key in clickTriggers) {
68
+ return;
69
+ }
70
+ this.editor.toolController.dispatchInputEvent({
71
+ kind: InputEvtType.KeyUpEvent,
72
+ key: evt.key,
73
+ ctrlKey: evt.ctrlKey,
74
+ });
75
+ };
47
76
  button.onclick = () => {
48
77
  if (!this.disabled) {
49
78
  this.handleClick();
@@ -85,9 +114,11 @@ export default class BaseWidget {
85
114
  this.disabled = disabled;
86
115
  if (this.disabled) {
87
116
  this.button.classList.add('disabled');
117
+ this.button.setAttribute('aria-disabled', 'true');
88
118
  }
89
119
  else {
90
120
  this.button.classList.remove('disabled');
121
+ this.button.removeAttribute('aria-disabled');
91
122
  }
92
123
  }
93
124
  setSelected(selected) {