js-draw 0.1.6 → 0.1.9
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.
- package/.eslintrc.js +1 -0
- package/CHANGELOG.md +16 -0
- package/README.md +2 -2
- package/dist/bundle.js +1 -1
- package/dist/src/Color4.js +6 -2
- package/dist/src/Editor.d.ts +1 -0
- package/dist/src/Editor.js +23 -8
- package/dist/src/EditorImage.d.ts +8 -13
- package/dist/src/EditorImage.js +51 -29
- package/dist/src/Viewport.d.ts +9 -1
- package/dist/src/Viewport.js +3 -1
- package/dist/src/commands/Command.d.ts +9 -8
- package/dist/src/commands/Command.js +15 -14
- package/dist/src/commands/Duplicate.d.ts +14 -0
- package/dist/src/commands/Duplicate.js +34 -0
- package/dist/src/commands/Erase.d.ts +5 -2
- package/dist/src/commands/Erase.js +28 -9
- package/dist/src/commands/SerializableCommand.d.ts +13 -0
- package/dist/src/commands/SerializableCommand.js +28 -0
- package/dist/src/commands/localization.d.ts +2 -0
- package/dist/src/commands/localization.js +2 -0
- package/dist/src/components/AbstractComponent.d.ts +15 -2
- package/dist/src/components/AbstractComponent.js +122 -26
- package/dist/src/components/SVGGlobalAttributesObject.d.ts +6 -1
- package/dist/src/components/SVGGlobalAttributesObject.js +23 -1
- package/dist/src/components/Stroke.d.ts +5 -0
- package/dist/src/components/Stroke.js +32 -1
- package/dist/src/components/Text.d.ts +11 -4
- package/dist/src/components/Text.js +57 -3
- package/dist/src/components/UnknownSVGObject.d.ts +2 -0
- package/dist/src/components/UnknownSVGObject.js +12 -1
- package/dist/src/components/builders/RectangleBuilder.d.ts +3 -1
- package/dist/src/components/builders/RectangleBuilder.js +17 -8
- package/dist/src/components/util/describeComponentList.d.ts +4 -0
- package/dist/src/components/util/describeComponentList.js +14 -0
- package/dist/src/geometry/Path.d.ts +4 -1
- package/dist/src/geometry/Path.js +4 -0
- package/dist/src/rendering/Display.d.ts +3 -0
- package/dist/src/rendering/Display.js +13 -0
- package/dist/src/rendering/RenderingStyle.d.ts +24 -0
- package/dist/src/rendering/RenderingStyle.js +32 -0
- package/dist/src/rendering/caching/RenderingCacheNode.js +5 -1
- package/dist/src/rendering/renderers/AbstractRenderer.d.ts +1 -8
- package/dist/src/rendering/renderers/AbstractRenderer.js +1 -6
- package/dist/src/rendering/renderers/CanvasRenderer.d.ts +2 -1
- package/dist/src/rendering/renderers/DummyRenderer.d.ts +2 -1
- package/dist/src/rendering/renderers/SVGRenderer.d.ts +2 -1
- package/dist/src/rendering/renderers/TextOnlyRenderer.d.ts +2 -1
- package/dist/src/toolbar/HTMLToolbar.d.ts +2 -1
- package/dist/src/toolbar/HTMLToolbar.js +57 -535
- package/dist/src/toolbar/icons.d.ts +5 -0
- package/dist/src/toolbar/icons.js +186 -13
- package/dist/src/toolbar/localization.d.ts +4 -0
- package/dist/src/toolbar/localization.js +4 -0
- package/dist/src/toolbar/makeColorInput.d.ts +5 -0
- package/dist/src/toolbar/makeColorInput.js +95 -0
- package/dist/src/toolbar/widgets/BaseToolWidget.d.ts +12 -0
- package/dist/src/toolbar/widgets/BaseToolWidget.js +44 -0
- package/dist/src/toolbar/widgets/BaseWidget.d.ts +32 -0
- package/dist/src/toolbar/widgets/BaseWidget.js +148 -0
- package/dist/src/toolbar/widgets/EraserWidget.d.ts +6 -0
- package/dist/src/toolbar/widgets/EraserWidget.js +14 -0
- package/dist/src/toolbar/widgets/HandToolWidget.d.ts +13 -0
- package/dist/src/toolbar/widgets/HandToolWidget.js +133 -0
- package/dist/src/toolbar/widgets/PenWidget.d.ts +20 -0
- package/dist/src/toolbar/widgets/PenWidget.js +131 -0
- package/dist/src/toolbar/widgets/SelectionWidget.d.ts +11 -0
- package/dist/src/toolbar/widgets/SelectionWidget.js +56 -0
- package/dist/src/toolbar/widgets/TextToolWidget.d.ts +13 -0
- package/dist/src/toolbar/widgets/TextToolWidget.js +72 -0
- package/dist/src/tools/Pen.js +1 -1
- package/dist/src/tools/PipetteTool.d.ts +20 -0
- package/dist/src/tools/PipetteTool.js +40 -0
- package/dist/src/tools/SelectionTool.d.ts +2 -0
- package/dist/src/tools/SelectionTool.js +41 -23
- package/dist/src/tools/TextTool.js +1 -1
- package/dist/src/tools/ToolController.d.ts +3 -1
- package/dist/src/tools/ToolController.js +4 -0
- package/dist/src/tools/localization.d.ts +2 -1
- package/dist/src/tools/localization.js +3 -2
- package/dist/src/types.d.ts +7 -2
- package/dist/src/types.js +1 -0
- package/jest.config.js +2 -0
- package/package.json +6 -6
- package/src/Color4.ts +9 -3
- package/src/Editor.ts +28 -11
- package/src/EditorImage.test.ts +5 -5
- package/src/EditorImage.ts +61 -20
- package/src/SVGLoader.ts +2 -1
- package/src/Viewport.ts +2 -1
- package/src/commands/Command.ts +21 -19
- package/src/commands/Duplicate.ts +49 -0
- package/src/commands/Erase.ts +34 -13
- package/src/commands/SerializableCommand.ts +41 -0
- package/src/commands/localization.ts +5 -0
- package/src/components/AbstractComponent.ts +168 -26
- package/src/components/SVGGlobalAttributesObject.ts +34 -2
- package/src/components/Stroke.test.ts +53 -0
- package/src/components/Stroke.ts +37 -2
- package/src/components/Text.test.ts +38 -0
- package/src/components/Text.ts +80 -5
- package/src/components/UnknownSVGObject.test.ts +10 -0
- package/src/components/UnknownSVGObject.ts +15 -1
- package/src/components/builders/FreehandLineBuilder.ts +2 -1
- package/src/components/builders/RectangleBuilder.ts +23 -8
- package/src/components/util/describeComponentList.ts +18 -0
- package/src/geometry/Path.ts +8 -1
- package/src/rendering/Display.ts +17 -1
- package/src/rendering/RenderingStyle.test.ts +68 -0
- package/src/rendering/RenderingStyle.ts +46 -0
- package/src/rendering/caching/RenderingCache.test.ts +1 -1
- package/src/rendering/caching/RenderingCacheNode.ts +6 -1
- package/src/rendering/renderers/AbstractRenderer.ts +1 -15
- package/src/rendering/renderers/CanvasRenderer.ts +2 -1
- package/src/rendering/renderers/DummyRenderer.ts +2 -1
- package/src/rendering/renderers/SVGRenderer.ts +2 -1
- package/src/rendering/renderers/TextOnlyRenderer.ts +2 -1
- package/src/toolbar/HTMLToolbar.ts +64 -661
- package/src/toolbar/icons.ts +205 -13
- package/src/toolbar/localization.ts +10 -2
- package/src/toolbar/makeColorInput.ts +120 -0
- package/src/toolbar/toolbar.css +116 -78
- package/src/toolbar/widgets/BaseToolWidget.ts +53 -0
- package/src/toolbar/widgets/BaseWidget.ts +175 -0
- package/src/toolbar/widgets/EraserWidget.ts +16 -0
- package/src/toolbar/widgets/HandToolWidget.ts +186 -0
- package/src/toolbar/widgets/PenWidget.ts +165 -0
- package/src/toolbar/widgets/SelectionWidget.ts +72 -0
- package/src/toolbar/widgets/TextToolWidget.ts +90 -0
- package/src/tools/Pen.ts +1 -1
- package/src/tools/PipetteTool.ts +56 -0
- package/src/tools/SelectionTool.test.ts +2 -4
- package/src/tools/SelectionTool.ts +47 -27
- package/src/tools/TextTool.ts +1 -1
- package/src/tools/ToolController.ts +10 -6
- package/src/tools/UndoRedoShortcut.test.ts +1 -1
- package/src/tools/localization.ts +6 -3
- package/src/types.ts +12 -1
@@ -5,503 +5,16 @@ import Color4 from '../Color4';
|
|
5
5
|
import Pen from '../tools/Pen';
|
6
6
|
import Eraser from '../tools/Eraser';
|
7
7
|
import SelectionTool from '../tools/SelectionTool';
|
8
|
-
import { makeFreehandLineBuilder } from '../components/builders/FreehandLineBuilder';
|
9
|
-
import { makeArrowBuilder } from '../components/builders/ArrowBuilder';
|
10
|
-
import { makeLineBuilder } from '../components/builders/LineBuilder';
|
11
|
-
import { makeFilledRectangleBuilder, makeOutlinedRectangleBuilder } from '../components/builders/RectangleBuilder';
|
12
8
|
import { defaultToolbarLocalization } from './localization';
|
13
|
-
import {
|
14
|
-
import PanZoom
|
15
|
-
import Mat33 from '../geometry/Mat33';
|
16
|
-
import Viewport from '../Viewport';
|
9
|
+
import { makeRedoIcon, makeUndoIcon } from './icons';
|
10
|
+
import PanZoom from '../tools/PanZoom';
|
17
11
|
import TextTool from '../tools/TextTool';
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
this.icon = null;
|
25
|
-
this.container = document.createElement('div');
|
26
|
-
this.container.classList.add(`${toolbarCSSPrefix}toolContainer`);
|
27
|
-
this.dropdownContainer = document.createElement('div');
|
28
|
-
this.dropdownContainer.classList.add(`${toolbarCSSPrefix}dropdown`);
|
29
|
-
this.dropdownContainer.classList.add('hidden');
|
30
|
-
this.hasDropdown = false;
|
31
|
-
this.button = document.createElement('div');
|
32
|
-
this.button.classList.add(`${toolbarCSSPrefix}button`);
|
33
|
-
this.label = document.createElement('label');
|
34
|
-
this.button.setAttribute('role', 'button');
|
35
|
-
this.button.tabIndex = 0;
|
36
|
-
editor.notifier.on(EditorEventType.ToolEnabled, toolEvt => {
|
37
|
-
if (toolEvt.kind !== EditorEventType.ToolEnabled) {
|
38
|
-
throw new Error('Incorrect event type! (Expected ToolEnabled)');
|
39
|
-
}
|
40
|
-
if (toolEvt.tool === targetTool) {
|
41
|
-
this.updateSelected(true);
|
42
|
-
}
|
43
|
-
});
|
44
|
-
editor.notifier.on(EditorEventType.ToolDisabled, toolEvt => {
|
45
|
-
if (toolEvt.kind !== EditorEventType.ToolDisabled) {
|
46
|
-
throw new Error('Incorrect event type! (Expected ToolDisabled)');
|
47
|
-
}
|
48
|
-
if (toolEvt.tool === targetTool) {
|
49
|
-
this.updateSelected(false);
|
50
|
-
this.setDropdownVisible(false);
|
51
|
-
}
|
52
|
-
});
|
53
|
-
}
|
54
|
-
setupActionBtnClickListener(button) {
|
55
|
-
button.onclick = () => {
|
56
|
-
this.handleClick();
|
57
|
-
};
|
58
|
-
}
|
59
|
-
handleClick() {
|
60
|
-
if (this.hasDropdown) {
|
61
|
-
if (!this.targetTool.isEnabled()) {
|
62
|
-
this.targetTool.setEnabled(true);
|
63
|
-
}
|
64
|
-
else {
|
65
|
-
this.setDropdownVisible(!this.isDropdownVisible());
|
66
|
-
}
|
67
|
-
}
|
68
|
-
else {
|
69
|
-
this.targetTool.setEnabled(!this.targetTool.isEnabled());
|
70
|
-
}
|
71
|
-
}
|
72
|
-
// Adds this to [parent]. This can only be called once for each ToolbarWidget.
|
73
|
-
addTo(parent) {
|
74
|
-
this.label.innerText = this.getTitle();
|
75
|
-
this.setupActionBtnClickListener(this.button);
|
76
|
-
this.icon = null;
|
77
|
-
this.updateIcon();
|
78
|
-
this.updateSelected(this.targetTool.isEnabled());
|
79
|
-
this.button.replaceChildren(this.icon, this.label);
|
80
|
-
this.container.appendChild(this.button);
|
81
|
-
this.hasDropdown = this.fillDropdown(this.dropdownContainer);
|
82
|
-
if (this.hasDropdown) {
|
83
|
-
this.dropdownIcon = this.createDropdownIcon();
|
84
|
-
this.button.appendChild(this.dropdownIcon);
|
85
|
-
this.container.appendChild(this.dropdownContainer);
|
86
|
-
}
|
87
|
-
this.setDropdownVisible(false);
|
88
|
-
parent.appendChild(this.container);
|
89
|
-
}
|
90
|
-
updateIcon() {
|
91
|
-
var _a;
|
92
|
-
const newIcon = this.createIcon();
|
93
|
-
(_a = this.icon) === null || _a === void 0 ? void 0 : _a.replaceWith(newIcon);
|
94
|
-
this.icon = newIcon;
|
95
|
-
this.icon.classList.add(`${toolbarCSSPrefix}icon`);
|
96
|
-
}
|
97
|
-
updateSelected(selected) {
|
98
|
-
const currentlySelected = this.container.classList.contains('selected');
|
99
|
-
if (currentlySelected === selected) {
|
100
|
-
return;
|
101
|
-
}
|
102
|
-
if (selected) {
|
103
|
-
this.container.classList.add('selected');
|
104
|
-
this.button.ariaSelected = 'true';
|
105
|
-
}
|
106
|
-
else {
|
107
|
-
this.container.classList.remove('selected');
|
108
|
-
this.button.ariaSelected = 'false';
|
109
|
-
}
|
110
|
-
}
|
111
|
-
setDropdownVisible(visible) {
|
112
|
-
const currentlyVisible = this.container.classList.contains('dropdownVisible');
|
113
|
-
if (currentlyVisible === visible) {
|
114
|
-
return;
|
115
|
-
}
|
116
|
-
if (visible) {
|
117
|
-
this.dropdownContainer.classList.remove('hidden');
|
118
|
-
this.container.classList.add('dropdownVisible');
|
119
|
-
this.editor.announceForAccessibility(this.localizationTable.dropdownShown(this.targetTool.description));
|
120
|
-
}
|
121
|
-
else {
|
122
|
-
this.dropdownContainer.classList.add('hidden');
|
123
|
-
this.container.classList.remove('dropdownVisible');
|
124
|
-
this.editor.announceForAccessibility(this.localizationTable.dropdownHidden(this.targetTool.description));
|
125
|
-
}
|
126
|
-
this.repositionDropdown();
|
127
|
-
}
|
128
|
-
repositionDropdown() {
|
129
|
-
const dropdownBBox = this.dropdownContainer.getBoundingClientRect();
|
130
|
-
const screenWidth = document.body.clientWidth;
|
131
|
-
if (dropdownBBox.left > screenWidth / 2) {
|
132
|
-
this.dropdownContainer.style.marginLeft = this.button.clientWidth + 'px';
|
133
|
-
this.dropdownContainer.style.transform = 'translate(-100%, 0)';
|
134
|
-
}
|
135
|
-
else {
|
136
|
-
this.dropdownContainer.style.marginLeft = '';
|
137
|
-
this.dropdownContainer.style.transform = '';
|
138
|
-
}
|
139
|
-
}
|
140
|
-
isDropdownVisible() {
|
141
|
-
return !this.dropdownContainer.classList.contains('hidden');
|
142
|
-
}
|
143
|
-
createDropdownIcon() {
|
144
|
-
const icon = makeDropdownIcon();
|
145
|
-
icon.classList.add(`${toolbarCSSPrefix}showHideDropdownIcon`);
|
146
|
-
return icon;
|
147
|
-
}
|
148
|
-
}
|
149
|
-
class EraserWidget extends ToolbarWidget {
|
150
|
-
getTitle() {
|
151
|
-
return this.localizationTable.eraser;
|
152
|
-
}
|
153
|
-
createIcon() {
|
154
|
-
return makeEraserIcon();
|
155
|
-
}
|
156
|
-
fillDropdown(_dropdown) {
|
157
|
-
// No dropdown associated with the eraser
|
158
|
-
return false;
|
159
|
-
}
|
160
|
-
}
|
161
|
-
class SelectionWidget extends ToolbarWidget {
|
162
|
-
constructor(editor, tool, localization) {
|
163
|
-
super(editor, tool, localization);
|
164
|
-
this.tool = tool;
|
165
|
-
}
|
166
|
-
getTitle() {
|
167
|
-
return this.localizationTable.select;
|
168
|
-
}
|
169
|
-
createIcon() {
|
170
|
-
return makeSelectionIcon();
|
171
|
-
}
|
172
|
-
fillDropdown(dropdown) {
|
173
|
-
const container = document.createElement('div');
|
174
|
-
const resizeButton = document.createElement('button');
|
175
|
-
const deleteButton = document.createElement('button');
|
176
|
-
resizeButton.innerText = this.localizationTable.resizeImageToSelection;
|
177
|
-
resizeButton.disabled = true;
|
178
|
-
deleteButton.innerText = this.localizationTable.deleteSelection;
|
179
|
-
deleteButton.disabled = true;
|
180
|
-
resizeButton.onclick = () => {
|
181
|
-
const selection = this.tool.getSelection();
|
182
|
-
this.editor.dispatch(this.editor.setImportExportRect(selection.region));
|
183
|
-
};
|
184
|
-
deleteButton.onclick = () => {
|
185
|
-
const selection = this.tool.getSelection();
|
186
|
-
this.editor.dispatch(selection.deleteSelectedObjects());
|
187
|
-
this.tool.clearSelection();
|
188
|
-
};
|
189
|
-
// Enable/disable actions based on whether items are selected
|
190
|
-
this.editor.notifier.on(EditorEventType.ToolUpdated, toolEvt => {
|
191
|
-
if (toolEvt.kind !== EditorEventType.ToolUpdated) {
|
192
|
-
throw new Error('Invalid event type!');
|
193
|
-
}
|
194
|
-
if (toolEvt.tool === this.tool) {
|
195
|
-
const selection = this.tool.getSelection();
|
196
|
-
const hasSelection = selection && selection.region.area > 0;
|
197
|
-
resizeButton.disabled = !hasSelection;
|
198
|
-
deleteButton.disabled = resizeButton.disabled;
|
199
|
-
}
|
200
|
-
});
|
201
|
-
container.replaceChildren(resizeButton, deleteButton);
|
202
|
-
dropdown.appendChild(container);
|
203
|
-
return true;
|
204
|
-
}
|
205
|
-
}
|
206
|
-
const makeZoomControl = (localizationTable, editor) => {
|
207
|
-
const zoomLevelRow = document.createElement('div');
|
208
|
-
const increaseButton = document.createElement('button');
|
209
|
-
const decreaseButton = document.createElement('button');
|
210
|
-
const zoomLevelDisplay = document.createElement('span');
|
211
|
-
increaseButton.innerText = '+';
|
212
|
-
decreaseButton.innerText = '-';
|
213
|
-
zoomLevelRow.replaceChildren(zoomLevelDisplay, increaseButton, decreaseButton);
|
214
|
-
zoomLevelRow.classList.add(`${toolbarCSSPrefix}zoomLevelEditor`);
|
215
|
-
zoomLevelDisplay.classList.add('zoomDisplay');
|
216
|
-
let lastZoom;
|
217
|
-
const updateZoomDisplay = () => {
|
218
|
-
let zoomLevel = editor.viewport.getScaleFactor() * 100;
|
219
|
-
if (zoomLevel > 0.1) {
|
220
|
-
zoomLevel = Math.round(zoomLevel * 10) / 10;
|
221
|
-
}
|
222
|
-
else {
|
223
|
-
zoomLevel = Math.round(zoomLevel * 1000) / 1000;
|
224
|
-
}
|
225
|
-
if (zoomLevel !== lastZoom) {
|
226
|
-
zoomLevelDisplay.innerText = localizationTable.zoomLevel(zoomLevel);
|
227
|
-
lastZoom = zoomLevel;
|
228
|
-
}
|
229
|
-
};
|
230
|
-
updateZoomDisplay();
|
231
|
-
editor.notifier.on(EditorEventType.ViewportChanged, (event) => {
|
232
|
-
if (event.kind === EditorEventType.ViewportChanged) {
|
233
|
-
updateZoomDisplay();
|
234
|
-
}
|
235
|
-
});
|
236
|
-
const zoomBy = (factor) => {
|
237
|
-
const screenCenter = editor.viewport.visibleRect.center;
|
238
|
-
const transformUpdate = Mat33.scaling2D(factor, screenCenter);
|
239
|
-
editor.dispatch(new Viewport.ViewportTransform(transformUpdate), false);
|
240
|
-
};
|
241
|
-
increaseButton.onclick = () => {
|
242
|
-
zoomBy(5.0 / 4);
|
243
|
-
};
|
244
|
-
decreaseButton.onclick = () => {
|
245
|
-
zoomBy(4.0 / 5);
|
246
|
-
};
|
247
|
-
return zoomLevelRow;
|
248
|
-
};
|
249
|
-
class HandToolWidget extends ToolbarWidget {
|
250
|
-
constructor(editor, tool, localizationTable) {
|
251
|
-
super(editor, tool, localizationTable);
|
252
|
-
this.tool = tool;
|
253
|
-
this.container.classList.add('dropdownShowable');
|
254
|
-
}
|
255
|
-
getTitle() {
|
256
|
-
return this.localizationTable.handTool;
|
257
|
-
}
|
258
|
-
createIcon() {
|
259
|
-
return makeHandToolIcon();
|
260
|
-
}
|
261
|
-
fillDropdown(dropdown) {
|
262
|
-
let idCounter = 0;
|
263
|
-
const addCheckbox = (label, onToggle) => {
|
264
|
-
const rowContainer = document.createElement('div');
|
265
|
-
const labelElem = document.createElement('label');
|
266
|
-
const checkboxElem = document.createElement('input');
|
267
|
-
checkboxElem.type = 'checkbox';
|
268
|
-
checkboxElem.id = `${toolbarCSSPrefix}hand-tool-option-${idCounter++}`;
|
269
|
-
labelElem.setAttribute('for', checkboxElem.id);
|
270
|
-
checkboxElem.oninput = () => {
|
271
|
-
onToggle(checkboxElem.checked);
|
272
|
-
};
|
273
|
-
labelElem.innerText = label;
|
274
|
-
rowContainer.replaceChildren(checkboxElem, labelElem);
|
275
|
-
dropdown.appendChild(rowContainer);
|
276
|
-
return checkboxElem;
|
277
|
-
};
|
278
|
-
const setModeFlag = (enabled, flag) => {
|
279
|
-
const mode = this.tool.getMode();
|
280
|
-
if (enabled) {
|
281
|
-
this.tool.setMode(mode | flag);
|
282
|
-
}
|
283
|
-
else {
|
284
|
-
this.tool.setMode(mode & ~flag);
|
285
|
-
}
|
286
|
-
};
|
287
|
-
const touchPanningCheckbox = addCheckbox(this.localizationTable.touchPanning, checked => {
|
288
|
-
setModeFlag(checked, PanZoomMode.OneFingerTouchGestures);
|
289
|
-
});
|
290
|
-
const anyDevicePanningCheckbox = addCheckbox(this.localizationTable.anyDevicePanning, checked => {
|
291
|
-
setModeFlag(checked, PanZoomMode.SinglePointerGestures);
|
292
|
-
});
|
293
|
-
dropdown.appendChild(makeZoomControl(this.localizationTable, this.editor));
|
294
|
-
const updateInputs = () => {
|
295
|
-
const mode = this.tool.getMode();
|
296
|
-
anyDevicePanningCheckbox.checked = !!(mode & PanZoomMode.SinglePointerGestures);
|
297
|
-
if (anyDevicePanningCheckbox.checked) {
|
298
|
-
touchPanningCheckbox.checked = true;
|
299
|
-
touchPanningCheckbox.disabled = true;
|
300
|
-
}
|
301
|
-
else {
|
302
|
-
touchPanningCheckbox.checked = !!(mode & PanZoomMode.OneFingerTouchGestures);
|
303
|
-
touchPanningCheckbox.disabled = false;
|
304
|
-
}
|
305
|
-
};
|
306
|
-
updateInputs();
|
307
|
-
this.editor.notifier.on(EditorEventType.ToolUpdated, event => {
|
308
|
-
if (event.kind === EditorEventType.ToolUpdated && event.tool === this.tool) {
|
309
|
-
updateInputs();
|
310
|
-
}
|
311
|
-
});
|
312
|
-
return true;
|
313
|
-
}
|
314
|
-
updateSelected(_active) {
|
315
|
-
}
|
316
|
-
handleClick() {
|
317
|
-
this.setDropdownVisible(!this.isDropdownVisible());
|
318
|
-
}
|
319
|
-
}
|
320
|
-
class TextToolWidget extends ToolbarWidget {
|
321
|
-
constructor(editor, tool, localization) {
|
322
|
-
super(editor, tool, localization);
|
323
|
-
this.tool = tool;
|
324
|
-
this.updateDropdownInputs = null;
|
325
|
-
editor.notifier.on(EditorEventType.ToolUpdated, evt => {
|
326
|
-
var _a;
|
327
|
-
if (evt.kind === EditorEventType.ToolUpdated && evt.tool === tool) {
|
328
|
-
this.updateIcon();
|
329
|
-
(_a = this.updateDropdownInputs) === null || _a === void 0 ? void 0 : _a.call(this);
|
330
|
-
}
|
331
|
-
});
|
332
|
-
}
|
333
|
-
getTitle() {
|
334
|
-
return this.targetTool.description;
|
335
|
-
}
|
336
|
-
createIcon() {
|
337
|
-
const textStyle = this.tool.getTextStyle();
|
338
|
-
return makeTextIcon(textStyle);
|
339
|
-
}
|
340
|
-
fillDropdown(dropdown) {
|
341
|
-
const fontRow = document.createElement('div');
|
342
|
-
const colorRow = document.createElement('div');
|
343
|
-
const fontInput = document.createElement('select');
|
344
|
-
const fontLabel = document.createElement('label');
|
345
|
-
const colorInput = document.createElement('input');
|
346
|
-
const colorLabel = document.createElement('label');
|
347
|
-
const fontsInInput = new Set();
|
348
|
-
const addFontToInput = (fontName) => {
|
349
|
-
const option = document.createElement('option');
|
350
|
-
option.value = fontName;
|
351
|
-
option.textContent = fontName;
|
352
|
-
fontInput.appendChild(option);
|
353
|
-
fontsInInput.add(fontName);
|
354
|
-
};
|
355
|
-
fontLabel.innerText = this.localizationTable.fontLabel;
|
356
|
-
colorLabel.innerText = this.localizationTable.colorLabel;
|
357
|
-
colorInput.classList.add('coloris_input');
|
358
|
-
colorInput.type = 'button';
|
359
|
-
colorInput.id = `${toolbarCSSPrefix}-text-color-input-${TextToolWidget.idCounter++}`;
|
360
|
-
colorLabel.setAttribute('for', colorInput.id);
|
361
|
-
addFontToInput('monospace');
|
362
|
-
addFontToInput('serif');
|
363
|
-
addFontToInput('sans-serif');
|
364
|
-
fontInput.id = `${toolbarCSSPrefix}-text-font-input-${TextToolWidget.idCounter++}`;
|
365
|
-
fontLabel.setAttribute('for', fontInput.id);
|
366
|
-
fontInput.onchange = () => {
|
367
|
-
this.tool.setFontFamily(fontInput.value);
|
368
|
-
};
|
369
|
-
colorInput.oninput = () => {
|
370
|
-
this.tool.setColor(Color4.fromString(colorInput.value));
|
371
|
-
};
|
372
|
-
colorRow.appendChild(colorLabel);
|
373
|
-
colorRow.appendChild(colorInput);
|
374
|
-
fontRow.appendChild(fontLabel);
|
375
|
-
fontRow.appendChild(fontInput);
|
376
|
-
this.updateDropdownInputs = () => {
|
377
|
-
const style = this.tool.getTextStyle();
|
378
|
-
colorInput.value = style.renderingStyle.fill.toHexString();
|
379
|
-
if (!fontsInInput.has(style.fontFamily)) {
|
380
|
-
addFontToInput(style.fontFamily);
|
381
|
-
}
|
382
|
-
fontInput.value = style.fontFamily;
|
383
|
-
};
|
384
|
-
this.updateDropdownInputs();
|
385
|
-
dropdown.replaceChildren(colorRow, fontRow);
|
386
|
-
return true;
|
387
|
-
}
|
388
|
-
}
|
389
|
-
TextToolWidget.idCounter = 0;
|
390
|
-
class PenWidget extends ToolbarWidget {
|
391
|
-
constructor(editor, tool, localization, penTypes) {
|
392
|
-
super(editor, tool, localization);
|
393
|
-
this.tool = tool;
|
394
|
-
this.penTypes = penTypes;
|
395
|
-
this.updateInputs = () => { };
|
396
|
-
this.editor.notifier.on(EditorEventType.ToolUpdated, toolEvt => {
|
397
|
-
if (toolEvt.kind !== EditorEventType.ToolUpdated) {
|
398
|
-
throw new Error('Invalid event type!');
|
399
|
-
}
|
400
|
-
// The button icon may depend on tool properties.
|
401
|
-
if (toolEvt.tool === this.tool) {
|
402
|
-
this.updateIcon();
|
403
|
-
this.updateInputs();
|
404
|
-
}
|
405
|
-
});
|
406
|
-
}
|
407
|
-
getTitle() {
|
408
|
-
return this.targetTool.description;
|
409
|
-
}
|
410
|
-
createIcon() {
|
411
|
-
const strokeFactory = this.tool.getStrokeFactory();
|
412
|
-
if (strokeFactory === makeFreehandLineBuilder) {
|
413
|
-
// Use a square-root scale to prevent the pen's tip from overflowing.
|
414
|
-
const scale = Math.round(Math.sqrt(this.tool.getThickness()) * 4);
|
415
|
-
const color = this.tool.getColor();
|
416
|
-
return makePenIcon(scale, color.toHexString());
|
417
|
-
}
|
418
|
-
else {
|
419
|
-
const strokeFactory = this.tool.getStrokeFactory();
|
420
|
-
return makeIconFromFactory(this.tool, strokeFactory);
|
421
|
-
}
|
422
|
-
}
|
423
|
-
fillDropdown(dropdown) {
|
424
|
-
const container = document.createElement('div');
|
425
|
-
const thicknessRow = document.createElement('div');
|
426
|
-
const objectTypeRow = document.createElement('div');
|
427
|
-
// Thickness: Value of the input is squared to allow for finer control/larger values.
|
428
|
-
const thicknessLabel = document.createElement('label');
|
429
|
-
const thicknessInput = document.createElement('input');
|
430
|
-
const objectSelectLabel = document.createElement('label');
|
431
|
-
const objectTypeSelect = document.createElement('select');
|
432
|
-
// Give inputs IDs so we can label them with a <label for=...>Label text</label>
|
433
|
-
thicknessInput.id = `${toolbarCSSPrefix}thicknessInput${PenWidget.idCounter++}`;
|
434
|
-
objectTypeSelect.id = `${toolbarCSSPrefix}builderSelect${PenWidget.idCounter++}`;
|
435
|
-
thicknessLabel.innerText = this.localizationTable.thicknessLabel;
|
436
|
-
thicknessLabel.setAttribute('for', thicknessInput.id);
|
437
|
-
objectSelectLabel.innerText = this.localizationTable.selectObjectType;
|
438
|
-
objectSelectLabel.setAttribute('for', objectTypeSelect.id);
|
439
|
-
thicknessInput.type = 'range';
|
440
|
-
thicknessInput.min = '1';
|
441
|
-
thicknessInput.max = '20';
|
442
|
-
thicknessInput.step = '1';
|
443
|
-
thicknessInput.oninput = () => {
|
444
|
-
this.tool.setThickness(Math.pow(parseFloat(thicknessInput.value), 2));
|
445
|
-
};
|
446
|
-
thicknessRow.appendChild(thicknessLabel);
|
447
|
-
thicknessRow.appendChild(thicknessInput);
|
448
|
-
objectTypeSelect.oninput = () => {
|
449
|
-
const penTypeIdx = parseInt(objectTypeSelect.value);
|
450
|
-
if (penTypeIdx < 0 || penTypeIdx >= this.penTypes.length) {
|
451
|
-
console.error('Invalid pen type index', penTypeIdx);
|
452
|
-
return;
|
453
|
-
}
|
454
|
-
this.tool.setStrokeFactory(this.penTypes[penTypeIdx].factory);
|
455
|
-
};
|
456
|
-
objectTypeRow.appendChild(objectSelectLabel);
|
457
|
-
objectTypeRow.appendChild(objectTypeSelect);
|
458
|
-
const colorRow = document.createElement('div');
|
459
|
-
const colorLabel = document.createElement('label');
|
460
|
-
const colorInput = document.createElement('input');
|
461
|
-
colorInput.id = `${toolbarCSSPrefix}colorInput${PenWidget.idCounter++}`;
|
462
|
-
colorLabel.innerText = this.localizationTable.colorLabel;
|
463
|
-
colorLabel.setAttribute('for', colorInput.id);
|
464
|
-
colorInput.className = 'coloris_input';
|
465
|
-
colorInput.type = 'button';
|
466
|
-
colorInput.oninput = () => {
|
467
|
-
this.tool.setColor(Color4.fromHex(colorInput.value));
|
468
|
-
};
|
469
|
-
colorInput.addEventListener('open', () => {
|
470
|
-
this.editor.notifier.dispatch(EditorEventType.ColorPickerToggled, {
|
471
|
-
kind: EditorEventType.ColorPickerToggled,
|
472
|
-
open: true,
|
473
|
-
});
|
474
|
-
});
|
475
|
-
colorInput.addEventListener('close', () => {
|
476
|
-
this.editor.notifier.dispatch(EditorEventType.ColorPickerToggled, {
|
477
|
-
kind: EditorEventType.ColorPickerToggled,
|
478
|
-
open: false,
|
479
|
-
});
|
480
|
-
});
|
481
|
-
colorRow.appendChild(colorLabel);
|
482
|
-
colorRow.appendChild(colorInput);
|
483
|
-
this.updateInputs = () => {
|
484
|
-
colorInput.value = this.tool.getColor().toHexString();
|
485
|
-
thicknessInput.value = Math.sqrt(this.tool.getThickness()).toString();
|
486
|
-
objectTypeSelect.replaceChildren();
|
487
|
-
for (let i = 0; i < this.penTypes.length; i++) {
|
488
|
-
const penType = this.penTypes[i];
|
489
|
-
const option = document.createElement('option');
|
490
|
-
option.value = i.toString();
|
491
|
-
option.innerText = penType.name;
|
492
|
-
objectTypeSelect.appendChild(option);
|
493
|
-
if (penType.factory === this.tool.getStrokeFactory()) {
|
494
|
-
objectTypeSelect.value = i.toString();
|
495
|
-
}
|
496
|
-
}
|
497
|
-
};
|
498
|
-
this.updateInputs();
|
499
|
-
container.replaceChildren(colorRow, thicknessRow, objectTypeRow);
|
500
|
-
dropdown.replaceChildren(container);
|
501
|
-
return true;
|
502
|
-
}
|
503
|
-
}
|
504
|
-
PenWidget.idCounter = 0;
|
12
|
+
import PenWidget from './widgets/PenWidget';
|
13
|
+
import EraserWidget from './widgets/EraserWidget';
|
14
|
+
import { SelectionWidget } from './widgets/SelectionWidget';
|
15
|
+
import TextToolWidget from './widgets/TextToolWidget';
|
16
|
+
import HandToolWidget from './widgets/HandToolWidget';
|
17
|
+
export const toolbarCSSPrefix = 'toolbar-';
|
505
18
|
export default class HTMLToolbar {
|
506
19
|
constructor(editor, parent, localizationTable = defaultToolbarLocalization) {
|
507
20
|
this.editor = editor;
|
@@ -510,51 +23,53 @@ export default class HTMLToolbar {
|
|
510
23
|
this.container.classList.add(`${toolbarCSSPrefix}root`);
|
511
24
|
this.container.setAttribute('role', 'toolbar');
|
512
25
|
parent.appendChild(this.container);
|
513
|
-
|
26
|
+
if (!HTMLToolbar.colorisStarted) {
|
27
|
+
colorisInit();
|
28
|
+
HTMLToolbar.colorisStarted = true;
|
29
|
+
}
|
514
30
|
this.setupColorPickers();
|
515
|
-
// Default pen types
|
516
|
-
this.penTypes = [
|
517
|
-
{
|
518
|
-
name: localizationTable.freehandPen,
|
519
|
-
factory: makeFreehandLineBuilder,
|
520
|
-
},
|
521
|
-
{
|
522
|
-
name: localizationTable.arrowPen,
|
523
|
-
factory: makeArrowBuilder,
|
524
|
-
},
|
525
|
-
{
|
526
|
-
name: localizationTable.linePen,
|
527
|
-
factory: makeLineBuilder,
|
528
|
-
},
|
529
|
-
{
|
530
|
-
name: localizationTable.filledRectanglePen,
|
531
|
-
factory: makeFilledRectangleBuilder,
|
532
|
-
},
|
533
|
-
{
|
534
|
-
name: localizationTable.outlinedRectanglePen,
|
535
|
-
factory: makeOutlinedRectangleBuilder,
|
536
|
-
},
|
537
|
-
];
|
538
31
|
}
|
539
32
|
setupColorPickers() {
|
540
33
|
const closePickerOverlay = document.createElement('div');
|
541
34
|
closePickerOverlay.className = `${toolbarCSSPrefix}closeColorPickerOverlay`;
|
542
35
|
this.editor.createHTMLOverlay(closePickerOverlay);
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
36
|
+
const maxSwatchLen = 12;
|
37
|
+
const swatches = [
|
38
|
+
Color4.red.toHexString(),
|
39
|
+
Color4.purple.toHexString(),
|
40
|
+
Color4.blue.toHexString(),
|
41
|
+
Color4.clay.toHexString(),
|
42
|
+
Color4.black.toHexString(),
|
43
|
+
Color4.white.toHexString(),
|
44
|
+
];
|
45
|
+
const presetColorEnd = swatches.length;
|
46
|
+
// (Re)init Coloris -- update the swatches list.
|
47
|
+
const initColoris = () => {
|
48
|
+
coloris({
|
49
|
+
el: '.coloris_input',
|
50
|
+
format: 'hex',
|
51
|
+
selectInput: false,
|
52
|
+
focusInput: false,
|
53
|
+
themeMode: 'auto',
|
54
|
+
swatches
|
55
|
+
});
|
56
|
+
};
|
57
|
+
initColoris();
|
58
|
+
const addColorToSwatch = (newColor) => {
|
59
|
+
let alreadyPresent = false;
|
60
|
+
for (const color of swatches) {
|
61
|
+
if (color === newColor) {
|
62
|
+
alreadyPresent = true;
|
63
|
+
}
|
64
|
+
}
|
65
|
+
if (!alreadyPresent) {
|
66
|
+
swatches.push(newColor);
|
67
|
+
if (swatches.length > maxSwatchLen) {
|
68
|
+
swatches.splice(presetColorEnd, 1);
|
69
|
+
}
|
70
|
+
initColoris();
|
71
|
+
}
|
72
|
+
};
|
558
73
|
this.editor.notifier.on(EditorEventType.ColorPickerToggled, event => {
|
559
74
|
if (event.kind !== EditorEventType.ColorPickerToggled) {
|
560
75
|
return;
|
@@ -563,6 +78,12 @@ export default class HTMLToolbar {
|
|
563
78
|
// on that shows/hides the color picker.
|
564
79
|
closePickerOverlay.style.display = event.open ? 'block' : 'none';
|
565
80
|
});
|
81
|
+
// Add newly-selected colors to the swatch.
|
82
|
+
this.editor.notifier.on(EditorEventType.ColorPickerColorSelected, event => {
|
83
|
+
if (event.kind === EditorEventType.ColorPickerColorSelected) {
|
84
|
+
addColorToSwatch(event.color.toHexString());
|
85
|
+
}
|
86
|
+
});
|
566
87
|
}
|
567
88
|
addActionButton(title, command, parent) {
|
568
89
|
const button = document.createElement('button');
|
@@ -615,7 +136,7 @@ export default class HTMLToolbar {
|
|
615
136
|
if (!(tool instanceof Pen)) {
|
616
137
|
throw new Error('All `Pen` tools must have kind === ToolType.Pen');
|
617
138
|
}
|
618
|
-
const widget = new PenWidget(this.editor, tool, this.localizationTable
|
139
|
+
const widget = new PenWidget(this.editor, tool, this.localizationTable);
|
619
140
|
widget.addTo(this.container);
|
620
141
|
}
|
621
142
|
for (const tool of toolController.getMatchingTools(ToolType.Eraser)) {
|
@@ -648,3 +169,4 @@ export default class HTMLToolbar {
|
|
648
169
|
this.addUndoRedoButtons();
|
649
170
|
}
|
650
171
|
}
|
172
|
+
HTMLToolbar.colorisStarted = false;
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import Color4 from '../Color4';
|
1
2
|
import { ComponentBuilderFactory } from '../components/builders/types';
|
2
3
|
import { TextStyle } from '../components/Text';
|
3
4
|
import Pen from '../tools/Pen';
|
@@ -7,6 +8,10 @@ export declare const makeDropdownIcon: () => SVGSVGElement;
|
|
7
8
|
export declare const makeEraserIcon: () => SVGSVGElement;
|
8
9
|
export declare const makeSelectionIcon: () => SVGSVGElement;
|
9
10
|
export declare const makeHandToolIcon: () => SVGSVGElement;
|
11
|
+
export declare const makeTouchPanningIcon: () => SVGSVGElement;
|
12
|
+
export declare const makeAllDevicePanningIcon: () => SVGSVGElement;
|
13
|
+
export declare const makeZoomIcon: () => SVGSVGElement;
|
10
14
|
export declare const makeTextIcon: (textStyle: TextStyle) => SVGSVGElement;
|
11
15
|
export declare const makePenIcon: (tipThickness: number, color: string) => SVGSVGElement;
|
12
16
|
export declare const makeIconFromFactory: (pen: Pen, factory: ComponentBuilderFactory) => SVGSVGElement;
|
17
|
+
export declare const makePipetteIcon: (color?: Color4) => SVGSVGElement;
|