js-draw 0.1.2 → 0.1.5

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 (79) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +21 -12
  3. package/dist/bundle.js +1 -1
  4. package/dist/src/Editor.d.ts +2 -1
  5. package/dist/src/Editor.js +20 -6
  6. package/dist/src/SVGLoader.d.ts +8 -0
  7. package/dist/src/SVGLoader.js +105 -6
  8. package/dist/src/Viewport.d.ts +1 -1
  9. package/dist/src/Viewport.js +5 -5
  10. package/dist/src/components/SVGGlobalAttributesObject.js +0 -1
  11. package/dist/src/components/Text.d.ts +30 -0
  12. package/dist/src/components/Text.js +111 -0
  13. package/dist/src/components/localization.d.ts +1 -0
  14. package/dist/src/components/localization.js +1 -0
  15. package/dist/src/geometry/Mat33.d.ts +1 -0
  16. package/dist/src/geometry/Mat33.js +30 -0
  17. package/dist/src/geometry/Path.js +8 -1
  18. package/dist/src/geometry/Rect2.d.ts +2 -0
  19. package/dist/src/geometry/Rect2.js +6 -0
  20. package/dist/src/localization.d.ts +2 -1
  21. package/dist/src/localization.js +2 -1
  22. package/dist/src/rendering/Display.d.ts +2 -0
  23. package/dist/src/rendering/Display.js +19 -0
  24. package/dist/src/rendering/localization.d.ts +5 -0
  25. package/dist/src/rendering/localization.js +4 -0
  26. package/dist/src/rendering/renderers/AbstractRenderer.d.ts +5 -0
  27. package/dist/src/rendering/renderers/AbstractRenderer.js +12 -0
  28. package/dist/src/rendering/renderers/CanvasRenderer.d.ts +3 -0
  29. package/dist/src/rendering/renderers/CanvasRenderer.js +28 -8
  30. package/dist/src/rendering/renderers/DummyRenderer.d.ts +3 -0
  31. package/dist/src/rendering/renderers/DummyRenderer.js +5 -0
  32. package/dist/src/rendering/renderers/SVGRenderer.d.ts +3 -0
  33. package/dist/src/rendering/renderers/SVGRenderer.js +30 -1
  34. package/dist/src/rendering/renderers/TextOnlyRenderer.d.ts +24 -0
  35. package/dist/src/rendering/renderers/TextOnlyRenderer.js +40 -0
  36. package/dist/src/testing/loadExpectExtensions.js +1 -4
  37. package/dist/src/toolbar/HTMLToolbar.js +78 -1
  38. package/dist/src/toolbar/icons.d.ts +2 -0
  39. package/dist/src/toolbar/icons.js +18 -0
  40. package/dist/src/toolbar/localization.d.ts +1 -0
  41. package/dist/src/toolbar/localization.js +1 -0
  42. package/dist/src/tools/SelectionTool.js +1 -1
  43. package/dist/src/tools/TextTool.d.ts +31 -0
  44. package/dist/src/tools/TextTool.js +174 -0
  45. package/dist/src/tools/ToolController.d.ts +2 -1
  46. package/dist/src/tools/ToolController.js +4 -1
  47. package/dist/src/tools/localization.d.ts +3 -1
  48. package/dist/src/tools/localization.js +3 -1
  49. package/dist-test/test-dist-bundle.html +8 -1
  50. package/package.json +1 -1
  51. package/src/Editor.css +12 -0
  52. package/src/Editor.ts +22 -7
  53. package/src/SVGLoader.ts +124 -6
  54. package/src/Viewport.ts +5 -5
  55. package/src/components/SVGGlobalAttributesObject.ts +0 -1
  56. package/src/components/Text.ts +140 -0
  57. package/src/components/localization.ts +2 -0
  58. package/src/geometry/Mat33.test.ts +44 -0
  59. package/src/geometry/Mat33.ts +41 -0
  60. package/src/geometry/Path.toString.test.ts +7 -3
  61. package/src/geometry/Path.ts +11 -1
  62. package/src/geometry/Rect2.ts +8 -0
  63. package/src/localization.ts +3 -1
  64. package/src/rendering/Display.ts +26 -0
  65. package/src/rendering/localization.ts +10 -0
  66. package/src/rendering/renderers/AbstractRenderer.ts +16 -0
  67. package/src/rendering/renderers/CanvasRenderer.ts +34 -10
  68. package/src/rendering/renderers/DummyRenderer.ts +8 -0
  69. package/src/rendering/renderers/SVGRenderer.ts +36 -1
  70. package/src/rendering/renderers/TextOnlyRenderer.ts +51 -0
  71. package/src/testing/loadExpectExtensions.ts +1 -4
  72. package/src/toolbar/HTMLToolbar.ts +96 -1
  73. package/src/toolbar/icons.ts +24 -0
  74. package/src/toolbar/localization.ts +2 -0
  75. package/src/toolbar/toolbar.css +6 -3
  76. package/src/tools/SelectionTool.ts +1 -1
  77. package/src/tools/TextTool.ts +229 -0
  78. package/src/tools/ToolController.ts +4 -0
  79. package/src/tools/localization.ts +7 -2
