js-draw 1.0.1 → 1.0.2

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 (182) hide show
  1. package/LICENSE +21 -0
  2. package/dist/bundle.js +1 -1
  3. package/dist/cjs/version.js +1 -1
  4. package/dist/mjs/version.mjs +1 -1
  5. package/docs/img/readme-images/js-draw.jpg +0 -0
  6. package/docs/img/readme-images/unsupported-elements--in-editor.png +0 -0
  7. package/package.json +5 -4
  8. package/dist-test/test_imports/package-lock.json +0 -13
  9. package/dist-test/test_imports/package.json +0 -12
  10. package/dist-test/test_imports/test-imports.js +0 -11
  11. package/dist-test/test_imports/test-require.cjs +0 -14
  12. package/src/Editor.loadFrom.test.ts +0 -24
  13. package/src/Editor.test.ts +0 -107
  14. package/src/Editor.toSVG.test.ts +0 -294
  15. package/src/Editor.ts +0 -1443
  16. package/src/EditorImage.test.ts +0 -117
  17. package/src/EditorImage.ts +0 -609
  18. package/src/EventDispatcher.test.ts +0 -123
  19. package/src/EventDispatcher.ts +0 -72
  20. package/src/Pointer.ts +0 -183
  21. package/src/SVGLoader.test.ts +0 -114
  22. package/src/SVGLoader.ts +0 -672
  23. package/src/UndoRedoHistory.test.ts +0 -34
  24. package/src/UndoRedoHistory.ts +0 -102
  25. package/src/Viewport.ts +0 -322
  26. package/src/bundle/bundled.ts +0 -7
  27. package/src/commands/Command.ts +0 -45
  28. package/src/commands/Duplicate.ts +0 -75
  29. package/src/commands/Erase.ts +0 -95
  30. package/src/commands/SerializableCommand.ts +0 -49
  31. package/src/commands/UnresolvedCommand.ts +0 -37
  32. package/src/commands/invertCommand.ts +0 -58
  33. package/src/commands/lib.ts +0 -16
  34. package/src/commands/localization.ts +0 -47
  35. package/src/commands/uniteCommands.test.ts +0 -23
  36. package/src/commands/uniteCommands.ts +0 -140
  37. package/src/components/AbstractComponent.transformBy.test.ts +0 -23
  38. package/src/components/AbstractComponent.ts +0 -383
  39. package/src/components/BackgroundComponent.test.ts +0 -44
  40. package/src/components/BackgroundComponent.ts +0 -348
  41. package/src/components/ImageComponent.ts +0 -176
  42. package/src/components/RestylableComponent.ts +0 -161
  43. package/src/components/SVGGlobalAttributesObject.ts +0 -79
  44. package/src/components/Stroke.test.ts +0 -137
  45. package/src/components/Stroke.ts +0 -294
  46. package/src/components/TextComponent.test.ts +0 -202
  47. package/src/components/TextComponent.ts +0 -429
  48. package/src/components/UnknownSVGObject.test.ts +0 -10
  49. package/src/components/UnknownSVGObject.ts +0 -60
  50. package/src/components/builders/ArrowBuilder.ts +0 -106
  51. package/src/components/builders/CircleBuilder.ts +0 -100
  52. package/src/components/builders/FreehandLineBuilder.test.ts +0 -24
  53. package/src/components/builders/FreehandLineBuilder.ts +0 -210
  54. package/src/components/builders/LineBuilder.ts +0 -77
  55. package/src/components/builders/PressureSensitiveFreehandLineBuilder.ts +0 -453
  56. package/src/components/builders/RectangleBuilder.ts +0 -73
  57. package/src/components/builders/types.ts +0 -15
  58. package/src/components/lib.ts +0 -31
  59. package/src/components/localization.ts +0 -24
  60. package/src/components/util/StrokeSmoother.ts +0 -302
  61. package/src/components/util/describeComponentList.ts +0 -18
  62. package/src/dialogs/makeAboutDialog.ts +0 -82
  63. package/src/inputEvents.ts +0 -143
  64. package/src/lib.ts +0 -91
  65. package/src/localization.ts +0 -34
  66. package/src/localizations/de.ts +0 -146
  67. package/src/localizations/en.ts +0 -8
  68. package/src/localizations/es.ts +0 -74
  69. package/src/localizations/getLocalizationTable.test.ts +0 -27
  70. package/src/localizations/getLocalizationTable.ts +0 -74
  71. package/src/rendering/Display.ts +0 -247
  72. package/src/rendering/RenderablePathSpec.ts +0 -88
  73. package/src/rendering/RenderingStyle.test.ts +0 -68
  74. package/src/rendering/RenderingStyle.ts +0 -55
  75. package/src/rendering/TextRenderingStyle.ts +0 -55
  76. package/src/rendering/caching/CacheRecord.test.ts +0 -48
  77. package/src/rendering/caching/CacheRecord.ts +0 -76
  78. package/src/rendering/caching/CacheRecordManager.ts +0 -71
  79. package/src/rendering/caching/RenderingCache.test.ts +0 -43
  80. package/src/rendering/caching/RenderingCache.ts +0 -66
  81. package/src/rendering/caching/RenderingCacheNode.ts +0 -404
  82. package/src/rendering/caching/testUtils.ts +0 -35
  83. package/src/rendering/caching/types.ts +0 -34
  84. package/src/rendering/lib.ts +0 -8
  85. package/src/rendering/localization.ts +0 -20
  86. package/src/rendering/renderers/AbstractRenderer.ts +0 -232
  87. package/src/rendering/renderers/CanvasRenderer.ts +0 -312
  88. package/src/rendering/renderers/DummyRenderer.test.ts +0 -41
  89. package/src/rendering/renderers/DummyRenderer.ts +0 -142
  90. package/src/rendering/renderers/SVGRenderer.ts +0 -434
  91. package/src/rendering/renderers/TextOnlyRenderer.test.ts +0 -34
  92. package/src/rendering/renderers/TextOnlyRenderer.ts +0 -68
  93. package/src/shortcuts/KeyBinding.test.ts +0 -61
  94. package/src/shortcuts/KeyBinding.ts +0 -257
  95. package/src/shortcuts/KeyboardShortcutManager.test.ts +0 -95
  96. package/src/shortcuts/KeyboardShortcutManager.ts +0 -163
  97. package/src/shortcuts/lib.ts +0 -3
  98. package/src/testing/createEditor.ts +0 -11
  99. package/src/testing/getUniquePointerId.ts +0 -18
  100. package/src/testing/lib.ts +0 -3
  101. package/src/testing/sendPenEvent.ts +0 -36
  102. package/src/testing/sendTouchEvent.ts +0 -71
  103. package/src/toolbar/AbstractToolbar.ts +0 -542
  104. package/src/toolbar/DropdownToolbar.ts +0 -220
  105. package/src/toolbar/EdgeToolbar.test.ts +0 -54
  106. package/src/toolbar/EdgeToolbar.ts +0 -543
  107. package/src/toolbar/IconProvider.ts +0 -861
  108. package/src/toolbar/constants.ts +0 -1
  109. package/src/toolbar/lib.ts +0 -6
  110. package/src/toolbar/localization.ts +0 -136
  111. package/src/toolbar/types.ts +0 -13
  112. package/src/toolbar/widgets/ActionButtonWidget.ts +0 -39
  113. package/src/toolbar/widgets/BaseToolWidget.ts +0 -81
  114. package/src/toolbar/widgets/BaseWidget.ts +0 -495
  115. package/src/toolbar/widgets/DocumentPropertiesWidget.ts +0 -250
  116. package/src/toolbar/widgets/EraserToolWidget.ts +0 -84
  117. package/src/toolbar/widgets/HandToolWidget.ts +0 -239
  118. package/src/toolbar/widgets/InsertImageWidget.ts +0 -248
  119. package/src/toolbar/widgets/OverflowWidget.ts +0 -92
  120. package/src/toolbar/widgets/PenToolWidget.ts +0 -369
  121. package/src/toolbar/widgets/SelectionToolWidget.ts +0 -195
  122. package/src/toolbar/widgets/TextToolWidget.ts +0 -149
  123. package/src/toolbar/widgets/components/makeColorInput.ts +0 -184
  124. package/src/toolbar/widgets/components/makeFileInput.ts +0 -128
  125. package/src/toolbar/widgets/components/makeGridSelector.ts +0 -179
  126. package/src/toolbar/widgets/components/makeSeparator.ts +0 -17
  127. package/src/toolbar/widgets/components/makeThicknessSlider.ts +0 -62
  128. package/src/toolbar/widgets/keybindings.ts +0 -19
  129. package/src/toolbar/widgets/layout/DropdownLayoutManager.ts +0 -262
  130. package/src/toolbar/widgets/layout/EdgeToolbarLayoutManager.ts +0 -71
  131. package/src/toolbar/widgets/layout/types.ts +0 -74
  132. package/src/toolbar/widgets/lib.ts +0 -13
  133. package/src/tools/BaseTool.ts +0 -169
  134. package/src/tools/Eraser.test.ts +0 -103
  135. package/src/tools/Eraser.ts +0 -173
  136. package/src/tools/FindTool.test.ts +0 -67
  137. package/src/tools/FindTool.ts +0 -153
  138. package/src/tools/InputFilter/FunctionMapper.ts +0 -17
  139. package/src/tools/InputFilter/InputMapper.ts +0 -41
  140. package/src/tools/InputFilter/InputPipeline.test.ts +0 -41
  141. package/src/tools/InputFilter/InputPipeline.ts +0 -34
  142. package/src/tools/InputFilter/InputStabilizer.ts +0 -254
  143. package/src/tools/InputFilter/StrokeKeyboardControl.ts +0 -104
  144. package/src/tools/PanZoom.test.ts +0 -339
  145. package/src/tools/PanZoom.ts +0 -525
  146. package/src/tools/PasteHandler.ts +0 -94
  147. package/src/tools/Pen.test.ts +0 -260
  148. package/src/tools/Pen.ts +0 -284
  149. package/src/tools/PipetteTool.ts +0 -84
  150. package/src/tools/SelectionTool/SelectAllShortcutHandler.ts +0 -29
  151. package/src/tools/SelectionTool/Selection.ts +0 -647
  152. package/src/tools/SelectionTool/SelectionHandle.ts +0 -142
  153. package/src/tools/SelectionTool/SelectionTool.test.ts +0 -370
  154. package/src/tools/SelectionTool/SelectionTool.ts +0 -510
  155. package/src/tools/SelectionTool/TransformMode.ts +0 -112
  156. package/src/tools/SelectionTool/types.ts +0 -11
  157. package/src/tools/SoundUITool.ts +0 -221
  158. package/src/tools/TextTool.ts +0 -339
  159. package/src/tools/ToolController.ts +0 -224
  160. package/src/tools/ToolEnabledGroup.ts +0 -14
  161. package/src/tools/ToolSwitcherShortcut.ts +0 -39
  162. package/src/tools/ToolbarShortcutHandler.ts +0 -39
  163. package/src/tools/UndoRedoShortcut.test.ts +0 -62
  164. package/src/tools/UndoRedoShortcut.ts +0 -24
  165. package/src/tools/keybindings.ts +0 -85
  166. package/src/tools/lib.ts +0 -22
  167. package/src/tools/localization.ts +0 -76
  168. package/src/types.ts +0 -151
  169. package/src/util/ReactiveValue.test.ts +0 -168
  170. package/src/util/ReactiveValue.ts +0 -241
  171. package/src/util/assertions.ts +0 -55
  172. package/src/util/fileToBase64.ts +0 -18
  173. package/src/util/guessKeyCodeFromKey.ts +0 -36
  174. package/src/util/listPrefixMatch.ts +0 -19
  175. package/src/util/stopPropagationOfScrollingWheelEvents.ts +0 -20
  176. package/src/util/untilNextAnimationFrame.ts +0 -9
  177. package/src/util/waitForAll.ts +0 -18
  178. package/src/util/waitForTimeout.ts +0 -9
  179. package/src/version.test.ts +0 -12
  180. package/src/version.ts +0 -3
  181. package/tools/allLocales.js +0 -4
  182. package/tools/copyREADME.ts +0 -62
