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
@@ -9,6 +9,20 @@ const primaryForegroundFill = `
|
|
9
9
|
const primaryForegroundStrokeFill = `
|
10
10
|
style='fill: var(--primary-foreground-color); stroke: var(--primary-foreground-color);'
|
11
11
|
`;
|
12
|
+
const checkerboardPatternDef = `
|
13
|
+
<pattern
|
14
|
+
id='checkerboard'
|
15
|
+
viewBox='0,0,10,10'
|
16
|
+
width='20%'
|
17
|
+
height='20%'
|
18
|
+
patternUnits='userSpaceOnUse'
|
19
|
+
>
|
20
|
+
<rect x=0 y=0 width=10 height=10 fill='white'/>
|
21
|
+
<rect x=0 y=0 width=5 height=5 fill='gray'/>
|
22
|
+
<rect x=5 y=5 width=5 height=5 fill='gray'/>
|
23
|
+
</pattern>
|
24
|
+
`;
|
25
|
+
const checkerboardPatternRef = 'url(#checkerboard)';
|
12
26
|
export const makeUndoIcon = () => {
|
13
27
|
return makeRedoIcon(true);
|
14
28
|
};
|
@@ -76,7 +90,7 @@ export const makeSelectionIcon = () => {
|
|
76
90
|
};
|
77
91
|
export const makeHandToolIcon = () => {
|
78
92
|
const icon = document.createElementNS(svgNamespace, 'svg');
|
79
|
-
// Draw a cursor-like shape
|
93
|
+
// Draw a cursor-like shape (like some of the other icons, made with Inkscape)
|
80
94
|
icon.innerHTML = `
|
81
95
|
<g>
|
82
96
|
<path d='
|
@@ -111,6 +125,130 @@ export const makeHandToolIcon = () => {
|
|
111
125
|
icon.setAttribute('viewBox', '0 0 100 100');
|
112
126
|
return icon;
|
113
127
|
};
|
128
|
+
export const makeTouchPanningIcon = () => {
|
129
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
130
|
+
icon.innerHTML = `
|
131
|
+
<path
|
132
|
+
d='
|
133
|
+
M 5,5.5
|
134
|
+
V 17.2
|
135
|
+
L 16.25,5.46
|
136
|
+
Z
|
137
|
+
|
138
|
+
m 33.75,0
|
139
|
+
L 50,17
|
140
|
+
V 5.5
|
141
|
+
Z
|
142
|
+
|
143
|
+
M 5,40.7
|
144
|
+
v 11.7
|
145
|
+
h 11.25
|
146
|
+
z
|
147
|
+
|
148
|
+
M 26,19
|
149
|
+
C 19.8,19.4 17.65,30.4 21.9,34.8
|
150
|
+
L 50,70
|
151
|
+
H 27.5
|
152
|
+
c -11.25,0 -11.25,17.6 0,17.6
|
153
|
+
H 61.25
|
154
|
+
C 94.9,87.8 95,87.6 95,40.7 78.125,23 67,29 55.6,46.5
|
155
|
+
L 33.1,23
|
156
|
+
C 30.3125,20.128192 27.9,19 25.830078,19.119756
|
157
|
+
Z
|
158
|
+
'
|
159
|
+
fill='none'
|
160
|
+
style='
|
161
|
+
stroke: var(--primary-foreground-color);
|
162
|
+
stroke-width: 2;
|
163
|
+
'
|
164
|
+
/>
|
165
|
+
`;
|
166
|
+
icon.setAttribute('viewBox', '0 0 100 100');
|
167
|
+
return icon;
|
168
|
+
};
|
169
|
+
export const makeAllDevicePanningIcon = () => {
|
170
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
171
|
+
icon.innerHTML = `
|
172
|
+
<path
|
173
|
+
d='
|
174
|
+
M 5 5
|
175
|
+
L 5 17.5
|
176
|
+
17.5 5
|
177
|
+
5 5
|
178
|
+
z
|
179
|
+
|
180
|
+
M 42.5 5
|
181
|
+
L 55 17.5
|
182
|
+
55 5
|
183
|
+
42.5 5
|
184
|
+
z
|
185
|
+
|
186
|
+
M 70 10
|
187
|
+
L 70 21
|
188
|
+
61 15
|
189
|
+
55.5 23
|
190
|
+
66 30
|
191
|
+
56 37
|
192
|
+
61 45
|
193
|
+
70 39
|
194
|
+
70 50
|
195
|
+
80 50
|
196
|
+
80 39
|
197
|
+
89 45
|
198
|
+
95 36
|
199
|
+
84 30
|
200
|
+
95 23
|
201
|
+
89 15
|
202
|
+
80 21
|
203
|
+
80 10
|
204
|
+
70 10
|
205
|
+
z
|
206
|
+
|
207
|
+
M 27.5 26.25
|
208
|
+
L 27.5 91.25
|
209
|
+
L 43.75 83.125
|
210
|
+
L 52 99
|
211
|
+
L 68 91
|
212
|
+
L 60 75
|
213
|
+
L 76.25 66.875
|
214
|
+
L 27.5 26.25
|
215
|
+
z
|
216
|
+
|
217
|
+
M 5 42.5
|
218
|
+
L 5 55
|
219
|
+
L 17.5 55
|
220
|
+
L 5 42.5
|
221
|
+
z
|
222
|
+
'
|
223
|
+
fill='none'
|
224
|
+
style='
|
225
|
+
stroke: var(--primary-foreground-color);
|
226
|
+
stroke-width: 2;
|
227
|
+
'
|
228
|
+
/>
|
229
|
+
`;
|
230
|
+
icon.setAttribute('viewBox', '0 0 100 100');
|
231
|
+
return icon;
|
232
|
+
};
|
233
|
+
export const makeZoomIcon = () => {
|
234
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
235
|
+
icon.setAttribute('viewBox', '0 0 100 100');
|
236
|
+
const addTextNode = (text, x, y) => {
|
237
|
+
const textNode = document.createElementNS(svgNamespace, 'text');
|
238
|
+
textNode.appendChild(document.createTextNode(text));
|
239
|
+
textNode.setAttribute('x', x.toString());
|
240
|
+
textNode.setAttribute('y', y.toString());
|
241
|
+
textNode.style.textAlign = 'center';
|
242
|
+
textNode.style.textAnchor = 'middle';
|
243
|
+
textNode.style.fontSize = '55px';
|
244
|
+
textNode.style.fill = 'var(--primary-foreground-color)';
|
245
|
+
textNode.style.fontFamily = 'monospace';
|
246
|
+
icon.appendChild(textNode);
|
247
|
+
};
|
248
|
+
addTextNode('+', 40, 45);
|
249
|
+
addTextNode('-', 70, 75);
|
250
|
+
return icon;
|
251
|
+
};
|
114
252
|
export const makeTextIcon = (textStyle) => {
|
115
253
|
var _a, _b;
|
116
254
|
const icon = document.createElementNS(svgNamespace, 'svg');
|
@@ -138,17 +276,7 @@ export const makePenIcon = (tipThickness, color) => {
|
|
138
276
|
const backgroundStrokeTipPath = `M14,63 L${50 - halfThickness},85 L${50 + halfThickness},83 L88,60 Z`;
|
139
277
|
icon.innerHTML = `
|
140
278
|
<defs>
|
141
|
-
|
142
|
-
id='checkerboard'
|
143
|
-
viewBox='0,0,10,10'
|
144
|
-
width='20%'
|
145
|
-
height='20%'
|
146
|
-
patternUnits='userSpaceOnUse'
|
147
|
-
>
|
148
|
-
<rect x=0 y=0 width=10 height=10 fill='white'/>
|
149
|
-
<rect x=0 y=0 width=5 height=5 fill='gray'/>
|
150
|
-
<rect x=5 y=5 width=5 height=5 fill='gray'/>
|
151
|
-
</pattern>
|
279
|
+
${checkerboardPatternDef}
|
152
280
|
</defs>
|
153
281
|
<g>
|
154
282
|
<!-- Pen grip -->
|
@@ -159,7 +287,7 @@ export const makePenIcon = (tipThickness, color) => {
|
|
159
287
|
</g>
|
160
288
|
<g>
|
161
289
|
<!-- Checkerboard background for slightly transparent pens -->
|
162
|
-
<path d='${backgroundStrokeTipPath}' fill='
|
290
|
+
<path d='${backgroundStrokeTipPath}' fill='${checkerboardPatternRef}'/>
|
163
291
|
|
164
292
|
<!-- Actual pen tip -->
|
165
293
|
<path
|
@@ -196,3 +324,48 @@ export const makeIconFromFactory = (pen, factory) => {
|
|
196
324
|
builder.preview(renderer);
|
197
325
|
return icon;
|
198
326
|
};
|
327
|
+
export const makePipetteIcon = (color) => {
|
328
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
329
|
+
const pipette = document.createElementNS(svgNamespace, 'path');
|
330
|
+
pipette.setAttribute('d', `
|
331
|
+
M 47,6
|
332
|
+
C 35,5 25,15 35,30
|
333
|
+
c -9.2,1.3 -15,0 -15,3
|
334
|
+
0,2 5,5 15,7
|
335
|
+
V 81
|
336
|
+
L 40,90
|
337
|
+
h 6
|
338
|
+
L 40,80
|
339
|
+
V 40
|
340
|
+
h 15
|
341
|
+
v 40
|
342
|
+
l -6,10
|
343
|
+
h 6
|
344
|
+
l 5,-9.2
|
345
|
+
V 40
|
346
|
+
C 70,38 75,35 75,33
|
347
|
+
75,30 69.2,31.2 60,30
|
348
|
+
65,15 65,5 47,6
|
349
|
+
Z
|
350
|
+
`);
|
351
|
+
pipette.style.fill = 'var(--primary-foreground-color)';
|
352
|
+
if (color) {
|
353
|
+
const defs = document.createElementNS(svgNamespace, 'defs');
|
354
|
+
defs.innerHTML = checkerboardPatternDef;
|
355
|
+
icon.appendChild(defs);
|
356
|
+
const fluidBackground = document.createElementNS(svgNamespace, 'path');
|
357
|
+
const fluid = document.createElementNS(svgNamespace, 'path');
|
358
|
+
const fluidPathData = `
|
359
|
+
m 40,50 c 5,5 10,0 15,-5 V 80 L 50,90 H 45 L 40,80 Z
|
360
|
+
`;
|
361
|
+
fluid.setAttribute('d', fluidPathData);
|
362
|
+
fluidBackground.setAttribute('d', fluidPathData);
|
363
|
+
fluid.style.fill = color.toHexString();
|
364
|
+
fluidBackground.style.fill = checkerboardPatternRef;
|
365
|
+
icon.appendChild(fluidBackground);
|
366
|
+
icon.appendChild(fluid);
|
367
|
+
}
|
368
|
+
icon.appendChild(pipette);
|
369
|
+
icon.setAttribute('viewBox', '0 0 100 100');
|
370
|
+
return icon;
|
371
|
+
};
|
@@ -16,10 +16,14 @@ export interface ToolbarLocalization {
|
|
16
16
|
thicknessLabel: string;
|
17
17
|
resizeImageToSelection: string;
|
18
18
|
deleteSelection: string;
|
19
|
+
duplicateSelection: string;
|
20
|
+
pickColorFronScreen: string;
|
19
21
|
undo: string;
|
20
22
|
redo: string;
|
23
|
+
zoom: string;
|
21
24
|
dropdownShown: (toolName: string) => string;
|
22
25
|
dropdownHidden: (toolName: string) => string;
|
23
26
|
zoomLevel: (zoomPercentage: number) => string;
|
27
|
+
colorChangedAnnouncement: (color: string) => string;
|
24
28
|
}
|
25
29
|
export declare const defaultToolbarLocalization: ToolbarLocalization;
|
@@ -3,14 +3,17 @@ export const defaultToolbarLocalization = {
|
|
3
3
|
eraser: 'Eraser',
|
4
4
|
select: 'Select',
|
5
5
|
handTool: 'Pan',
|
6
|
+
zoom: 'Zoom',
|
6
7
|
thicknessLabel: 'Thickness: ',
|
7
8
|
colorLabel: 'Color: ',
|
8
9
|
fontLabel: 'Font: ',
|
9
10
|
resizeImageToSelection: 'Resize image to selection',
|
10
11
|
deleteSelection: 'Delete selection',
|
12
|
+
duplicateSelection: 'Duplicate selection',
|
11
13
|
undo: 'Undo',
|
12
14
|
redo: 'Redo',
|
13
15
|
selectObjectType: 'Object type: ',
|
16
|
+
pickColorFronScreen: 'Pick color from screen',
|
14
17
|
touchPanning: 'Touchscreen panning',
|
15
18
|
anyDevicePanning: 'Any device panning',
|
16
19
|
freehandPen: 'Freehand',
|
@@ -21,4 +24,5 @@ export const defaultToolbarLocalization = {
|
|
21
24
|
dropdownShown: (toolName) => `Dropdown for ${toolName} shown`,
|
22
25
|
dropdownHidden: (toolName) => `Dropdown for ${toolName} hidden`,
|
23
26
|
zoomLevel: (zoomPercent) => `Zoom: ${zoomPercent}%`,
|
27
|
+
colorChangedAnnouncement: (color) => `Color changed to ${color}`,
|
24
28
|
};
|
@@ -0,0 +1,5 @@
|
|
1
|
+
import Color4 from '../Color4';
|
2
|
+
import Editor from '../Editor';
|
3
|
+
declare type OnColorChangeListener = (color: Color4) => void;
|
4
|
+
export declare const makeColorInput: (editor: Editor, onColorChange: OnColorChangeListener) => [HTMLInputElement, HTMLElement];
|
5
|
+
export default makeColorInput;
|
@@ -0,0 +1,95 @@
|
|
1
|
+
import Color4 from '../Color4';
|
2
|
+
import { ToolType } from '../tools/ToolController';
|
3
|
+
import { EditorEventType } from '../types';
|
4
|
+
import { makePipetteIcon } from './icons';
|
5
|
+
// Returns [ input, container ].
|
6
|
+
export const makeColorInput = (editor, onColorChange) => {
|
7
|
+
const colorInputContainer = document.createElement('span');
|
8
|
+
const colorInput = document.createElement('input');
|
9
|
+
colorInput.type = 'button';
|
10
|
+
colorInput.classList.add('coloris_input');
|
11
|
+
colorInputContainer.classList.add('color-input-container');
|
12
|
+
colorInputContainer.appendChild(colorInput);
|
13
|
+
addPipetteTool(editor, colorInputContainer, (color) => {
|
14
|
+
colorInput.value = color.toHexString();
|
15
|
+
onInputEnd();
|
16
|
+
// Update the color preview, if it exists (may be managed by Coloris).
|
17
|
+
const parentElem = colorInput.parentElement;
|
18
|
+
if (parentElem && parentElem.classList.contains('clr-field')) {
|
19
|
+
parentElem.style.color = colorInput.value;
|
20
|
+
}
|
21
|
+
});
|
22
|
+
let currentColor;
|
23
|
+
const handleColorInput = () => {
|
24
|
+
currentColor = Color4.fromHex(colorInput.value);
|
25
|
+
};
|
26
|
+
const onInputEnd = () => {
|
27
|
+
handleColorInput();
|
28
|
+
if (currentColor) {
|
29
|
+
editor.announceForAccessibility(editor.localization.colorChangedAnnouncement(currentColor.toHexString()));
|
30
|
+
onColorChange(currentColor);
|
31
|
+
editor.notifier.dispatch(EditorEventType.ColorPickerColorSelected, {
|
32
|
+
kind: EditorEventType.ColorPickerColorSelected,
|
33
|
+
color: currentColor,
|
34
|
+
});
|
35
|
+
}
|
36
|
+
};
|
37
|
+
colorInput.oninput = handleColorInput;
|
38
|
+
colorInput.addEventListener('open', () => {
|
39
|
+
editor.notifier.dispatch(EditorEventType.ColorPickerToggled, {
|
40
|
+
kind: EditorEventType.ColorPickerToggled,
|
41
|
+
open: true,
|
42
|
+
});
|
43
|
+
});
|
44
|
+
colorInput.addEventListener('close', () => {
|
45
|
+
editor.notifier.dispatch(EditorEventType.ColorPickerToggled, {
|
46
|
+
kind: EditorEventType.ColorPickerToggled,
|
47
|
+
open: false,
|
48
|
+
});
|
49
|
+
onInputEnd();
|
50
|
+
});
|
51
|
+
return [colorInput, colorInputContainer];
|
52
|
+
};
|
53
|
+
const addPipetteTool = (editor, container, onColorChange) => {
|
54
|
+
const pipetteButton = document.createElement('button');
|
55
|
+
pipetteButton.classList.add('pipetteButton');
|
56
|
+
pipetteButton.title = editor.localization.pickColorFronScreen;
|
57
|
+
pipetteButton.setAttribute('alt', pipetteButton.title);
|
58
|
+
const updatePipetteIcon = (color) => {
|
59
|
+
pipetteButton.replaceChildren(makePipetteIcon(color));
|
60
|
+
};
|
61
|
+
updatePipetteIcon();
|
62
|
+
const pipetteTool = editor.toolController.getMatchingTools(ToolType.Pipette)[0];
|
63
|
+
const endColorSelectMode = () => {
|
64
|
+
pipetteTool === null || pipetteTool === void 0 ? void 0 : pipetteTool.clearColorListener();
|
65
|
+
updatePipetteIcon();
|
66
|
+
pipetteButton.classList.remove('active');
|
67
|
+
};
|
68
|
+
const pipetteColorSelect = (color) => {
|
69
|
+
endColorSelectMode();
|
70
|
+
if (color) {
|
71
|
+
onColorChange(color);
|
72
|
+
}
|
73
|
+
};
|
74
|
+
const pipetteColorPreview = (color) => {
|
75
|
+
if (color) {
|
76
|
+
updatePipetteIcon(color);
|
77
|
+
}
|
78
|
+
else {
|
79
|
+
updatePipetteIcon();
|
80
|
+
}
|
81
|
+
};
|
82
|
+
pipetteButton.onclick = () => {
|
83
|
+
// If already picking, cancel it.
|
84
|
+
if (pipetteButton.classList.contains('active')) {
|
85
|
+
endColorSelectMode();
|
86
|
+
return;
|
87
|
+
}
|
88
|
+
pipetteTool === null || pipetteTool === void 0 ? void 0 : pipetteTool.setColorListener(pipetteColorPreview, pipetteColorSelect);
|
89
|
+
if (pipetteTool) {
|
90
|
+
pipetteButton.classList.add('active');
|
91
|
+
}
|
92
|
+
};
|
93
|
+
container.appendChild(pipetteButton);
|
94
|
+
};
|
95
|
+
export default makeColorInput;
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import Editor from '../../Editor';
|
2
|
+
import BaseTool from '../../tools/BaseTool';
|
3
|
+
import { ToolbarLocalization } from '../localization';
|
4
|
+
import BaseWidget from './BaseWidget';
|
5
|
+
export default abstract class BaseToolWidget extends BaseWidget {
|
6
|
+
protected editor: Editor;
|
7
|
+
protected targetTool: BaseTool;
|
8
|
+
protected localizationTable: ToolbarLocalization;
|
9
|
+
constructor(editor: Editor, targetTool: BaseTool, localizationTable: ToolbarLocalization);
|
10
|
+
protected handleClick(): void;
|
11
|
+
addTo(parent: HTMLElement): void;
|
12
|
+
}
|
@@ -0,0 +1,44 @@
|
|
1
|
+
import { EditorEventType } from '../../types';
|
2
|
+
import BaseWidget from './BaseWidget';
|
3
|
+
export default class BaseToolWidget extends BaseWidget {
|
4
|
+
constructor(editor, targetTool, localizationTable) {
|
5
|
+
super(editor, localizationTable);
|
6
|
+
this.editor = editor;
|
7
|
+
this.targetTool = targetTool;
|
8
|
+
this.localizationTable = localizationTable;
|
9
|
+
editor.notifier.on(EditorEventType.ToolEnabled, toolEvt => {
|
10
|
+
if (toolEvt.kind !== EditorEventType.ToolEnabled) {
|
11
|
+
throw new Error('Incorrect event type! (Expected ToolEnabled)');
|
12
|
+
}
|
13
|
+
if (toolEvt.tool === targetTool) {
|
14
|
+
this.setSelected(true);
|
15
|
+
}
|
16
|
+
});
|
17
|
+
editor.notifier.on(EditorEventType.ToolDisabled, toolEvt => {
|
18
|
+
if (toolEvt.kind !== EditorEventType.ToolDisabled) {
|
19
|
+
throw new Error('Incorrect event type! (Expected ToolDisabled)');
|
20
|
+
}
|
21
|
+
if (toolEvt.tool === targetTool) {
|
22
|
+
this.setSelected(false);
|
23
|
+
this.setDropdownVisible(false);
|
24
|
+
}
|
25
|
+
});
|
26
|
+
}
|
27
|
+
handleClick() {
|
28
|
+
if (this.hasDropdown) {
|
29
|
+
if (!this.targetTool.isEnabled()) {
|
30
|
+
this.targetTool.setEnabled(true);
|
31
|
+
}
|
32
|
+
else {
|
33
|
+
this.setDropdownVisible(!this.isDropdownVisible());
|
34
|
+
}
|
35
|
+
}
|
36
|
+
else {
|
37
|
+
this.targetTool.setEnabled(!this.targetTool.isEnabled());
|
38
|
+
}
|
39
|
+
}
|
40
|
+
addTo(parent) {
|
41
|
+
super.addTo(parent);
|
42
|
+
this.setSelected(this.targetTool.isEnabled());
|
43
|
+
}
|
44
|
+
}
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import Editor from '../../Editor';
|
2
|
+
import { ToolbarLocalization } from '../localization';
|
3
|
+
export default abstract class BaseWidget {
|
4
|
+
#private;
|
5
|
+
protected editor: Editor;
|
6
|
+
protected localizationTable: ToolbarLocalization;
|
7
|
+
protected readonly container: HTMLElement;
|
8
|
+
private button;
|
9
|
+
private icon;
|
10
|
+
private dropdownContainer;
|
11
|
+
private dropdownIcon;
|
12
|
+
private label;
|
13
|
+
private disabled;
|
14
|
+
private subWidgets;
|
15
|
+
constructor(editor: Editor, localizationTable: ToolbarLocalization);
|
16
|
+
protected abstract getTitle(): string;
|
17
|
+
protected abstract createIcon(): Element;
|
18
|
+
protected fillDropdown(dropdown: HTMLElement): boolean;
|
19
|
+
protected setupActionBtnClickListener(button: HTMLElement): void;
|
20
|
+
protected abstract handleClick(): void;
|
21
|
+
protected get hasDropdown(): boolean;
|
22
|
+
protected addSubWidget(widget: BaseWidget): void;
|
23
|
+
addTo(parent: HTMLElement): void;
|
24
|
+
protected updateIcon(): void;
|
25
|
+
setDisabled(disabled: boolean): void;
|
26
|
+
setSelected(selected: boolean): void;
|
27
|
+
protected setDropdownVisible(visible: boolean): void;
|
28
|
+
protected repositionDropdown(): void;
|
29
|
+
protected isDropdownVisible(): boolean;
|
30
|
+
protected isSelected(): boolean;
|
31
|
+
private createDropdownIcon;
|
32
|
+
}
|
@@ -0,0 +1,148 @@
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
6
|
+
};
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
11
|
+
};
|
12
|
+
var _BaseWidget_hasDropdown;
|
13
|
+
import { toolbarCSSPrefix } from '../HTMLToolbar';
|
14
|
+
import { makeDropdownIcon } from '../icons';
|
15
|
+
export default class BaseWidget {
|
16
|
+
constructor(editor, localizationTable) {
|
17
|
+
this.editor = editor;
|
18
|
+
this.localizationTable = localizationTable;
|
19
|
+
_BaseWidget_hasDropdown.set(this, void 0);
|
20
|
+
this.disabled = false;
|
21
|
+
this.subWidgets = [];
|
22
|
+
this.icon = null;
|
23
|
+
this.container = document.createElement('div');
|
24
|
+
this.container.classList.add(`${toolbarCSSPrefix}toolContainer`);
|
25
|
+
this.dropdownContainer = document.createElement('div');
|
26
|
+
this.dropdownContainer.classList.add(`${toolbarCSSPrefix}dropdown`);
|
27
|
+
this.dropdownContainer.classList.add('hidden');
|
28
|
+
__classPrivateFieldSet(this, _BaseWidget_hasDropdown, false, "f");
|
29
|
+
this.button = document.createElement('div');
|
30
|
+
this.button.classList.add(`${toolbarCSSPrefix}button`);
|
31
|
+
this.label = document.createElement('label');
|
32
|
+
this.button.setAttribute('role', 'button');
|
33
|
+
this.button.tabIndex = 0;
|
34
|
+
}
|
35
|
+
// Add content to the widget's associated dropdown menu.
|
36
|
+
// Returns true if such a menu should be created, false otherwise.
|
37
|
+
fillDropdown(dropdown) {
|
38
|
+
if (this.subWidgets.length === 0) {
|
39
|
+
return false;
|
40
|
+
}
|
41
|
+
for (const widget of this.subWidgets) {
|
42
|
+
widget.addTo(dropdown);
|
43
|
+
}
|
44
|
+
return true;
|
45
|
+
}
|
46
|
+
setupActionBtnClickListener(button) {
|
47
|
+
button.onclick = () => {
|
48
|
+
if (!this.disabled) {
|
49
|
+
this.handleClick();
|
50
|
+
}
|
51
|
+
};
|
52
|
+
}
|
53
|
+
get hasDropdown() {
|
54
|
+
return __classPrivateFieldGet(this, _BaseWidget_hasDropdown, "f");
|
55
|
+
}
|
56
|
+
// Add a widget to this' dropdown. Must be called before this.addTo.
|
57
|
+
addSubWidget(widget) {
|
58
|
+
this.subWidgets.push(widget);
|
59
|
+
}
|
60
|
+
// Adds this to [parent]. This can only be called once for each ToolbarWidget.
|
61
|
+
addTo(parent) {
|
62
|
+
this.label.innerText = this.getTitle();
|
63
|
+
this.setupActionBtnClickListener(this.button);
|
64
|
+
this.icon = null;
|
65
|
+
this.updateIcon();
|
66
|
+
this.button.replaceChildren(this.icon, this.label);
|
67
|
+
this.container.appendChild(this.button);
|
68
|
+
__classPrivateFieldSet(this, _BaseWidget_hasDropdown, this.fillDropdown(this.dropdownContainer), "f");
|
69
|
+
if (__classPrivateFieldGet(this, _BaseWidget_hasDropdown, "f")) {
|
70
|
+
this.dropdownIcon = this.createDropdownIcon();
|
71
|
+
this.button.appendChild(this.dropdownIcon);
|
72
|
+
this.container.appendChild(this.dropdownContainer);
|
73
|
+
}
|
74
|
+
this.setDropdownVisible(false);
|
75
|
+
parent.appendChild(this.container);
|
76
|
+
}
|
77
|
+
updateIcon() {
|
78
|
+
var _a;
|
79
|
+
const newIcon = this.createIcon();
|
80
|
+
(_a = this.icon) === null || _a === void 0 ? void 0 : _a.replaceWith(newIcon);
|
81
|
+
this.icon = newIcon;
|
82
|
+
this.icon.classList.add(`${toolbarCSSPrefix}icon`);
|
83
|
+
}
|
84
|
+
setDisabled(disabled) {
|
85
|
+
this.disabled = disabled;
|
86
|
+
if (this.disabled) {
|
87
|
+
this.button.classList.add('disabled');
|
88
|
+
}
|
89
|
+
else {
|
90
|
+
this.button.classList.remove('disabled');
|
91
|
+
}
|
92
|
+
}
|
93
|
+
setSelected(selected) {
|
94
|
+
const currentlySelected = this.isSelected();
|
95
|
+
if (currentlySelected === selected) {
|
96
|
+
return;
|
97
|
+
}
|
98
|
+
if (selected) {
|
99
|
+
this.container.classList.add('selected');
|
100
|
+
this.button.ariaSelected = 'true';
|
101
|
+
}
|
102
|
+
else {
|
103
|
+
this.container.classList.remove('selected');
|
104
|
+
this.button.ariaSelected = 'false';
|
105
|
+
}
|
106
|
+
}
|
107
|
+
setDropdownVisible(visible) {
|
108
|
+
const currentlyVisible = this.container.classList.contains('dropdownVisible');
|
109
|
+
if (currentlyVisible === visible) {
|
110
|
+
return;
|
111
|
+
}
|
112
|
+
if (visible) {
|
113
|
+
this.dropdownContainer.classList.remove('hidden');
|
114
|
+
this.container.classList.add('dropdownVisible');
|
115
|
+
this.editor.announceForAccessibility(this.localizationTable.dropdownShown(this.getTitle()));
|
116
|
+
}
|
117
|
+
else {
|
118
|
+
this.dropdownContainer.classList.add('hidden');
|
119
|
+
this.container.classList.remove('dropdownVisible');
|
120
|
+
this.editor.announceForAccessibility(this.localizationTable.dropdownHidden(this.getTitle()));
|
121
|
+
}
|
122
|
+
this.repositionDropdown();
|
123
|
+
}
|
124
|
+
repositionDropdown() {
|
125
|
+
const dropdownBBox = this.dropdownContainer.getBoundingClientRect();
|
126
|
+
const screenWidth = document.body.clientWidth;
|
127
|
+
if (dropdownBBox.left > screenWidth / 2) {
|
128
|
+
this.dropdownContainer.style.marginLeft = this.button.clientWidth + 'px';
|
129
|
+
this.dropdownContainer.style.transform = 'translate(-100%, 0)';
|
130
|
+
}
|
131
|
+
else {
|
132
|
+
this.dropdownContainer.style.marginLeft = '';
|
133
|
+
this.dropdownContainer.style.transform = '';
|
134
|
+
}
|
135
|
+
}
|
136
|
+
isDropdownVisible() {
|
137
|
+
return !this.dropdownContainer.classList.contains('hidden');
|
138
|
+
}
|
139
|
+
isSelected() {
|
140
|
+
return this.container.classList.contains('selected');
|
141
|
+
}
|
142
|
+
createDropdownIcon() {
|
143
|
+
const icon = makeDropdownIcon();
|
144
|
+
icon.classList.add(`${toolbarCSSPrefix}showHideDropdownIcon`);
|
145
|
+
return icon;
|
146
|
+
}
|
147
|
+
}
|
148
|
+
_BaseWidget_hasDropdown = new WeakMap();
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { makeEraserIcon } from '../icons';
|
2
|
+
import BaseToolWidget from './BaseToolWidget';
|
3
|
+
export default class EraserWidget extends BaseToolWidget {
|
4
|
+
getTitle() {
|
5
|
+
return this.localizationTable.eraser;
|
6
|
+
}
|
7
|
+
createIcon() {
|
8
|
+
return makeEraserIcon();
|
9
|
+
}
|
10
|
+
fillDropdown(_dropdown) {
|
11
|
+
// No dropdown associated with the eraser
|
12
|
+
return false;
|
13
|
+
}
|
14
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import Editor from '../../Editor';
|
2
|
+
import PanZoom from '../../tools/PanZoom';
|
3
|
+
import { ToolbarLocalization } from '../localization';
|
4
|
+
import BaseToolWidget from './BaseToolWidget';
|
5
|
+
export default class HandToolWidget extends BaseToolWidget {
|
6
|
+
protected tool: PanZoom;
|
7
|
+
private touchPanningWidget;
|
8
|
+
constructor(editor: Editor, tool: PanZoom, localizationTable: ToolbarLocalization);
|
9
|
+
protected getTitle(): string;
|
10
|
+
protected createIcon(): Element;
|
11
|
+
setSelected(_selected: boolean): void;
|
12
|
+
protected handleClick(): void;
|
13
|
+
}
|