@@ -15,10 +15,11 @@ import { makeLineBuilder } from '../components/builders/LineBuilder';
15
15
  import { makeFilledRectangleBuilder, makeOutlinedRectangleBuilder } from '../components/builders/RectangleBuilder';
16
16
  import { defaultToolbarLocalization, ToolbarLocalization } from './localization';
17
17
  import { ActionButtonIcon } from './types';
18
- import { makeDropdownIcon, makeEraserIcon, makeIconFromFactory, makePenIcon, makeRedoIcon, makeSelectionIcon, makeHandToolIcon, makeUndoIcon } from './icons';
18
+ import { makeDropdownIcon, makeEraserIcon, makeIconFromFactory, makePenIcon, makeRedoIcon, makeSelectionIcon, makeHandToolIcon, makeUndoIcon, makeTextIcon } from './icons';
19
19
  import PanZoom, { PanZoomMode } from '../tools/PanZoom';
20
20
  import Mat33 from '../geometry/Mat33';
21
21
  import Viewport from '../Viewport';
22
+ import TextTool from '../tools/TextTool';
22
23
 
23
24
 
24
25
  const toolbarCSSPrefix = 'toolbar-';
@@ -403,6 +404,92 @@ class HandToolWidget extends ToolbarWidget {
403
404
  }
404
405
  }
405
406
 