@@ -1,250 +0,0 @@
1
- import Erase from '../../commands/Erase';
2
- import SerializableCommand from '../../commands/SerializableCommand';
3
- import uniteCommands from '../../commands/uniteCommands';
4
- import BackgroundComponent, { BackgroundType } from '../../components/BackgroundComponent';
5
- import Editor from '../../Editor';
6
- import { EditorImageEventType } from '../../EditorImage';
7
- import { Rect2, Color4 } from '@js-draw/math';
8
- import { EditorEventType } from '../../types';
9
- import { toolbarCSSPrefix } from '../constants';
10
- import { ToolbarLocalization } from '../localization';
11
- import makeColorInput from './components/makeColorInput';
12
- import BaseWidget from './BaseWidget';
13
-
14
- export default class DocumentPropertiesWidget extends BaseWidget {
15
- private updateDropdownContent: ()=>void = () => {};
16
-
17
- public constructor(editor: Editor, localizationTable?: ToolbarLocalization) {
18
- super(editor, 'document-properties-widget', localizationTable);
19
-
20
- // Make it possible to open the dropdown, even if this widget isn't selected.
21
- this.container.classList.add('dropdownShowable');
22
-
23
- this.editor.notifier.on(EditorEventType.UndoRedoStackUpdated, () => {
24
- this.queueDropdownUpdate();
25
- });
26
-
27
- this.editor.image.notifier.on(EditorImageEventType.ExportViewportChanged, () => {
28
- this.queueDropdownUpdate();
29
- });
30
- }
31
-
32
- protected getTitle(): string {
33
- return this.localizationTable.documentProperties;
34
- }
35
-
36
- protected createIcon(): Element {
37
- return this.editor.icons.makeConfigureDocumentIcon();
38
- }
39
-
40
- protected handleClick() {
41
- this.setDropdownVisible(!this.isDropdownVisible());
42
- this.queueDropdownUpdate();
43
- }
44
-
45
- private dropdownUpdateQueued: boolean = false;
46
- private queueDropdownUpdate() {
47
- if (!this.dropdownUpdateQueued) {
48
- requestAnimationFrame(() => this.updateDropdown());
49
- this.dropdownUpdateQueued = true;
50
- }
51
- }
52
-
53
- private updateDropdown() {
54
- this.dropdownUpdateQueued = false;
55
-
56
- if (this.isDropdownVisible()) {
57
- this.updateDropdownContent();
58
- }
59
- }
60
-
61
- private setBackgroundColor(color: Color4) {
62
- this.editor.dispatch(this.editor.setBackgroundColor(color));
63
- }
64
-
65
- private getBackgroundColor() {
66
- return this.editor.estimateBackgroundColor();
67
- }
68
-
69
- private removeBackgroundComponents(): SerializableCommand {
70
- const previousBackgrounds = [];
71
- for (const component of this.editor.image.getBackgroundComponents()) {
72
- if (component instanceof BackgroundComponent) {
73
- previousBackgrounds.push(component);
74
- }
75
- }
76
-
77
- return new Erase(previousBackgrounds);
78
- }
79
-
80
- /** Replace existing background components with a background of the given type. */
81
- private setBackgroundType(backgroundType: BackgroundType): SerializableCommand {
82
- const prevBackgroundColor = this.editor.estimateBackgroundColor();
83
- const newBackground = new BackgroundComponent(backgroundType, prevBackgroundColor);
84
- const addBackgroundCommand = this.editor.image.addElement(newBackground);
85
-
86
- return uniteCommands([ this.removeBackgroundComponents(), addBackgroundCommand ]);
87
- }
88
-
89
- /** Returns the type of the topmost background component */
90
- private getBackgroundType(): BackgroundType {
91
- const backgroundComponents = this.editor.image.getBackgroundComponents();
92
- for (let i = backgroundComponents.length - 1; i >= 0; i--) {
93
- const component = backgroundComponents[i];
94
- if (component instanceof BackgroundComponent) {
95
- return component.getBackgroundType();
96
- }
97
- }
98
-
99
- return BackgroundType.None;
100
- }
101
-
102
- private updateImportExportRectSize(size: { width?: number, height?: number }) {
103
- const filterDimension = (dim: number|undefined) => {
104
- if (dim !== undefined && (!isFinite(dim) || dim <= 0)) {
105
- dim = 100;
106
- }
107
-
108
- return dim;
109
- };
110
-
111
- const width = filterDimension(size.width);
112
- const height = filterDimension(size.height);
113
-
114
- const currentRect = this.editor.getImportExportRect();
115
- const newRect = new Rect2(
116
- currentRect.x, currentRect.y,
117
- width ?? currentRect.w, height ?? currentRect.h
118
- );
119
-
120
- this.editor.dispatch(this.editor.image.setImportExportRect(newRect));
121
- this.editor.queueRerender();
122
- }
123
-
124
- private static idCounter = 0;
125
-
126
- protected override fillDropdown(dropdown: HTMLElement): boolean {
127
- const container = document.createElement('div');
128
- container.classList.add(
129
- `${toolbarCSSPrefix}spacedList`,
130
- `${toolbarCSSPrefix}nonbutton-controls-main-list`,
131
- `${toolbarCSSPrefix}document-properties-widget`
132
- );
133
-
134
- // Background color input
135
- const backgroundColorRow = document.createElement('div');
136
- const backgroundColorLabel = document.createElement('label');
137
-
138
- backgroundColorLabel.innerText = this.localizationTable.backgroundColor;
139
-
140
- const {
141
- input: colorInput, container: backgroundColorInputContainer, setValue: setBgColorInputValue
142
- } = makeColorInput(this.editor, color => {
143
- if (!color.eq(this.getBackgroundColor())) {
144
- this.setBackgroundColor(color);
145
- }
146
- });
147
-
148
- colorInput.id = `${toolbarCSSPrefix}docPropertiesColorInput-${DocumentPropertiesWidget.idCounter++}`;
149
- backgroundColorLabel.htmlFor = colorInput.id;
150
-
151
- backgroundColorRow.replaceChildren(backgroundColorLabel, backgroundColorInputContainer);
152
-
153
- // Background style selector
154
- const useGridRow = document.createElement('div');
155
- const useGridLabel = document.createElement('label');
156
- const useGridCheckbox = document.createElement('input');
157
-
158
- useGridCheckbox.id = `${toolbarCSSPrefix}docPropertiesUseGridCheckbox-${DocumentPropertiesWidget.idCounter++}`;
159
- useGridLabel.htmlFor = useGridCheckbox.id;
160
-
161
- useGridCheckbox.type = 'checkbox';
162
- useGridLabel.innerText = this.localizationTable.useGridOption;
163
-
164
- useGridCheckbox.oninput = () => {
165
- const prevBackgroundType = this.getBackgroundType();
166
- const wasGrid = prevBackgroundType === BackgroundType.Grid;
167
-
168
- if (wasGrid === useGridCheckbox.checked) {
169
- // Already the requested background type.
170
- return;
171
- }
172
-
173
- let newBackgroundType = BackgroundType.SolidColor;
174
- if (useGridCheckbox.checked) {
175
- newBackgroundType = BackgroundType.Grid;
176
- }
177
-
178
- this.editor.dispatch(this.setBackgroundType(newBackgroundType));
179
- };
180
-
181
- useGridRow.replaceChildren(useGridLabel, useGridCheckbox);
182
-
183
- // Adds a width/height input
184
- const addDimensionRow = (labelContent: string, onChange: (value: number)=>void) => {
185
- const row = document.createElement('div');
186
- const label = document.createElement('label');
187
- const input = document.createElement('input');
188
-
189
- label.innerText = labelContent;
190
- input.type = 'number';
191
- input.min = '0';
192
- input.id = `${toolbarCSSPrefix}docPropertiesDimensionRow-${DocumentPropertiesWidget.idCounter++}`;
193
- label.htmlFor = input.id;
194
-
195
- input.style.flexGrow = '2';
196
- input.style.width = '25px';
197
-
198
- row.style.display = 'flex';
199
-
200
- input.oninput = () => {
201
- onChange(parseFloat(input.value));
202
- };
203
-
204
- row.replaceChildren(label, input);
205
-
206
- return {
207
- setValue: (value: number) => {
208
- input.value = value.toString();
209
- },
210
- element: row,
211
- };
212
- };
213
-
214
- const imageWidthRow = addDimensionRow(this.localizationTable.imageWidthOption, (value: number) => {
215
- this.updateImportExportRectSize({ width: value });
216
- });
217
- const imageHeightRow = addDimensionRow(this.localizationTable.imageHeightOption, (value: number) => {
218
- this.updateImportExportRectSize({ height: value });
219
- });
220
-
221
- // The "About..." button
222
- const aboutButton = document.createElement('button');
223
- aboutButton.classList.add('about-button');
224
- aboutButton.innerText = this.localizationTable.about;
225
-
226
- aboutButton.onclick = () => {
227
- this.editor.showAboutDialog();
228
- };
229
-
230
-
231
- this.updateDropdownContent = () => {
232
- setBgColorInputValue(this.getBackgroundColor());
233
-
234
- const importExportRect = this.editor.getImportExportRect();
235
- imageWidthRow.setValue(importExportRect.width);
236
- imageHeightRow.setValue(importExportRect.height);
237
-
238
- useGridCheckbox.checked = this.getBackgroundType() === BackgroundType.Grid;
239
- };
240
- this.updateDropdownContent();
241
-
242
-
243
- container.replaceChildren(
244
- backgroundColorRow, useGridRow, imageWidthRow.element, imageHeightRow.element, aboutButton
245
- );
246
- dropdown.replaceChildren(container);
247
-
248
- return true;
249
- }
250
- }
@@ -1,84 +0,0 @@
1
- import Editor from '../../Editor';
2
- import Eraser from '../../tools/Eraser';
3
- import { EditorEventType } from '../../types';
4
- import { toolbarCSSPrefix } from '../constants';
5
- import { ToolbarLocalization } from '../localization';
6
- import BaseToolWidget from './BaseToolWidget';
7
- import { SavedToolbuttonState } from './BaseWidget';
8
- import makeThicknessSlider from './components/makeThicknessSlider';
9
-
10
- export default class EraserToolWidget extends BaseToolWidget {
11
- private updateInputs: ()=>void = () => {};
12
-
13
- public constructor(
14
- editor: Editor,
15
- private tool: Eraser,
16
- localizationTable?: ToolbarLocalization
17
- ) {
18
- super(editor, tool, 'eraser-tool-widget', localizationTable);
19
-
20
- this.editor.notifier.on(EditorEventType.ToolUpdated, toolEvt => {
21
- if (toolEvt.kind === EditorEventType.ToolUpdated && toolEvt.tool === this.tool) {
22
- this.updateInputs();
23
- this.updateIcon();
24
- }
25
- });
26
- }
27
-
28
- protected getTitle(): string {
29
- return this.localizationTable.eraser;
30
- }
31
-
32
- protected createIcon(): Element {
33
- return this.editor.icons.makeEraserIcon(this.tool.getThickness());
34
- }
35
-
36
- protected override fillDropdown(dropdown: HTMLElement): boolean {
37
- const container = document.createElement('div');
38
-
39
- container.classList.add(`${toolbarCSSPrefix}spacedList`, `${toolbarCSSPrefix}nonbutton-controls-main-list`);
40
-
41
- const thicknessSlider = makeThicknessSlider(this.editor, thickness => {
42
- this.tool.setThickness(thickness);
43
- });
44
- thicknessSlider.setBounds(10, 55);
45
-
46
- this.updateInputs = () => {
47
- thicknessSlider.setValue(this.tool.getThickness());
48
- };
49
-
50
- this.updateInputs();
51
-
52
- const spacer = document.createElement('div');
53
- spacer.style.height = '5px';
54
-
55
- container.replaceChildren(thicknessSlider.container, spacer);
56
-
57
- dropdown.replaceChildren(container);
58
- return true;
59
- }
60
-
61
- public override serializeState(): SavedToolbuttonState {
62
- return {
63
- ...super.serializeState(),
64
-
65
- thickness: this.tool.getThickness(),
66
- };
67
- }
68
-
69
- public override deserializeFrom(state: SavedToolbuttonState) {
70
- super.deserializeFrom(state);
71
-
72
- if (state.thickness) {
73
- const parsedThickness = parseFloat(state.thickness);
74
-
75
- if (typeof parsedThickness !== 'number' || !isFinite(parsedThickness)) {
76
- throw new Error(
77
- `Deserializing property ${parsedThickness} is not a number or is not finite.`
78
- );
79
- }
80
-
81
- this.tool.setThickness(parsedThickness);
82
- }
83
- }
84
- }
@@ -1,239 +0,0 @@
1
- import Editor from '../../Editor';
2
- import { Mat33 } from '@js-draw/math';
3
- import PanZoom, { PanZoomMode } from '../../tools/PanZoom';
4
- import ToolController from '../../tools/ToolController';
5
- import { EditorEventType } from '../../types';
6
- import Viewport from '../../Viewport';
7
- import { toolbarCSSPrefix } from '../constants';
8
- import { ToolbarLocalization } from '../localization';
9
- import BaseToolWidget from './BaseToolWidget';
10
- import BaseWidget, { SavedToolbuttonState } from './BaseWidget';
11
- import makeSeparator from './components/makeSeparator';
12
-
13
- const makeZoomControl = (localizationTable: ToolbarLocalization, editor: Editor) => {
14
- const zoomLevelRow = document.createElement('div');
15
-
16
- const increaseButton = document.createElement('button');
17
- const decreaseButton = document.createElement('button');
18
- const resetViewButton = document.createElement('button');
19
- const zoomLevelDisplay = document.createElement('span');
20
- increaseButton.innerText = '+';
21
- decreaseButton.innerText = '-';
22
- resetViewButton.innerText = localizationTable.resetView;
23
- zoomLevelRow.replaceChildren(zoomLevelDisplay, increaseButton, decreaseButton, resetViewButton);
24
-
25
- zoomLevelRow.classList.add(`${toolbarCSSPrefix}zoomLevelEditor`);
26
- zoomLevelDisplay.classList.add('zoomDisplay');
27
-
28
- let lastZoom: number|undefined;
29
- const updateZoomDisplay = () => {
30
- let zoomLevel = editor.viewport.getScaleFactor() * 100;
31
-
32
- if (zoomLevel > 0.1) {
33
- zoomLevel = Math.round(zoomLevel * 10) / 10;
34
- } else {
35
- zoomLevel = Math.round(zoomLevel * 1000) / 1000;
36
- }
37
-
38
- if (zoomLevel !== lastZoom) {
39
- zoomLevelDisplay.innerText = localizationTable.zoomLevel(zoomLevel);
40
- lastZoom = zoomLevel;
41
- }
42
- };
43
- updateZoomDisplay();
44
-
45
- editor.notifier.on(EditorEventType.ViewportChanged, (event) => {
46
- if (event.kind === EditorEventType.ViewportChanged) {
47
- updateZoomDisplay();
48
-
49
- // Can't reset if already reset.
50
- resetViewButton.disabled = event.newTransform.eq(Mat33.identity);
51
- }
52
- });
53
-
54
- const zoomBy = (factor: number) => {
55
- const screenCenter = editor.viewport.visibleRect.center;
56
- const transformUpdate = Mat33.scaling2D(factor, screenCenter);
57
- editor.dispatch(Viewport.transformBy(transformUpdate), false);
58
- };
59
-
60
- increaseButton.onclick = () => {
61
- zoomBy(5.0/4);
62
- };
63
-
64
- decreaseButton.onclick = () => {
65
- zoomBy(4.0/5);
66
- };
67
-
68
- resetViewButton.onclick = () => {
69
- const addToHistory = false;
70
- editor.dispatch(Viewport.transformBy(
71
- editor.viewport.canvasToScreenTransform.inverse()
72
- ), addToHistory);
73
- };
74
-
75
- return zoomLevelRow;
76
- };
77
-
78
- class HandModeWidget extends BaseWidget {
79
- public constructor(
80
- editor: Editor,
81
-
82
- protected tool: PanZoom, protected flag: PanZoomMode, protected makeIcon: ()=> Element,
83
- private title: string,
84
-
85
- localizationTable?: ToolbarLocalization,
86
- ) {
87
- super(editor, `pan-mode-${flag}`, localizationTable);
88
-
89
- editor.notifier.on(EditorEventType.ToolUpdated, toolEvt => {
90
- if (toolEvt.kind === EditorEventType.ToolUpdated && toolEvt.tool === tool) {
91
- const allEnabled = !!(tool.getMode() & PanZoomMode.SinglePointerGestures);
92
- this.setSelected(!!(tool.getMode() & flag) || allEnabled);
93
-
94
- // Unless this widget toggles all single pointer gestures, toggling while
95
- // single pointer gestures are enabled should have no effect
96
- this.setDisabled(allEnabled && flag !== PanZoomMode.SinglePointerGestures);
97
- }
98
- });
99
- this.setSelected(false);
100
- }
101
-
102
- private setModeFlag(enabled: boolean) {
103
- this.tool.setModeEnabled(this.flag, enabled);
104
- }
105
-
106
- protected handleClick() {
107
- this.setModeFlag(!this.isSelected());
108
- }
109
-
110
- protected getTitle(): string {
111
- return this.title;
112
- }
113
-
114
- protected createIcon(): Element {
115
- return this.makeIcon();
116
- }
117
-
118
- protected override fillDropdown(_dropdown: HTMLElement): boolean {
119
- return false;
120
- }
121
- }
122
-
123
- export default class HandToolWidget extends BaseToolWidget {
124
- private allowTogglingBaseTool: boolean;
125
-
126
- public constructor(
127
- editor: Editor,
128
-
129
- // Pan zoom tool that overrides all other tools (enabling this tool for a device
130
- // causes that device to pan/zoom instead of interact with the primary tools)
131
- protected overridePanZoomTool: PanZoom,
132
-
133
- localizationTable: ToolbarLocalization,
134
- ) {
135
- const primaryHandTool = HandToolWidget.getPrimaryHandTool(editor.toolController);
136
- const tool = primaryHandTool ?? overridePanZoomTool;
137
- super(editor, tool, 'hand-tool-widget', localizationTable);
138
-
139
- // Only allow toggling a hand tool if we're using the primary hand tool and not the override
140
- // hand tool for this button.
141
- this.allowTogglingBaseTool = primaryHandTool !== null;
142
-
143
- // Allow showing/hiding the dropdown, even if `overridePanZoomTool` isn't enabled.
144
- if (!this.allowTogglingBaseTool) {
145
- this.container.classList.add('dropdownShowable');
146
- }
147
-
148
- // Controls for the overriding hand tool.
149
- const touchPanningWidget = new HandModeWidget(
150
- editor,
151
-
152
- overridePanZoomTool, PanZoomMode.OneFingerTouchGestures,
153
- () => this.editor.icons.makeTouchPanningIcon(),
154
-
155
- localizationTable.touchPanning,
156
-
157
- localizationTable,
158
- );
159
-
160
- const rotationLockWidget = new HandModeWidget(
161
- editor,
162
-
163
- overridePanZoomTool, PanZoomMode.RotationLocked,
164
- () => this.editor.icons.makeRotationLockIcon(),
165
-
166
- localizationTable.lockRotation,
167
- localizationTable,
168
- );
169
-
170
- this.addSubWidget(touchPanningWidget);
171
- this.addSubWidget(rotationLockWidget);
172
- }
173
-
174
- private static getPrimaryHandTool(toolController: ToolController): PanZoom|null {
175
- const primaryPanZoomToolList = toolController.getPrimaryTools().filter(tool => tool instanceof PanZoom);
176
- const primaryPanZoomTool = primaryPanZoomToolList[0];
177
- return primaryPanZoomTool as PanZoom|null;
178
- }
179
-
180
- protected getTitle(): string {
181
- return this.localizationTable.handTool;
182
- }
183
-
184
- protected createIcon(): Element {
185
- return this.editor.icons.makeHandToolIcon();
186
- }
187
-
188
- protected override handleClick(): void {
189
- if (this.allowTogglingBaseTool) {
190
- super.handleClick();
191
- } else {
192
- this.setDropdownVisible(!this.isDropdownVisible());
193
- }
194
- }
195
-
196
- protected override fillDropdown(dropdown: HTMLElement): boolean {
197
- super.fillDropdown(dropdown);
198
-
199
- // The container for all actions that come after the toolbar buttons.
200
- const nonbuttonActionContainer = document.createElement('div');
201
- nonbuttonActionContainer.classList.add(`${toolbarCSSPrefix}nonbutton-controls-main-list`);
202
-
203
- makeSeparator().addTo(nonbuttonActionContainer);
204
- nonbuttonActionContainer.appendChild(
205
- makeZoomControl(this.localizationTable, this.editor)
206
- );
207
- dropdown.appendChild(nonbuttonActionContainer);
208
-
209
- return true;
210
- }
211
-
212
- public override setSelected(selected: boolean): void {
213
- if (this.allowTogglingBaseTool) {
214
- super.setSelected(selected);
215
- }
216
- }
217
-
218
- public override serializeState(): SavedToolbuttonState {
219
- const toolMode = this.overridePanZoomTool.getMode();
220
-
221
- return {
222
- ...super.serializeState(),
223
- touchPanning: toolMode & PanZoomMode.OneFingerTouchGestures,
224
- rotationLocked: toolMode & PanZoomMode.RotationLocked,
225
- };
226
- }
227
-
228
- public override deserializeFrom(state: SavedToolbuttonState): void {
229
- if (state.touchPanning !== undefined) {
230
- this.overridePanZoomTool.setModeEnabled(PanZoomMode.OneFingerTouchGestures, state.touchPanning);
231
- }
232
-
233
- if (state.rotationLocked !== undefined) {
234
- this.overridePanZoomTool.setModeEnabled(PanZoomMode.RotationLocked, state.rotationLocked);
235
- }
236
-
237
- super.deserializeFrom(state);
238
- }
239
- }