js-draw 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/dist/bundle.js +1 -1
- package/dist/src/Editor.d.ts +9 -6
- package/dist/src/Editor.js +9 -3
- package/dist/src/EditorImage.d.ts +3 -0
- package/dist/src/EditorImage.js +7 -0
- package/dist/src/SVGLoader.js +5 -6
- package/dist/src/components/AbstractComponent.d.ts +1 -0
- package/dist/src/components/AbstractComponent.js +4 -0
- package/dist/src/components/SVGGlobalAttributesObject.d.ts +1 -0
- package/dist/src/components/SVGGlobalAttributesObject.js +3 -0
- package/dist/src/components/Text.d.ts +3 -5
- package/dist/src/components/Text.js +19 -10
- package/dist/src/components/UnknownSVGObject.d.ts +1 -0
- package/dist/src/components/UnknownSVGObject.js +3 -0
- package/dist/src/components/builders/FreehandLineBuilder.js +3 -3
- package/dist/src/rendering/renderers/SVGRenderer.js +1 -1
- package/dist/src/testing/beforeEachFile.js +4 -0
- package/dist/src/toolbar/HTMLToolbar.js +2 -3
- package/dist/src/toolbar/IconProvider.d.ts +24 -0
- package/dist/src/toolbar/IconProvider.js +415 -0
- package/dist/src/toolbar/lib.d.ts +1 -1
- package/dist/src/toolbar/lib.js +1 -2
- package/dist/src/toolbar/localization.d.ts +0 -1
- package/dist/src/toolbar/localization.js +0 -1
- package/dist/src/toolbar/makeColorInput.js +1 -2
- package/dist/src/toolbar/widgets/BaseWidget.d.ts +2 -0
- package/dist/src/toolbar/widgets/BaseWidget.js +16 -2
- package/dist/src/toolbar/widgets/EraserToolWidget.js +1 -2
- package/dist/src/toolbar/widgets/HandToolWidget.d.ts +5 -3
- package/dist/src/toolbar/widgets/HandToolWidget.js +35 -12
- package/dist/src/toolbar/widgets/PenToolWidget.d.ts +2 -0
- package/dist/src/toolbar/widgets/PenToolWidget.js +16 -3
- package/dist/src/toolbar/widgets/SelectionToolWidget.d.ts +3 -0
- package/dist/src/toolbar/widgets/SelectionToolWidget.js +20 -7
- package/dist/src/toolbar/widgets/TextToolWidget.js +1 -2
- package/dist/src/tools/PanZoom.d.ts +1 -1
- package/dist/src/tools/PanZoom.js +4 -1
- package/dist/src/tools/SelectionTool/SelectionTool.d.ts +3 -0
- package/dist/src/tools/SelectionTool/SelectionTool.js +66 -3
- package/dist/src/tools/ToolController.js +3 -0
- package/dist/src/tools/ToolbarShortcutHandler.d.ts +12 -0
- package/dist/src/tools/ToolbarShortcutHandler.js +23 -0
- package/dist/src/tools/lib.d.ts +1 -0
- package/dist/src/tools/lib.js +1 -0
- package/dist/src/tools/localization.d.ts +1 -0
- package/dist/src/tools/localization.js +1 -0
- package/dist/src/types.d.ts +4 -2
- package/package.json +1 -1
- package/src/Editor.ts +17 -7
- package/src/EditorImage.ts +9 -0
- package/src/SVGLoader.test.ts +37 -0
- package/src/SVGLoader.ts +5 -6
- package/src/components/AbstractComponent.ts +5 -0
- package/src/components/SVGGlobalAttributesObject.ts +4 -0
- package/src/components/Text.test.ts +1 -16
- package/src/components/Text.ts +21 -11
- package/src/components/UnknownSVGObject.ts +4 -0
- package/src/components/builders/FreehandLineBuilder.ts +3 -3
- package/src/rendering/renderers/SVGRenderer.ts +1 -1
- package/src/testing/beforeEachFile.ts +6 -1
- package/src/toolbar/HTMLToolbar.ts +2 -3
- package/src/toolbar/IconProvider.ts +476 -0
- package/src/toolbar/lib.ts +1 -1
- package/src/toolbar/localization.ts +0 -2
- package/src/toolbar/makeColorInput.ts +1 -2
- package/src/toolbar/widgets/BaseWidget.ts +20 -3
- package/src/toolbar/widgets/EraserToolWidget.ts +1 -2
- package/src/toolbar/widgets/HandToolWidget.ts +42 -20
- package/src/toolbar/widgets/PenToolWidget.ts +20 -4
- package/src/toolbar/widgets/SelectionToolWidget.ts +24 -8
- package/src/toolbar/widgets/TextToolWidget.ts +1 -2
- package/src/tools/PanZoom.ts +4 -1
- package/src/tools/SelectionTool/SelectionTool.css +2 -1
- package/src/tools/SelectionTool/SelectionTool.test.ts +40 -0
- package/src/tools/SelectionTool/SelectionTool.ts +73 -4
- package/src/tools/ToolController.ts +3 -0
- package/src/tools/ToolbarShortcutHandler.ts +34 -0
- package/src/tools/UndoRedoShortcut.test.ts +3 -0
- package/src/tools/lib.ts +1 -0
- package/src/tools/localization.ts +4 -0
- package/src/types.ts +13 -8
- package/typedoc.json +5 -1
- package/dist/src/toolbar/icons.d.ts +0 -20
- package/dist/src/toolbar/icons.js +0 -385
- package/src/toolbar/icons.ts +0 -443
@@ -0,0 +1,415 @@
|
|
1
|
+
import Color4 from '../Color4';
|
2
|
+
import EventDispatcher from '../EventDispatcher';
|
3
|
+
import { Vec2 } from '../math/Vec2';
|
4
|
+
import SVGRenderer from '../rendering/renderers/SVGRenderer';
|
5
|
+
import Viewport from '../Viewport';
|
6
|
+
const svgNamespace = 'http://www.w3.org/2000/svg';
|
7
|
+
const iconColorFill = `
|
8
|
+
style='fill: var(--icon-color);'
|
9
|
+
`;
|
10
|
+
const iconColorStrokeFill = `
|
11
|
+
style='fill: var(--icon-color); stroke: var(--icon-color);'
|
12
|
+
`;
|
13
|
+
const checkerboardPatternDef = `
|
14
|
+
<pattern
|
15
|
+
id='checkerboard'
|
16
|
+
viewBox='0,0,10,10'
|
17
|
+
width='20%'
|
18
|
+
height='20%'
|
19
|
+
patternUnits='userSpaceOnUse'
|
20
|
+
>
|
21
|
+
<rect x=0 y=0 width=10 height=10 fill='white'/>
|
22
|
+
<rect x=0 y=0 width=5 height=5 fill='gray'/>
|
23
|
+
<rect x=5 y=5 width=5 height=5 fill='gray'/>
|
24
|
+
</pattern>
|
25
|
+
`;
|
26
|
+
const checkerboardPatternRef = 'url(#checkerboard)';
|
27
|
+
// Provides icons that can be used in the toolbar, etc.
|
28
|
+
// Extend this class and override methods to customize icons.
|
29
|
+
export default class IconProvider {
|
30
|
+
constructor() {
|
31
|
+
this.makeZoomIcon = () => {
|
32
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
33
|
+
icon.setAttribute('viewBox', '0 0 100 100');
|
34
|
+
const addTextNode = (text, x, y) => {
|
35
|
+
const textNode = document.createElementNS(svgNamespace, 'text');
|
36
|
+
textNode.appendChild(document.createTextNode(text));
|
37
|
+
textNode.setAttribute('x', x.toString());
|
38
|
+
textNode.setAttribute('y', y.toString());
|
39
|
+
textNode.style.textAlign = 'center';
|
40
|
+
textNode.style.textAnchor = 'middle';
|
41
|
+
textNode.style.fontSize = '55px';
|
42
|
+
textNode.style.fill = 'var(--icon-color)';
|
43
|
+
textNode.style.fontFamily = 'monospace';
|
44
|
+
icon.appendChild(textNode);
|
45
|
+
};
|
46
|
+
addTextNode('+', 40, 45);
|
47
|
+
addTextNode('-', 70, 75);
|
48
|
+
return icon;
|
49
|
+
};
|
50
|
+
}
|
51
|
+
makeUndoIcon() {
|
52
|
+
return this.makeRedoIcon(true);
|
53
|
+
}
|
54
|
+
// @param mirror - reflect across the x-axis @internal
|
55
|
+
// @returns a redo icon.
|
56
|
+
makeRedoIcon(mirror = false) {
|
57
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
58
|
+
icon.innerHTML = `
|
59
|
+
<style>
|
60
|
+
.toolbar-svg-undo-redo-icon {
|
61
|
+
stroke: var(--icon-color);
|
62
|
+
stroke-width: 12;
|
63
|
+
stroke-linejoin: round;
|
64
|
+
stroke-linecap: round;
|
65
|
+
fill: none;
|
66
|
+
|
67
|
+
transform-origin: center;
|
68
|
+
}
|
69
|
+
</style>
|
70
|
+
<path
|
71
|
+
d='M20,20 A15,15 0 0 1 70,80 L80,90 L60,70 L65,90 L87,90 L65,80'
|
72
|
+
class='toolbar-svg-undo-redo-icon'
|
73
|
+
style='${mirror ? 'transform: scale(-1, 1);' : ''}'/>
|
74
|
+
`;
|
75
|
+
icon.setAttribute('viewBox', '0 0 100 100');
|
76
|
+
return icon;
|
77
|
+
}
|
78
|
+
makeDropdownIcon() {
|
79
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
80
|
+
icon.innerHTML = `
|
81
|
+
<g>
|
82
|
+
<path
|
83
|
+
d='M5,10 L50,90 L95,10 Z'
|
84
|
+
${iconColorFill}
|
85
|
+
/>
|
86
|
+
</g>
|
87
|
+
`;
|
88
|
+
icon.setAttribute('viewBox', '0 0 100 100');
|
89
|
+
return icon;
|
90
|
+
}
|
91
|
+
makeEraserIcon() {
|
92
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
93
|
+
// Draw an eraser-like shape
|
94
|
+
icon.innerHTML = `
|
95
|
+
<g>
|
96
|
+
<rect x=10 y=50 width=80 height=30 rx=10 fill='pink' />
|
97
|
+
<rect
|
98
|
+
x=10 y=10 width=80 height=50
|
99
|
+
${iconColorFill}
|
100
|
+
/>
|
101
|
+
</g>
|
102
|
+
`;
|
103
|
+
icon.setAttribute('viewBox', '0 0 100 100');
|
104
|
+
return icon;
|
105
|
+
}
|
106
|
+
makeSelectionIcon() {
|
107
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
108
|
+
// Draw a cursor-like shape
|
109
|
+
icon.innerHTML = `
|
110
|
+
<g>
|
111
|
+
<rect x=10 y=10 width=70 height=70 fill='pink' stroke='black'/>
|
112
|
+
<rect x=75 y=75 width=10 height=10 fill='white' stroke='black'/>
|
113
|
+
</g>
|
114
|
+
`;
|
115
|
+
icon.setAttribute('viewBox', '0 0 100 100');
|
116
|
+
return icon;
|
117
|
+
}
|
118
|
+
makeIconFromPath(pathData, fill = 'var(--icon-color)', strokeColor = 'none', strokeWidth = '0px') {
|
119
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
120
|
+
const path = document.createElementNS(svgNamespace, 'path');
|
121
|
+
path.setAttribute('d', pathData);
|
122
|
+
path.style.fill = fill;
|
123
|
+
path.style.stroke = strokeColor;
|
124
|
+
path.style.strokeWidth = strokeWidth;
|
125
|
+
icon.appendChild(path);
|
126
|
+
icon.setAttribute('viewBox', '0 0 100 100');
|
127
|
+
return icon;
|
128
|
+
}
|
129
|
+
makeHandToolIcon() {
|
130
|
+
const fill = 'none';
|
131
|
+
const strokeColor = 'var(--icon-color)';
|
132
|
+
const strokeWidth = '3';
|
133
|
+
// Draw a cursor-like shape (like some of the other icons, made with Inkscape)
|
134
|
+
return this.makeIconFromPath(`
|
135
|
+
m 10,60
|
136
|
+
5,30
|
137
|
+
H 90
|
138
|
+
V 30
|
139
|
+
C 90,20 75,20 75,30
|
140
|
+
V 60
|
141
|
+
20
|
142
|
+
C 75,10 60,10 60,20
|
143
|
+
V 60
|
144
|
+
15
|
145
|
+
C 60,5 45,5 45,15
|
146
|
+
V 60
|
147
|
+
25
|
148
|
+
C 45,15 30,15 30,25
|
149
|
+
V 60
|
150
|
+
75
|
151
|
+
L 25,60
|
152
|
+
C 20,45 10,50 10,60
|
153
|
+
Z
|
154
|
+
`, fill, strokeColor, strokeWidth);
|
155
|
+
}
|
156
|
+
makeTouchPanningIcon() {
|
157
|
+
const fill = 'none';
|
158
|
+
const strokeColor = 'var(--icon-color)';
|
159
|
+
const strokeWidth = '3';
|
160
|
+
return this.makeIconFromPath(`
|
161
|
+
M 5,5.5
|
162
|
+
V 17.2
|
163
|
+
L 16.25,5.46
|
164
|
+
Z
|
165
|
+
|
166
|
+
m 33.75,0
|
167
|
+
L 50,17
|
168
|
+
V 5.5
|
169
|
+
Z
|
170
|
+
|
171
|
+
M 5,40.7
|
172
|
+
v 11.7
|
173
|
+
h 11.25
|
174
|
+
z
|
175
|
+
|
176
|
+
M 26,19
|
177
|
+
C 19.8,19.4 17.65,30.4 21.9,34.8
|
178
|
+
L 50,70
|
179
|
+
H 27.5
|
180
|
+
c -11.25,0 -11.25,17.6 0,17.6
|
181
|
+
H 61.25
|
182
|
+
C 94.9,87.8 95,87.6 95,40.7 78.125,23 67,29 55.6,46.5
|
183
|
+
L 33.1,23
|
184
|
+
C 30.3125,20.128192 27.9,19 25.830078,19.119756
|
185
|
+
Z
|
186
|
+
`, fill, strokeColor, strokeWidth);
|
187
|
+
}
|
188
|
+
makeAllDevicePanningIcon() {
|
189
|
+
const fill = 'none';
|
190
|
+
const strokeColor = 'var(--icon-color)';
|
191
|
+
const strokeWidth = '3';
|
192
|
+
return this.makeIconFromPath(`
|
193
|
+
M 5 5
|
194
|
+
L 5 17.5
|
195
|
+
17.5 5
|
196
|
+
5 5
|
197
|
+
z
|
198
|
+
|
199
|
+
M 42.5 5
|
200
|
+
L 55 17.5
|
201
|
+
55 5
|
202
|
+
42.5 5
|
203
|
+
z
|
204
|
+
|
205
|
+
M 70 10
|
206
|
+
L 70 21
|
207
|
+
61 15
|
208
|
+
55.5 23
|
209
|
+
66 30
|
210
|
+
56 37
|
211
|
+
61 45
|
212
|
+
70 39
|
213
|
+
70 50
|
214
|
+
80 50
|
215
|
+
80 39
|
216
|
+
89 45
|
217
|
+
95 36
|
218
|
+
84 30
|
219
|
+
95 23
|
220
|
+
89 15
|
221
|
+
80 21
|
222
|
+
80 10
|
223
|
+
70 10
|
224
|
+
z
|
225
|
+
|
226
|
+
M 27.5 26.25
|
227
|
+
L 27.5 91.25
|
228
|
+
L 43.75 83.125
|
229
|
+
L 52 99
|
230
|
+
L 68 91
|
231
|
+
L 60 75
|
232
|
+
L 76.25 66.875
|
233
|
+
L 27.5 26.25
|
234
|
+
z
|
235
|
+
|
236
|
+
M 5 42.5
|
237
|
+
L 5 55
|
238
|
+
L 17.5 55
|
239
|
+
L 5 42.5
|
240
|
+
z
|
241
|
+
`, fill, strokeColor, strokeWidth);
|
242
|
+
}
|
243
|
+
makeTextIcon(textStyle) {
|
244
|
+
var _a, _b;
|
245
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
246
|
+
icon.setAttribute('viewBox', '0 0 100 100');
|
247
|
+
const textNode = document.createElementNS(svgNamespace, 'text');
|
248
|
+
textNode.appendChild(document.createTextNode('T'));
|
249
|
+
textNode.style.fontFamily = textStyle.fontFamily;
|
250
|
+
textNode.style.fontWeight = (_a = textStyle.fontWeight) !== null && _a !== void 0 ? _a : '';
|
251
|
+
textNode.style.fontVariant = (_b = textStyle.fontVariant) !== null && _b !== void 0 ? _b : '';
|
252
|
+
textNode.style.fill = textStyle.renderingStyle.fill.toHexString();
|
253
|
+
textNode.style.textAnchor = 'middle';
|
254
|
+
textNode.setAttribute('x', '50');
|
255
|
+
textNode.setAttribute('y', '75');
|
256
|
+
textNode.style.fontSize = '65px';
|
257
|
+
textNode.style.filter = 'drop-shadow(0px 0px 10px var(--primary-shadow-color))';
|
258
|
+
icon.appendChild(textNode);
|
259
|
+
return icon;
|
260
|
+
}
|
261
|
+
makePenIcon(tipThickness, color) {
|
262
|
+
if (color instanceof Color4) {
|
263
|
+
color = color.toHexString();
|
264
|
+
}
|
265
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
266
|
+
icon.setAttribute('viewBox', '0 0 100 100');
|
267
|
+
const halfThickness = tipThickness / 2;
|
268
|
+
// Draw a pen-like shape
|
269
|
+
const primaryStrokeTipPath = `M14,63 L${50 - halfThickness},95 L${50 + halfThickness},90 L88,60 Z`;
|
270
|
+
const backgroundStrokeTipPath = `M14,63 L${50 - halfThickness},85 L${50 + halfThickness},83 L88,60 Z`;
|
271
|
+
icon.innerHTML = `
|
272
|
+
<defs>
|
273
|
+
${checkerboardPatternDef}
|
274
|
+
</defs>
|
275
|
+
<g>
|
276
|
+
<!-- Pen grip -->
|
277
|
+
<path
|
278
|
+
d='M10,10 L90,10 L90,60 L${50 + halfThickness},80 L${50 - halfThickness},80 L10,60 Z'
|
279
|
+
${iconColorStrokeFill}
|
280
|
+
/>
|
281
|
+
</g>
|
282
|
+
<g>
|
283
|
+
<!-- Checkerboard background for slightly transparent pens -->
|
284
|
+
<path d='${backgroundStrokeTipPath}' fill='${checkerboardPatternRef}'/>
|
285
|
+
|
286
|
+
<!-- Actual pen tip -->
|
287
|
+
<path
|
288
|
+
d='${primaryStrokeTipPath}'
|
289
|
+
fill='${color}'
|
290
|
+
stroke='${color}'
|
291
|
+
/>
|
292
|
+
</g>
|
293
|
+
`;
|
294
|
+
return icon;
|
295
|
+
}
|
296
|
+
makeIconFromFactory(pen, factory) {
|
297
|
+
const toolThickness = pen.getThickness();
|
298
|
+
const nowTime = (new Date()).getTime();
|
299
|
+
const startPoint = {
|
300
|
+
pos: Vec2.of(10, 10),
|
301
|
+
width: toolThickness / 5,
|
302
|
+
color: pen.getColor(),
|
303
|
+
time: nowTime - 100,
|
304
|
+
};
|
305
|
+
const endPoint = {
|
306
|
+
pos: Vec2.of(90, 90),
|
307
|
+
width: toolThickness / 5,
|
308
|
+
color: pen.getColor(),
|
309
|
+
time: nowTime,
|
310
|
+
};
|
311
|
+
const viewport = new Viewport(new EventDispatcher());
|
312
|
+
const builder = factory(startPoint, viewport);
|
313
|
+
builder.addPoint(endPoint);
|
314
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
315
|
+
icon.setAttribute('viewBox', '0 0 100 100');
|
316
|
+
viewport.updateScreenSize(Vec2.of(100, 100));
|
317
|
+
const renderer = new SVGRenderer(icon, viewport);
|
318
|
+
builder.preview(renderer);
|
319
|
+
return icon;
|
320
|
+
}
|
321
|
+
makePipetteIcon(color) {
|
322
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
323
|
+
const pipette = document.createElementNS(svgNamespace, 'path');
|
324
|
+
pipette.setAttribute('d', `
|
325
|
+
M 47,6
|
326
|
+
C 35,5 25,15 35,30
|
327
|
+
c -9.2,1.3 -15,0 -15,3
|
328
|
+
0,2 5,5 15,7
|
329
|
+
V 81
|
330
|
+
L 40,90
|
331
|
+
h 6
|
332
|
+
L 40,80
|
333
|
+
V 40
|
334
|
+
h 15
|
335
|
+
v 40
|
336
|
+
l -6,10
|
337
|
+
h 6
|
338
|
+
l 5,-9.2
|
339
|
+
V 40
|
340
|
+
C 70,38 75,35 75,33
|
341
|
+
75,30 69.2,31.2 60,30
|
342
|
+
65,15 65,5 47,6
|
343
|
+
Z
|
344
|
+
`);
|
345
|
+
pipette.style.fill = 'var(--icon-color)';
|
346
|
+
if (color) {
|
347
|
+
const defs = document.createElementNS(svgNamespace, 'defs');
|
348
|
+
defs.innerHTML = checkerboardPatternDef;
|
349
|
+
icon.appendChild(defs);
|
350
|
+
const fluidBackground = document.createElementNS(svgNamespace, 'path');
|
351
|
+
const fluid = document.createElementNS(svgNamespace, 'path');
|
352
|
+
const fluidPathData = `
|
353
|
+
m 40,50 c 5,5 10,0 15,-5 V 80 L 50,90 H 45 L 40,80 Z
|
354
|
+
`;
|
355
|
+
fluid.setAttribute('d', fluidPathData);
|
356
|
+
fluidBackground.setAttribute('d', fluidPathData);
|
357
|
+
fluid.style.fill = color.toHexString();
|
358
|
+
fluidBackground.style.fill = checkerboardPatternRef;
|
359
|
+
icon.appendChild(fluidBackground);
|
360
|
+
icon.appendChild(fluid);
|
361
|
+
}
|
362
|
+
icon.appendChild(pipette);
|
363
|
+
icon.setAttribute('viewBox', '0 0 100 100');
|
364
|
+
return icon;
|
365
|
+
}
|
366
|
+
makeResizeViewportIcon() {
|
367
|
+
return this.makeIconFromPath(`
|
368
|
+
M 75 5 75 10 90 10 90 25 95 25 95 5 75 5 z
|
369
|
+
M 15 15 15 30 20 30 20 20 30 20 30 15 15 15 z
|
370
|
+
M 84 15 82 17 81 16 81 20 85 20 84 19 86 17 84 15 z
|
371
|
+
M 26 24 24 26 26 28 25 29 29 29 29 25 28 26 26 24 z
|
372
|
+
M 25 71 26 72 24 74 26 76 28 74 29 75 29 71 25 71 z
|
373
|
+
M 15 75 15 85 25 85 25 80 20 80 20 75 15 75 z
|
374
|
+
M 90 75 90 90 75 90 75 95 95 95 95 75 90 75 z
|
375
|
+
M 81 81 81 85 82 84 84 86 86 84 84 82 85 81 81 81 z
|
376
|
+
`);
|
377
|
+
}
|
378
|
+
makeDuplicateSelectionIcon() {
|
379
|
+
return this.makeIconFromPath(`
|
380
|
+
M 45,10 45,55 90,55 90,10 45,10 z
|
381
|
+
M 10,25 10,90 70,90 70,60 40,60 40,25 10,25 z
|
382
|
+
`);
|
383
|
+
}
|
384
|
+
makeDeleteSelectionIcon() {
|
385
|
+
const strokeWidth = '5px';
|
386
|
+
const strokeColor = 'var(--icon-color)';
|
387
|
+
const fillColor = 'none';
|
388
|
+
return this.makeIconFromPath(`
|
389
|
+
M 10,10 90,90
|
390
|
+
M 10,90 90,10
|
391
|
+
`, fillColor, strokeColor, strokeWidth);
|
392
|
+
}
|
393
|
+
makeSaveIcon() {
|
394
|
+
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
395
|
+
svg.innerHTML = `
|
396
|
+
<style>
|
397
|
+
.toolbar-save-icon {
|
398
|
+
stroke: var(--icon-color);
|
399
|
+
stroke-width: 10;
|
400
|
+
stroke-linejoin: round;
|
401
|
+
stroke-linecap: round;
|
402
|
+
fill: none;
|
403
|
+
}
|
404
|
+
</style>
|
405
|
+
<path
|
406
|
+
d='
|
407
|
+
M 15,55 30,70 85,20
|
408
|
+
'
|
409
|
+
class='toolbar-save-icon'
|
410
|
+
/>
|
411
|
+
`;
|
412
|
+
svg.setAttribute('viewBox', '0 0 100 100');
|
413
|
+
return svg;
|
414
|
+
}
|
415
|
+
}
|
package/dist/src/toolbar/lib.js
CHANGED
@@ -18,7 +18,6 @@ export const defaultToolbarLocalization = {
|
|
18
18
|
clickToPickColorAnnouncement: 'Click on the screen to pick a color',
|
19
19
|
selectionToolKeyboardShortcuts: 'Selection tool: Use arrow keys to move selected items, lowercase/uppercase ‘i’ and ‘o’ to resize.',
|
20
20
|
touchPanning: 'Touchscreen panning',
|
21
|
-
anyDevicePanning: 'Any device panning',
|
22
21
|
freehandPen: 'Freehand',
|
23
22
|
arrowPen: 'Arrow',
|
24
23
|
linePen: 'Line',
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import Color4 from '../Color4';
|
2
2
|
import PipetteTool from '../tools/PipetteTool';
|
3
3
|
import { EditorEventType } from '../types';
|
4
|
-
import { makePipetteIcon } from './icons';
|
5
4
|
// Returns [ color input, input container ].
|
6
5
|
export const makeColorInput = (editor, onColorChange) => {
|
7
6
|
const colorInputContainer = document.createElement('span');
|
@@ -56,7 +55,7 @@ const addPipetteTool = (editor, container, onColorChange) => {
|
|
56
55
|
pipetteButton.title = editor.localization.pickColorFromScreen;
|
57
56
|
pipetteButton.setAttribute('alt', pipetteButton.title);
|
58
57
|
const updatePipetteIcon = (color) => {
|
59
|
-
pipetteButton.replaceChildren(makePipetteIcon(color));
|
58
|
+
pipetteButton.replaceChildren(editor.icons.makePipetteIcon(color));
|
60
59
|
};
|
61
60
|
updatePipetteIcon();
|
62
61
|
const pipetteTool = editor.toolController.getMatchingTools(PipetteTool)[0];
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import Editor from '../../Editor';
|
2
|
+
import { KeyPressEvent } from '../../types';
|
2
3
|
import { ToolbarLocalization } from '../localization';
|
3
4
|
export default abstract class BaseWidget {
|
4
5
|
#private;
|
@@ -18,6 +19,7 @@ export default abstract class BaseWidget {
|
|
18
19
|
protected abstract createIcon(): Element;
|
19
20
|
protected fillDropdown(dropdown: HTMLElement): boolean;
|
20
21
|
protected setupActionBtnClickListener(button: HTMLElement): void;
|
22
|
+
protected onKeyPress(_event: KeyPressEvent): boolean;
|
21
23
|
protected abstract handleClick(): void;
|
22
24
|
protected get hasDropdown(): boolean;
|
23
25
|
protected addSubWidget(widget: BaseWidget): void;
|
@@ -10,9 +10,9 @@ 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 ToolbarShortcutHandler from '../../tools/ToolbarShortcutHandler';
|
13
14
|
import { EditorEventType, InputEvtType } from '../../types';
|
14
15
|
import { toolbarCSSPrefix } from '../HTMLToolbar';
|
15
|
-
import { makeDropdownIcon } from '../icons';
|
16
16
|
export default class BaseWidget {
|
17
17
|
constructor(editor, localizationTable) {
|
18
18
|
this.editor = editor;
|
@@ -33,6 +33,12 @@ export default class BaseWidget {
|
|
33
33
|
this.label = document.createElement('label');
|
34
34
|
this.button.setAttribute('role', 'button');
|
35
35
|
this.button.tabIndex = 0;
|
36
|
+
const toolbarShortcutHandlers = this.editor.toolController.getMatchingTools(ToolbarShortcutHandler);
|
37
|
+
// If the onKeyPress function has been extended and the editor is configured to send keypress events to
|
38
|
+
// toolbar widgets,
|
39
|
+
if (toolbarShortcutHandlers.length > 0 && this.onKeyPress !== BaseWidget.prototype.onKeyPress) {
|
40
|
+
toolbarShortcutHandlers[0].registerListener(event => this.onKeyPress(event));
|
41
|
+
}
|
36
42
|
}
|
37
43
|
// Add content to the widget's associated dropdown menu.
|
38
44
|
// Returns true if such a menu should be created, false otherwise.
|
@@ -62,6 +68,7 @@ export default class BaseWidget {
|
|
62
68
|
kind: InputEvtType.KeyPressEvent,
|
63
69
|
key: evt.key,
|
64
70
|
ctrlKey: evt.ctrlKey,
|
71
|
+
altKey: evt.altKey,
|
65
72
|
});
|
66
73
|
}
|
67
74
|
};
|
@@ -73,6 +80,7 @@ export default class BaseWidget {
|
|
73
80
|
kind: InputEvtType.KeyUpEvent,
|
74
81
|
key: evt.key,
|
75
82
|
ctrlKey: evt.ctrlKey,
|
83
|
+
altKey: evt.altKey,
|
76
84
|
});
|
77
85
|
};
|
78
86
|
button.onclick = () => {
|
@@ -81,6 +89,12 @@ export default class BaseWidget {
|
|
81
89
|
}
|
82
90
|
};
|
83
91
|
}
|
92
|
+
// Add a listener that is triggered when a key is pressed.
|
93
|
+
// Listeners will fire regardless of whether this widget is selected and require that
|
94
|
+
// {@link lib!Editor.toolController} to have an enabled {@link lib!ToolbarShortcutHandler} tool.
|
95
|
+
onKeyPress(_event) {
|
96
|
+
return false;
|
97
|
+
}
|
84
98
|
get hasDropdown() {
|
85
99
|
return __classPrivateFieldGet(this, _BaseWidget_hasDropdown, "f");
|
86
100
|
}
|
@@ -191,7 +205,7 @@ export default class BaseWidget {
|
|
191
205
|
return this.container.classList.contains('selected');
|
192
206
|
}
|
193
207
|
createDropdownIcon() {
|
194
|
-
const icon = makeDropdownIcon();
|
208
|
+
const icon = this.editor.icons.makeDropdownIcon();
|
195
209
|
icon.classList.add(`${toolbarCSSPrefix}showHideDropdownIcon`);
|
196
210
|
return icon;
|
197
211
|
}
|
@@ -1,11 +1,10 @@
|
|
1
|
-
import { makeEraserIcon } from '../icons';
|
2
1
|
import BaseToolWidget from './BaseToolWidget';
|
3
2
|
export default class EraserToolWidget extends BaseToolWidget {
|
4
3
|
getTitle() {
|
5
4
|
return this.localizationTable.eraser;
|
6
5
|
}
|
7
6
|
createIcon() {
|
8
|
-
return makeEraserIcon();
|
7
|
+
return this.editor.icons.makeEraserIcon();
|
9
8
|
}
|
10
9
|
fillDropdown(_dropdown) {
|
11
10
|
// No dropdown associated with the eraser
|
@@ -3,11 +3,13 @@ import PanZoom from '../../tools/PanZoom';
|
|
3
3
|
import { ToolbarLocalization } from '../localization';
|
4
4
|
import BaseToolWidget from './BaseToolWidget';
|
5
5
|
export default class HandToolWidget extends BaseToolWidget {
|
6
|
-
protected
|
6
|
+
protected overridePanZoomTool: PanZoom;
|
7
7
|
private touchPanningWidget;
|
8
|
-
|
8
|
+
private allowTogglingBaseTool;
|
9
|
+
constructor(editor: Editor, overridePanZoomTool: PanZoom, localizationTable: ToolbarLocalization);
|
10
|
+
private static getPrimaryHandTool;
|
9
11
|
protected getTitle(): string;
|
10
12
|
protected createIcon(): Element;
|
11
|
-
setSelected(_selected: boolean): void;
|
12
13
|
protected handleClick(): void;
|
14
|
+
setSelected(selected: boolean): void;
|
13
15
|
}
|
@@ -1,9 +1,8 @@
|
|
1
1
|
import Mat33 from '../../math/Mat33';
|
2
|
-
import { PanZoomMode } from '../../tools/PanZoom';
|
2
|
+
import PanZoom, { PanZoomMode } from '../../tools/PanZoom';
|
3
3
|
import { EditorEventType } from '../../types';
|
4
4
|
import Viewport from '../../Viewport';
|
5
5
|
import { toolbarCSSPrefix } from '../HTMLToolbar';
|
6
|
-
import { makeAllDevicePanningIcon, makeHandToolIcon, makeTouchPanningIcon, makeZoomIcon } from '../icons';
|
7
6
|
import BaseToolWidget from './BaseToolWidget';
|
8
7
|
import BaseWidget from './BaseWidget';
|
9
8
|
const makeZoomControl = (localizationTable, editor) => {
|
@@ -66,7 +65,7 @@ class ZoomWidget extends BaseWidget {
|
|
66
65
|
return this.localizationTable.zoom;
|
67
66
|
}
|
68
67
|
createIcon() {
|
69
|
-
return makeZoomIcon();
|
68
|
+
return this.editor.icons.makeZoomIcon();
|
70
69
|
}
|
71
70
|
handleClick() {
|
72
71
|
this.setDropdownVisible(!this.isDropdownVisible());
|
@@ -117,24 +116,48 @@ class HandModeWidget extends BaseWidget {
|
|
117
116
|
}
|
118
117
|
}
|
119
118
|
export default class HandToolWidget extends BaseToolWidget {
|
120
|
-
constructor(editor,
|
119
|
+
constructor(editor,
|
120
|
+
// Pan zoom tool that overrides all other tools (enabling this tool for a device
|
121
|
+
// causes that device to pan/zoom instead of interact with the primary tools)
|
122
|
+
overridePanZoomTool, localizationTable) {
|
123
|
+
const primaryHandTool = HandToolWidget.getPrimaryHandTool(editor.toolController);
|
124
|
+
const tool = primaryHandTool !== null && primaryHandTool !== void 0 ? primaryHandTool : overridePanZoomTool;
|
121
125
|
super(editor, tool, localizationTable);
|
122
|
-
this.
|
123
|
-
|
124
|
-
|
126
|
+
this.overridePanZoomTool = overridePanZoomTool;
|
127
|
+
// Only allow toggling a hand tool if we're using the primary hand tool and not the override
|
128
|
+
// hand tool for this button.
|
129
|
+
this.allowTogglingBaseTool = primaryHandTool !== null;
|
130
|
+
// Allow showing/hiding the dropdown, even if `overridePanZoomTool` isn't enabled.
|
131
|
+
if (!this.allowTogglingBaseTool) {
|
132
|
+
this.container.classList.add('dropdownShowable');
|
133
|
+
}
|
134
|
+
// Controls for the overriding hand tool.
|
135
|
+
this.touchPanningWidget = new HandModeWidget(editor, localizationTable, overridePanZoomTool, PanZoomMode.OneFingerTouchGestures, () => this.editor.icons.makeTouchPanningIcon(), localizationTable.touchPanning);
|
125
136
|
this.addSubWidget(this.touchPanningWidget);
|
126
|
-
this.addSubWidget(new HandModeWidget(editor, localizationTable, tool, PanZoomMode.SinglePointerGestures, makeAllDevicePanningIcon, localizationTable.anyDevicePanning));
|
127
137
|
this.addSubWidget(new ZoomWidget(editor, localizationTable));
|
128
138
|
}
|
139
|
+
static getPrimaryHandTool(toolController) {
|
140
|
+
const primaryPanZoomToolList = toolController.getPrimaryTools().filter(tool => tool instanceof PanZoom);
|
141
|
+
const primaryPanZoomTool = primaryPanZoomToolList[0];
|
142
|
+
return primaryPanZoomTool;
|
143
|
+
}
|
129
144
|
getTitle() {
|
130
145
|
return this.localizationTable.handTool;
|
131
146
|
}
|
132
147
|
createIcon() {
|
133
|
-
return makeHandToolIcon();
|
134
|
-
}
|
135
|
-
setSelected(_selected) {
|
148
|
+
return this.editor.icons.makeHandToolIcon();
|
136
149
|
}
|
137
150
|
handleClick() {
|
138
|
-
|
151
|
+
if (this.allowTogglingBaseTool) {
|
152
|
+
super.handleClick();
|
153
|
+
}
|
154
|
+
else {
|
155
|
+
this.setDropdownVisible(!this.isDropdownVisible());
|
156
|
+
}
|
157
|
+
}
|
158
|
+
setSelected(selected) {
|
159
|
+
if (this.allowTogglingBaseTool) {
|
160
|
+
super.setSelected(selected);
|
161
|
+
}
|
139
162
|
}
|
140
163
|
}
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { ComponentBuilderFactory } from '../../components/builders/types';
|
2
2
|
import Editor from '../../Editor';
|
3
3
|
import Pen from '../../tools/Pen';
|
4
|
+
import { KeyPressEvent } from '../../types';
|
4
5
|
import { ToolbarLocalization } from '../localization';
|
5
6
|
import BaseToolWidget from './BaseToolWidget';
|
6
7
|
export interface PenTypeRecord {
|
@@ -16,4 +17,5 @@ export default class PenToolWidget extends BaseToolWidget {
|
|
16
17
|
protected createIcon(): Element;
|
17
18
|
private static idCounter;
|
18
19
|
protected fillDropdown(dropdown: HTMLElement): boolean;
|
20
|
+
protected onKeyPress(event: KeyPressEvent): boolean;
|
19
21
|
}
|