407
+ class TextToolWidget extends ToolbarWidget {
408
+ private updateDropdownInputs: (()=>void)|null = null;
409
+ public constructor(editor: Editor, private tool: TextTool, localization: ToolbarLocalization) {
410
+ super(editor, tool, localization);
411
+
412
+ editor.notifier.on(EditorEventType.ToolUpdated, evt => {
413
+ if (evt.kind === EditorEventType.ToolUpdated && evt.tool === tool) {
414
+ this.updateIcon();
415
+ this.updateDropdownInputs?.();
416
+ }
417
+ });
418
+ }
419
+
420
+ protected getTitle(): string {
421
+ return this.targetTool.description;
422
+ }
423
+
424
+ protected createIcon(): Element {
425
+ const textStyle = this.tool.getTextStyle();
426
+ return makeTextIcon(textStyle);
427
+ }
428
+
429
+ private static idCounter: number = 0;
430
+ protected fillDropdown(dropdown: HTMLElement): boolean {
431
+ const fontRow = document.createElement('div');
432
+ const colorRow = document.createElement('div');
433
+
434
+ const fontInput = document.createElement('select');
435
+ const fontLabel = document.createElement('label');
436
+
437
+ const colorInput = document.createElement('input');
438
+ const colorLabel = document.createElement('label');
439
+
440
+ const fontsInInput = new Set();
441
+ const addFontToInput = (fontName: string) => {
442
+ const option = document.createElement('option');
443
+ option.value = fontName;
444
+ option.textContent = fontName;
445
+ fontInput.appendChild(option);
446
+ fontsInInput.add(fontName);
447
+ };
448
+
449
+ fontLabel.innerText = this.localizationTable.fontLabel;
450
+ colorLabel.innerText = this.localizationTable.colorLabel;
451
+
452
+ colorInput.classList.add('coloris_input');
453
+ colorInput.type = 'button';
454
+ colorInput.id = `${toolbarCSSPrefix}-text-color-input-${TextToolWidget.idCounter++}`;
455
+ colorLabel.setAttribute('for', colorInput.id);
456
+
457
+ addFontToInput('monospace');
458
+ addFontToInput('serif');
459
+ addFontToInput('sans-serif');
460
+ fontInput.id = `${toolbarCSSPrefix}-text-font-input-${TextToolWidget.idCounter++}`;
461
+ fontLabel.setAttribute('for', fontInput.id);
462
+
463
+ fontInput.onchange = () => {
464
+ this.tool.setFontFamily(fontInput.value);
465
+ };
466
+
467
+ colorInput.oninput = () => {
468
+ this.tool.setColor(Color4.fromString(colorInput.value));
469
+ };
470
+
471
+ colorRow.appendChild(colorLabel);
472
+ colorRow.appendChild(colorInput);
473
+
474
+ fontRow.appendChild(fontLabel);
475
+ fontRow.appendChild(fontInput);
476
+
477
+ this.updateDropdownInputs = () => {
478
+ const style = this.tool.getTextStyle();
479
+ colorInput.value = style.renderingStyle.fill.toHexString();
480
+
481
+ if (!fontsInInput.has(style.fontFamily)) {
482
+ addFontToInput(style.fontFamily);
483
+ }
484
+ fontInput.value = style.fontFamily;
485
+ };
486
+ this.updateDropdownInputs();
487
+
488
+ dropdown.replaceChildren(colorRow, fontRow);
489
+ return true;
490
+ }
491
+ }
492
+
406
493
  class PenWidget extends ToolbarWidget {
407
494
  private updateInputs: ()=> void = () => {};
408
495
 
@@ -702,6 +789,14 @@ export default class HTMLToolbar {
702
789
  (new SelectionWidget(this.editor, tool, this.localizationTable)).addTo(this.container);
703
790
  }
704
791
 
792
+ for (const tool of toolController.getMatchingTools(ToolType.Text)) {
793
+ if (!(tool instanceof TextTool)) {
794
+ throw new Error('All text tools must have kind === ToolType.Text');
795
+ }
796
+
797
+ (new TextToolWidget(this.editor, tool, this.localizationTable)).addTo(this.container);
798
+ }
799
+
705
800
  for (const tool of toolController.getMatchingTools(ToolType.PanZoom)) {
706
801
  if (!(tool instanceof PanZoom)) {
707
802
  throw new Error('All SelectionTools must have kind === ToolType.PanZoom');
@@ -1,4 +1,5 @@
1
1
  import { ComponentBuilderFactory } from '../components/builders/types';
2
+ import { TextStyle } from '../components/Text';
2
3
  import EventDispatcher from '../EventDispatcher';
3
4
  import { Vec2 } from '../geometry/Vec2';
4
5
  import SVGRenderer from '../rendering/renderers/SVGRenderer';
@@ -126,6 +127,29 @@ export const makeHandToolIcon = () => {
126
127
  return icon;
127
128
  };
128
129
 
130
+ export const makeTextIcon = (textStyle: TextStyle) => {
131
+ const icon = document.createElementNS(svgNamespace, 'svg');
132
+ icon.setAttribute('viewBox', '0 0 100 100');
133
+
134
+ const textNode = document.createElementNS(svgNamespace, 'text');
135
+ textNode.appendChild(document.createTextNode('T'));
136
+
137
+ textNode.style.fontFamily = textStyle.fontFamily;
138
+ textNode.style.fontWeight = textStyle.fontWeight ?? '';
139
+ textNode.style.fontVariant = textStyle.fontVariant ?? '';
140
+ textNode.style.fill = textStyle.renderingStyle.fill.toHexString();
141
+
142
+ textNode.style.textAnchor = 'middle';
143
+ textNode.setAttribute('x', '50');
144
+ textNode.setAttribute('y', '75');
145
+ textNode.style.fontSize = '65px';
146
+ textNode.style.filter = 'drop-shadow(0px 0px 10px var(--primary-shadow-color))';
147
+
148
+ icon.appendChild(textNode);
149
+
150
+ return icon;
151
+ };
152
+
129
153
  export const makePenIcon = (tipThickness: number, color: string) => {
130
154
  const icon = document.createElementNS(svgNamespace, 'svg');
131
155
  icon.setAttribute('viewBox', '0 0 100 100');
@@ -1,6 +1,7 @@
1
1
 
2
2
 
3
3
  export interface ToolbarLocalization {
4
+ fontLabel: string;
4
5
  anyDevicePanning: string;
5
6
  touchPanning: string;
6
7
  outlinedRectanglePen: string;
@@ -32,6 +33,7 @@ export const defaultToolbarLocalization: ToolbarLocalization = {
32
33
  handTool: 'Pan',
33
34
  thicknessLabel: 'Thickness: ',
34
35
  colorLabel: 'Color: ',
36
+ fontLabel: 'Font: ',
35
37
  resizeImageToSelection: 'Resize image to selection',
36
38
  deleteSelection: 'Delete selection',
37
39
  undo: 'Undo',
@@ -12,6 +12,9 @@
12
12
  flex-direction: row;
13
13
  justify-content: center;
14
14
 
15
+ /* Display above selection dialogs, etc. */
16
+ z-index: 1000;
17
+
15
18
  font-family: system-ui, -apple-system, sans-serif;
16
19
  }
17
20
 
@@ -41,13 +44,13 @@
41
44
  background-color: var(--primary-background-color);
42
45
  color: var(--primary-foreground-color);
43
46
  border: none;
44
- box-shadow: 0px 0px 2px var(--primary-foreground-color);
47
+ box-shadow: 0px 0px 2px var(--primary-shadow-color);
45
48
 
46
49
  transition: background-color 0.25s ease, box-shadow 0.25s ease, opacity 0.3s ease;
47
50
  }
48
51
 
49
52
  .toolbar-button:hover, .toolbar-root button:not(:disabled):hover {
50
- box-shadow: 0px 2px 4px var(--primary-foreground-color);
53
+ box-shadow: 0px 2px 4px var(--primary-shadow-color);
51
54
  }
52
55
 
53
56
  .toolbar-root button:disabled {
@@ -90,7 +93,7 @@
90
93
  /* Prevent overlap/being displayed under the undo/redo buttons */
91
94
  z-index: 2;
92
95
  background-color: var(--primary-background-color);
93
- box-shadow: 0px 3px 3px var(--primary-foreground-color);
96
+ box-shadow: 0px 3px 3px var(--primary-shadow-color);
94
97
  }
95
98
 
96
99
  .toolbar-buttonGroup {
@@ -494,7 +494,7 @@ export default class SelectionTool extends BaseTool {
494
494
  );
495
495
 
496
496
  const selectionRect = this.selectionBox.region;
497
- this.editor.viewport.zoomTo(selectionRect).apply(this.editor);
497
+ this.editor.viewport.zoomTo(selectionRect, false).apply(this.editor);
498
498
  }
499
499
  }
500
500
 
@@ -0,0 +1,229 @@
1
+ import Color4 from '../Color4';
2
+ import Text, { TextStyle } from '../components/Text';
3
+ import Editor from '../Editor';
4
+ import EditorImage from '../EditorImage';
5
+ import Mat33 from '../geometry/Mat33';
6
+ import { Vec2 } from '../geometry/Vec2';
7
+ import { PointerDevice } from '../Pointer';
8
+ import { EditorEventType, PointerEvt } from '../types';
9
+ import BaseTool from './BaseTool';
10
+ import { ToolLocalization } from './localization';
11
+ import { ToolType } from './ToolController';
12
+
13
+ const overlayCssClass = 'textEditorOverlay';
14
+ export default class TextTool extends BaseTool {
15
+ public kind: ToolType = ToolType.Text;
16
+ private textStyle: TextStyle;
17
+
18
+ private textEditOverlay: HTMLElement;
19
+ private textInputElem: HTMLInputElement|null = null;
20
+ private textTargetPosition: Vec2|null = null;
21
+ private textMeasuringCtx: CanvasRenderingContext2D|null = null;
22
+ private textRotation: number;
23
+
24
+ public constructor(private editor: Editor, description: string, private localizationTable: ToolLocalization) {
25
+ super(editor.notifier, description);
26
+ this.textStyle = {
27
+ size: 32,
28
+ fontFamily: 'sans-serif',
29
+ renderingStyle: {
30
+ fill: Color4.purple,
31
+ },
32
+ };
33
+
34
+ this.textEditOverlay = document.createElement('div');
35
+ this.textEditOverlay.classList.add(overlayCssClass);
36
+ this.editor.addStyleSheet(`
37
+ .${overlayCssClass} {
38
+ height: 0;
39
+ overflow: visible;
40
+ }
41
+
42
+ .${overlayCssClass} input {
43
+ background-color: rgba(0, 0, 0, 0);
44
+ border: none;
45
+ padding: 0;
46
+ }
47
+ `);
48
+ this.editor.createHTMLOverlay(this.textEditOverlay);
49
+ this.editor.notifier.on(EditorEventType.ViewportChanged, () => this.updateTextInput());
50
+ }
51
+
52
+ private getTextAscent(text: string, style: TextStyle): number {
53
+ this.textMeasuringCtx ??= document.createElement('canvas').getContext('2d');
54
+ if (this.textMeasuringCtx) {
55
+ Text.applyTextStyles(this.textMeasuringCtx, style);
56
+ return this.textMeasuringCtx.measureText(text).actualBoundingBoxAscent;
57
+ }
58
+
59
+ // Estimate
60
+ return style.size * 2 / 3;
61
+ }
62
+
63
+ private flushInput() {
64
+ if (this.textInputElem && this.textTargetPosition) {
65
+ const content = this.textInputElem.value;
66
+ this.textInputElem.remove();
67
+ this.textInputElem = null;
68
+
69
+ if (content === '') {
70
+ return;
71
+ }
72
+
73
+ const textTransform = Mat33.translation(
74
+ this.textTargetPosition
75
+ ).rightMul(
76
+ Mat33.scaling2D(this.editor.viewport.getSizeOfPixelOnCanvas())
77
+ ).rightMul(
78
+ Mat33.zRotation(this.textRotation)
79
+ );
80
+
81
+ const textComponent = new Text(
82
+ [ content ],
83
+ textTransform,
84
+ this.textStyle,
85
+ );
86
+
87
+ const action = new EditorImage.AddElementCommand(textComponent);
88
+ this.editor.dispatch(action);
89
+ }
90
+ }
91
+
92
+ private updateTextInput() {
93
+ if (!this.textInputElem || !this.textTargetPosition) {
94
+ this.textInputElem?.remove();
95
+ return;
96
+ }
97
+
98
+ const viewport = this.editor.viewport;
99
+ const textScreenPos = viewport.canvasToScreen(this.textTargetPosition);
100
+ this.textInputElem.type = 'text';
101
+ this.textInputElem.placeholder = this.localizationTable.enterTextToInsert;
102
+ this.textInputElem.style.fontFamily = this.textStyle.fontFamily;
103
+ this.textInputElem.style.fontVariant = this.textStyle.fontVariant ?? '';
104
+ this.textInputElem.style.fontWeight = this.textStyle.fontWeight ?? '';
105
+ this.textInputElem.style.fontSize = `${this.textStyle.size}px`;
106
+ this.textInputElem.style.color = this.textStyle.renderingStyle.fill.toHexString();
107
+
108
+ this.textInputElem.style.position = 'relative';
109
+ this.textInputElem.style.left = `${textScreenPos.x}px`;
110
+ this.textInputElem.style.top = `${textScreenPos.y}px`;
111
+ this.textInputElem.style.margin = '0';
112
+
113
+ const rotation = this.textRotation + viewport.getRotationAngle();
114
+ const ascent = this.getTextAscent(this.textInputElem.value || 'W', this.textStyle);
115
+ this.textInputElem.style.transform = `rotate(${rotation * 180 / Math.PI}deg) translate(0, ${-ascent}px)`;
116
+ this.textInputElem.style.transformOrigin = 'top left';
117
+ }
118
+
119
+ private startTextInput(textCanvasPos: Vec2, initialText: string) {
120
+ this.flushInput();
121
+
122
+ this.textInputElem = document.createElement('input');
123
+ this.textInputElem.value = initialText;
124
+ this.textTargetPosition = textCanvasPos;
125
+ this.textRotation = -this.editor.viewport.getRotationAngle();
126
+ this.updateTextInput();
127
+
128
+ this.textInputElem.oninput = () => {
129
+ if (this.textInputElem) {
130
+ this.textInputElem.size = this.textInputElem?.value.length || 10;
131
+ }
132
+ };
133
+ this.textInputElem.onblur = () => {
134
+ // Don't remove the input within the context of a blur event handler.
135
+ // Doing so causes errors.
136
+ setTimeout(() => this.flushInput(), 0);
137
+ };
138
+ this.textInputElem.onkeyup = (evt) => {
139
+ if (evt.key === 'Enter') {
140
+ this.flushInput();
141
+ this.editor.focus();
142
+ } else if (evt.key === 'Escape') {
143
+ // Cancel input.
144
+ this.textInputElem?.remove();
145
+ this.textInputElem = null;
146
+ this.editor.focus();
147
+ }
148
+ };
149
+
150
+ this.textEditOverlay.replaceChildren(this.textInputElem);
151
+ setTimeout(() => this.textInputElem?.focus(), 0);
152
+ }
153
+
154
+ public setEnabled(enabled: boolean) {
155
+ super.setEnabled(enabled);
156
+
157
+ if (!enabled) {
158
+ this.flushInput();
159
+ }
160
+
161
+ this.textEditOverlay.style.display = enabled ? 'block' : 'none';
162
+ }
163
+
164
+ public onPointerDown({ current, allPointers }: PointerEvt): boolean {
165
+ if (current.device === PointerDevice.Eraser) {
166
+ return false;
167
+ }
168
+
169
+ if (allPointers.length === 1) {
170
+ this.startTextInput(current.canvasPos, '');
171
+ return true;
172
+ }
173
+
174
+ return false;
175
+ }
176
+
177
+ public onGestureCancel(): void {
178
+ this.flushInput();
179
+ this.editor.focus();
180
+ }
181
+
182
+ private dispatchUpdateEvent() {
183
+ this.updateTextInput();
184
+ this.editor.notifier.dispatch(EditorEventType.ToolUpdated, {
185
+ kind: EditorEventType.ToolUpdated,
186
+ tool: this,
187
+ });
188
+ }
189
+
190
+ public setFontFamily(fontFamily: string) {
191
+ if (fontFamily !== this.textStyle.fontFamily) {
192
+ this.textStyle = {
193
+ ...this.textStyle,
194
+ fontFamily: fontFamily,
195
+ };
196
+
197
+ this.dispatchUpdateEvent();
198
+ }
199
+ }
200
+
201
+ public setColor(color: Color4) {
202
+ if (!color.eq(this.textStyle.renderingStyle.fill)) {
203
+ this.textStyle = {
204
+ ...this.textStyle,
205
+ renderingStyle: {
206
+ ...this.textStyle.renderingStyle,
207
+ fill: color,
208
+ },
209
+ };
210
+
211
+ this.dispatchUpdateEvent();
212
+ }
213
+ }
214
+
215
+ public setFontSize(size: number) {
216
+ if (size !== this.textStyle.size) {
217
+ this.textStyle = {
218
+ ...this.textStyle,
219
+ size,
220
+ };
221
+
222
+ this.dispatchUpdateEvent();
223
+ }
224
+ }
225
+
226
+ public getTextStyle(): TextStyle {
227
+ return this.textStyle;
228
+ }
229
+ }
@@ -9,12 +9,14 @@ import SelectionTool from './SelectionTool';
9
9
  import Color4 from '../Color4';
10
10
  import { ToolLocalization } from './localization';
11
11
  import UndoRedoShortcut from './UndoRedoShortcut';
12
+ import TextTool from './TextTool';
12
13
 
13
14
  export enum ToolType {
14
15
  Pen,
15
16
  Selection,
16
17
  Eraser,
17
18
  PanZoom,
19
+ Text,
18
20
  UndoRedoShortcut,
19
21
  }
20
22
 
@@ -36,6 +38,8 @@ export default class ToolController {
36
38
 
37
39
  // Highlighter-like pen with width=64
38
40
  new Pen(editor, localization.penTool(3), { color: Color4.ofRGBA(1, 1, 0, 0.5), thickness: 64 }),
41
+
42
+ new TextTool(editor, localization.textTool, localization),
39
43
  ];
40
44
  this.tools = [
41
45
  panZoomTool,
@@ -1,12 +1,14 @@
1
1
 
2
2
  export interface ToolLocalization {
3
- RightClickDragPanTool: string;
3
+ rightClickDragPanTool: string;
4
4
  penTool: (penId: number)=>string;
5
5
  selectionTool: string;
6
6
  eraserTool: string;
7
7
  touchPanTool: string;
8
8
  twoFingerPanZoomTool: string;
9
9
  undoRedoTool: string;
10
+ textTool: string;
11
+ enterTextToInsert: string;
10
12
 
11
13
  toolEnabledAnnouncement: (toolName: string) => string;
12
14
  toolDisabledAnnouncement: (toolName: string) => string;
@@ -19,7 +21,10 @@ export const defaultToolLocalization: ToolLocalization = {
19
21
  touchPanTool: 'Touch Panning',
20
22
  twoFingerPanZoomTool: 'Panning and Zooming',
21
23
  undoRedoTool: 'Undo/Redo',
22
- RightClickDragPanTool: 'Right-click drag',
24
+ rightClickDragPanTool: 'Right-click drag',
25
+
26
+ textTool: 'Text',
27
+ enterTextToInsert: 'Text to insert',
23
28
 
24
29
  toolEnabledAnnouncement: (toolName) => `${toolName} enabled`,
25
30
  toolDisabledAnnouncement: (toolName) => `${toolName} disabled`,