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
package/src/toolbar/icons.ts
CHANGED
@@ -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 EventDispatcher from '../EventDispatcher';
|
@@ -14,6 +15,20 @@ const primaryForegroundFill = `
|
|
14
15
|
const primaryForegroundStrokeFill = `
|
15
16
|
style='fill: var(--primary-foreground-color); stroke: var(--primary-foreground-color);'
|
16
17
|
`;
|
18
|
+
const checkerboardPatternDef = `
|
19
|
+
<pattern
|
20
|
+
id='checkerboard'
|
21
|
+
viewBox='0,0,10,10'
|
22
|
+
width='20%'
|
23
|
+
height='20%'
|
24
|
+
patternUnits='userSpaceOnUse'
|
25
|
+
>
|
26
|
+
<rect x=0 y=0 width=10 height=10 fill='white'/>
|
27
|
+
<rect x=0 y=0 width=5 height=5 fill='gray'/>
|
28
|
+
<rect x=5 y=5 width=5 height=5 fill='gray'/>
|
29
|
+
</pattern>
|
30
|
+
`;
|
31
|
+
const checkerboardPatternRef = 'url(#checkerboard)';
|
17
32
|
|
18
33
|
export const makeUndoIcon = () => {
|
19
34
|
return makeRedoIcon(true);
|
@@ -91,7 +106,7 @@ export const makeSelectionIcon = () => {
|
|
91
106
|
export const makeHandToolIcon = () => {
|
92
107
|
const icon = document.createElementNS(svgNamespace, 'svg');
|
93
108
|
|
94
|
-
// Draw a cursor-like shape
|
109
|
+
// Draw a cursor-like shape (like some of the other icons, made with Inkscape)
|
95
110
|
icon.innerHTML = `
|
96
111
|
<g>
|
97
112
|
<path d='
|
@@ -127,6 +142,139 @@ export const makeHandToolIcon = () => {
|
|
127
142
|
return icon;
|
128
143
|
};
|
129
144
|
|
145
|
+
export const makeTouchPanningIcon = () => {
|
146
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
147
|
+
icon.innerHTML = `
|
148
|
+
<path
|
149
|
+
d='
|
150
|
+
M 5,5.5
|
151
|
+
V 17.2
|
152
|
+
L 16.25,5.46
|
153
|
+
Z
|
154
|
+
|
155
|
+
m 33.75,0
|
156
|
+
L 50,17
|
157
|
+
V 5.5
|
158
|
+
Z
|
159
|
+
|
160
|
+
M 5,40.7
|
161
|
+
v 11.7
|
162
|
+
h 11.25
|
163
|
+
z
|
164
|
+
|
165
|
+
M 26,19
|
166
|
+
C 19.8,19.4 17.65,30.4 21.9,34.8
|
167
|
+
L 50,70
|
168
|
+
H 27.5
|
169
|
+
c -11.25,0 -11.25,17.6 0,17.6
|
170
|
+
H 61.25
|
171
|
+
C 94.9,87.8 95,87.6 95,40.7 78.125,23 67,29 55.6,46.5
|
172
|
+
L 33.1,23
|
173
|
+
C 30.3125,20.128192 27.9,19 25.830078,19.119756
|
174
|
+
Z
|
175
|
+
'
|
176
|
+
fill='none'
|
177
|
+
style='
|
178
|
+
stroke: var(--primary-foreground-color);
|
179
|
+
stroke-width: 2;
|
180
|
+
'
|
181
|
+
/>
|
182
|
+
`;
|
183
|
+
|
184
|
+
icon.setAttribute('viewBox', '0 0 100 100');
|
185
|
+
return icon;
|
186
|
+
};
|
187
|
+
|
188
|
+
export const makeAllDevicePanningIcon = () => {
|
189
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
190
|
+
icon.innerHTML = `
|
191
|
+
<path
|
192
|
+
d='
|
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
|
+
'
|
242
|
+
fill='none'
|
243
|
+
style='
|
244
|
+
stroke: var(--primary-foreground-color);
|
245
|
+
stroke-width: 2;
|
246
|
+
'
|
247
|
+
/>
|
248
|
+
`;
|
249
|
+
|
250
|
+
icon.setAttribute('viewBox', '0 0 100 100');
|
251
|
+
return icon;
|
252
|
+
};
|
253
|
+
|
254
|
+
export const makeZoomIcon = () => {
|
255
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
256
|
+
icon.setAttribute('viewBox', '0 0 100 100');
|
257
|
+
|
258
|
+
const addTextNode = (text: string, x: number, y: number) => {
|
259
|
+
const textNode = document.createElementNS(svgNamespace, 'text');
|
260
|
+
textNode.appendChild(document.createTextNode(text));
|
261
|
+
textNode.setAttribute('x', x.toString());
|
262
|
+
textNode.setAttribute('y', y.toString());
|
263
|
+
textNode.style.textAlign = 'center';
|
264
|
+
textNode.style.textAnchor = 'middle';
|
265
|
+
textNode.style.fontSize = '55px';
|
266
|
+
textNode.style.fill = 'var(--primary-foreground-color)';
|
267
|
+
textNode.style.fontFamily = 'monospace';
|
268
|
+
|
269
|
+
icon.appendChild(textNode);
|
270
|
+
};
|
271
|
+
|
272
|
+
addTextNode('+', 40, 45);
|
273
|
+
addTextNode('-', 70, 75);
|
274
|
+
|
275
|
+
return icon;
|
276
|
+
};
|
277
|
+
|
130
278
|
export const makeTextIcon = (textStyle: TextStyle) => {
|
131
279
|
const icon = document.createElementNS(svgNamespace, 'svg');
|
132
280
|
icon.setAttribute('viewBox', '0 0 100 100');
|
@@ -161,17 +309,7 @@ export const makePenIcon = (tipThickness: number, color: string) => {
|
|
161
309
|
const backgroundStrokeTipPath = `M14,63 L${50 - halfThickness},85 L${50 + halfThickness},83 L88,60 Z`;
|
162
310
|
icon.innerHTML = `
|
163
311
|
<defs>
|
164
|
-
|
165
|
-
id='checkerboard'
|
166
|
-
viewBox='0,0,10,10'
|
167
|
-
width='20%'
|
168
|
-
height='20%'
|
169
|
-
patternUnits='userSpaceOnUse'
|
170
|
-
>
|
171
|
-
<rect x=0 y=0 width=10 height=10 fill='white'/>
|
172
|
-
<rect x=0 y=0 width=5 height=5 fill='gray'/>
|
173
|
-
<rect x=5 y=5 width=5 height=5 fill='gray'/>
|
174
|
-
</pattern>
|
312
|
+
${checkerboardPatternDef}
|
175
313
|
</defs>
|
176
314
|
<g>
|
177
315
|
<!-- Pen grip -->
|
@@ -182,7 +320,7 @@ export const makePenIcon = (tipThickness: number, color: string) => {
|
|
182
320
|
</g>
|
183
321
|
<g>
|
184
322
|
<!-- Checkerboard background for slightly transparent pens -->
|
185
|
-
<path d='${backgroundStrokeTipPath}' fill='
|
323
|
+
<path d='${backgroundStrokeTipPath}' fill='${checkerboardPatternRef}'/>
|
186
324
|
|
187
325
|
<!-- Actual pen tip -->
|
188
326
|
<path
|
@@ -225,3 +363,57 @@ export const makeIconFromFactory = (pen: Pen, factory: ComponentBuilderFactory)
|
|
225
363
|
|
226
364
|
return icon;
|
227
365
|
};
|
366
|
+
|
367
|
+
export const makePipetteIcon = (color?: Color4) => {
|
368
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
369
|
+
const pipette = document.createElementNS(svgNamespace, 'path');
|
370
|
+
|
371
|
+
pipette.setAttribute('d', `
|
372
|
+
M 47,6
|
373
|
+
C 35,5 25,15 35,30
|
374
|
+
c -9.2,1.3 -15,0 -15,3
|
375
|
+
0,2 5,5 15,7
|
376
|
+
V 81
|
377
|
+
L 40,90
|
378
|
+
h 6
|
379
|
+
L 40,80
|
380
|
+
V 40
|
381
|
+
h 15
|
382
|
+
v 40
|
383
|
+
l -6,10
|
384
|
+
h 6
|
385
|
+
l 5,-9.2
|
386
|
+
V 40
|
387
|
+
C 70,38 75,35 75,33
|
388
|
+
75,30 69.2,31.2 60,30
|
389
|
+
65,15 65,5 47,6
|
390
|
+
Z
|
391
|
+
`);
|
392
|
+
pipette.style.fill = 'var(--primary-foreground-color)';
|
393
|
+
|
394
|
+
if (color) {
|
395
|
+
const defs = document.createElementNS(svgNamespace, 'defs');
|
396
|
+
defs.innerHTML = checkerboardPatternDef;
|
397
|
+
icon.appendChild(defs);
|
398
|
+
|
399
|
+
const fluidBackground = document.createElementNS(svgNamespace, 'path');
|
400
|
+
const fluid = document.createElementNS(svgNamespace, 'path');
|
401
|
+
|
402
|
+
const fluidPathData = `
|
403
|
+
m 40,50 c 5,5 10,0 15,-5 V 80 L 50,90 H 45 L 40,80 Z
|
404
|
+
`;
|
405
|
+
|
406
|
+
fluid.setAttribute('d', fluidPathData);
|
407
|
+
fluidBackground.setAttribute('d', fluidPathData);
|
408
|
+
|
409
|
+
fluid.style.fill = color.toHexString();
|
410
|
+
fluidBackground.style.fill = checkerboardPatternRef;
|
411
|
+
|
412
|
+
icon.appendChild(fluidBackground);
|
413
|
+
icon.appendChild(fluid);
|
414
|
+
}
|
415
|
+
icon.appendChild(pipette);
|
416
|
+
|
417
|
+
icon.setAttribute('viewBox', '0 0 100 100');
|
418
|
+
return icon;
|
419
|
+
};
|
@@ -18,12 +18,16 @@ export interface ToolbarLocalization {
|
|
18
18
|
thicknessLabel: string;
|
19
19
|
resizeImageToSelection: string;
|
20
20
|
deleteSelection: string;
|
21
|
+
duplicateSelection: string;
|
22
|
+
pickColorFronScreen: string;
|
21
23
|
undo: string;
|
22
24
|
redo: string;
|
25
|
+
zoom: string;
|
23
26
|
|
24
|
-
dropdownShown: (toolName: string)=>string;
|
25
|
-
dropdownHidden: (toolName: string)=>string;
|
27
|
+
dropdownShown: (toolName: string)=> string;
|
28
|
+
dropdownHidden: (toolName: string)=> string;
|
26
29
|
zoomLevel: (zoomPercentage: number)=> string;
|
30
|
+
colorChangedAnnouncement: (color: string)=> string;
|
27
31
|
}
|
28
32
|
|
29
33
|
export const defaultToolbarLocalization: ToolbarLocalization = {
|
@@ -31,14 +35,17 @@ export const defaultToolbarLocalization: ToolbarLocalization = {
|
|
31
35
|
eraser: 'Eraser',
|
32
36
|
select: 'Select',
|
33
37
|
handTool: 'Pan',
|
38
|
+
zoom: 'Zoom',
|
34
39
|
thicknessLabel: 'Thickness: ',
|
35
40
|
colorLabel: 'Color: ',
|
36
41
|
fontLabel: 'Font: ',
|
37
42
|
resizeImageToSelection: 'Resize image to selection',
|
38
43
|
deleteSelection: 'Delete selection',
|
44
|
+
duplicateSelection: 'Duplicate selection',
|
39
45
|
undo: 'Undo',
|
40
46
|
redo: 'Redo',
|
41
47
|
selectObjectType: 'Object type: ',
|
48
|
+
pickColorFronScreen: 'Pick color from screen',
|
42
49
|
|
43
50
|
touchPanning: 'Touchscreen panning',
|
44
51
|
anyDevicePanning: 'Any device panning',
|
@@ -52,4 +59,5 @@ export const defaultToolbarLocalization: ToolbarLocalization = {
|
|
52
59
|
dropdownShown: (toolName) => `Dropdown for ${toolName} shown`,
|
53
60
|
dropdownHidden: (toolName) => `Dropdown for ${toolName} hidden`,
|
54
61
|
zoomLevel: (zoomPercent: number) => `Zoom: ${zoomPercent}%`,
|
62
|
+
colorChangedAnnouncement: (color: string)=> `Color changed to ${color}`,
|
55
63
|
};
|
@@ -0,0 +1,120 @@
|
|
1
|
+
import Color4 from '../Color4';
|
2
|
+
import Editor from '../Editor';
|
3
|
+
import PipetteTool from '../tools/PipetteTool';
|
4
|
+
import { ToolType } from '../tools/ToolController';
|
5
|
+
import { EditorEventType } from '../types';
|
6
|
+
import { makePipetteIcon } from './icons';
|
7
|
+
|
8
|
+
type OnColorChangeListener = (color: Color4)=>void;
|
9
|
+
|
10
|
+
|
11
|
+
// Returns [ input, container ].
|
12
|
+
export const makeColorInput = (editor: Editor, onColorChange: OnColorChangeListener): [ HTMLInputElement, HTMLElement ] => {
|
13
|
+
const colorInputContainer = document.createElement('span');
|
14
|
+
const colorInput = document.createElement('input');
|
15
|
+
|
16
|
+
colorInput.type = 'button';
|
17
|
+
colorInput.classList.add('coloris_input');
|
18
|
+
colorInputContainer.classList.add('color-input-container');
|
19
|
+
|
20
|
+
colorInputContainer.appendChild(colorInput);
|
21
|
+
addPipetteTool(editor, colorInputContainer, (color: Color4) => {
|
22
|
+
colorInput.value = color.toHexString();
|
23
|
+
onInputEnd();
|
24
|
+
|
25
|
+
// Update the color preview, if it exists (may be managed by Coloris).
|
26
|
+
const parentElem = colorInput.parentElement;
|
27
|
+
if (parentElem && parentElem.classList.contains('clr-field')) {
|
28
|
+
parentElem.style.color = colorInput.value;
|
29
|
+
}
|
30
|
+
});
|
31
|
+
|
32
|
+
let currentColor: Color4|undefined;
|
33
|
+
const handleColorInput = () => {
|
34
|
+
currentColor = Color4.fromHex(colorInput.value);
|
35
|
+
};
|
36
|
+
const onInputEnd = () => {
|
37
|
+
handleColorInput();
|
38
|
+
|
39
|
+
if (currentColor) {
|
40
|
+
editor.announceForAccessibility(
|
41
|
+
editor.localization.colorChangedAnnouncement(currentColor.toHexString())
|
42
|
+
);
|
43
|
+
onColorChange(currentColor);
|
44
|
+
editor.notifier.dispatch(EditorEventType.ColorPickerColorSelected, {
|
45
|
+
kind: EditorEventType.ColorPickerColorSelected,
|
46
|
+
color: currentColor,
|
47
|
+
});
|
48
|
+
}
|
49
|
+
};
|
50
|
+
|
51
|
+
colorInput.oninput = handleColorInput;
|
52
|
+
colorInput.addEventListener('open', () => {
|
53
|
+
editor.notifier.dispatch(EditorEventType.ColorPickerToggled, {
|
54
|
+
kind: EditorEventType.ColorPickerToggled,
|
55
|
+
open: true,
|
56
|
+
});
|
57
|
+
});
|
58
|
+
colorInput.addEventListener('close', () => {
|
59
|
+
editor.notifier.dispatch(EditorEventType.ColorPickerToggled, {
|
60
|
+
kind: EditorEventType.ColorPickerToggled,
|
61
|
+
open: false,
|
62
|
+
});
|
63
|
+
onInputEnd();
|
64
|
+
});
|
65
|
+
|
66
|
+
return [ colorInput, colorInputContainer ];
|
67
|
+
};
|
68
|
+
|
69
|
+
const addPipetteTool = (editor: Editor, container: HTMLElement, onColorChange: OnColorChangeListener) => {
|
70
|
+
const pipetteButton = document.createElement('button');
|
71
|
+
pipetteButton.classList.add('pipetteButton');
|
72
|
+
pipetteButton.title = editor.localization.pickColorFronScreen;
|
73
|
+
pipetteButton.setAttribute('alt', pipetteButton.title);
|
74
|
+
|
75
|
+
const updatePipetteIcon = (color?: Color4) => {
|
76
|
+
pipetteButton.replaceChildren(makePipetteIcon(color));
|
77
|
+
};
|
78
|
+
updatePipetteIcon();
|
79
|
+
|
80
|
+
const pipetteTool: PipetteTool|undefined = editor.toolController.getMatchingTools(ToolType.Pipette)[0] as PipetteTool|undefined;
|
81
|
+
const endColorSelectMode = () => {
|
82
|
+
pipetteTool?.clearColorListener();
|
83
|
+
updatePipetteIcon();
|
84
|
+
pipetteButton.classList.remove('active');
|
85
|
+
};
|
86
|
+
const pipetteColorSelect = (color: Color4|null) => {
|
87
|
+
endColorSelectMode();
|
88
|
+
|
89
|
+
if (color) {
|
90
|
+
onColorChange(color);
|
91
|
+
}
|
92
|
+
};
|
93
|
+
const pipetteColorPreview = (color: Color4|null) => {
|
94
|
+
if (color) {
|
95
|
+
updatePipetteIcon(color);
|
96
|
+
} else {
|
97
|
+
updatePipetteIcon();
|
98
|
+
}
|
99
|
+
};
|
100
|
+
|
101
|
+
pipetteButton.onclick = () => {
|
102
|
+
// If already picking, cancel it.
|
103
|
+
if (pipetteButton.classList.contains('active')) {
|
104
|
+
endColorSelectMode();
|
105
|
+
return;
|
106
|
+
}
|
107
|
+
|
108
|
+
pipetteTool?.setColorListener(
|
109
|
+
pipetteColorPreview,
|
110
|
+
pipetteColorSelect,
|
111
|
+
);
|
112
|
+
if (pipetteTool) {
|
113
|
+
pipetteButton.classList.add('active');
|
114
|
+
}
|
115
|
+
};
|
116
|
+
|
117
|
+
container.appendChild(pipetteButton);
|
118
|
+
};
|
119
|
+
|
120
|
+
export default makeColorInput;
|
package/src/toolbar/toolbar.css
CHANGED
@@ -1,138 +1,176 @@
|
|
1
1
|
.toolbar-root {
|
2
|
-
|
2
|
+
background-color: var(--primary-background-color);
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
border: 1px solid var(--secondary-background-color);
|
5
|
+
border-radius: 2px;
|
6
|
+
flex-wrap: wrap;
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
box-sizing: border-box;
|
9
|
+
width: 100%;
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
display: flex;
|
12
|
+
flex-direction: row;
|
13
|
+
justify-content: center;
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
/* Display above selection dialogs, etc. */
|
16
|
+
z-index: 2;
|
17
17
|
|
18
|
-
|
18
|
+
font-family: system-ui, -apple-system, sans-serif;
|
19
19
|
}
|
20
20
|
|
21
|
-
.toolbar-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
.toolbar-root > .toolbar-toolContainer > .toolbar-button,
|
22
|
+
.toolbar-root > .toolbar-toolContainer > * > button,
|
23
|
+
.toolbar-root > .toolbar-buttonGroup > button {
|
24
|
+
width: min-content;
|
25
|
+
height: min(20vh, 60px);
|
26
|
+
}
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
28
|
+
.toolbar-dropdown .toolbar-button > .toolbar-icon {
|
29
|
+
max-width: 50px;
|
30
|
+
}
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
margin-right: 3px;
|
32
|
+
.toolbar-button.disabled {
|
33
|
+
filter: opacity(0.8) saturate(0.1);
|
34
|
+
}
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
.toolbar-button, .toolbar-root button {
|
37
|
+
cursor: pointer;
|
38
|
+
text-align: center;
|
39
|
+
border-radius: 6px;
|
40
40
|
|
41
|
-
|
41
|
+
background-color: var(--primary-background-color);
|
42
|
+
color: var(--primary-foreground-color);
|
43
|
+
border: none;
|
44
|
+
box-shadow: 0px 0px 2px var(--primary-shadow-color);
|
42
45
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
46
|
+
transition: background-color 0.25s ease, box-shadow 0.25s ease, opacity 0.3s ease;
|
47
|
+
}
|
48
|
+
|
49
|
+
.toolbar-button,
|
50
|
+
.toolbar-buttonGroup > button,
|
51
|
+
.toolbar-toolContainer > * > button,
|
52
|
+
.toolbar-root > button {
|
53
|
+
display: flex;
|
54
|
+
flex-direction: column;
|
55
|
+
align-items: center;
|
56
|
+
justify-content: center;
|
48
57
|
|
49
|
-
|
58
|
+
padding-left: 3px;
|
59
|
+
padding-right: 3px;
|
60
|
+
margin-left: 3px;
|
61
|
+
margin-right: 3px;
|
62
|
+
|
63
|
+
min-width: 40px;
|
64
|
+
max-width: 70px;
|
65
|
+
font-size: 11pt;
|
50
66
|
}
|
51
67
|
|
52
68
|
.toolbar-button:hover, .toolbar-root button:not(:disabled):hover {
|
53
|
-
|
69
|
+
box-shadow: 0px 2px 4px var(--primary-shadow-color);
|
54
70
|
}
|
55
71
|
|
56
72
|
.toolbar-root button:disabled {
|
57
|
-
|
58
|
-
|
73
|
+
cursor: inherit;
|
74
|
+
filter: opacity(0.5);
|
59
75
|
}
|
60
76
|
|
61
77
|
.toolbar-root .toolbar-icon {
|
62
|
-
|
63
|
-
|
78
|
+
flex-shrink: 1;
|
79
|
+
min-width: 30px;
|
80
|
+
min-height: 30px;
|
64
81
|
}
|
65
82
|
|
66
83
|
.toolbar-toolContainer.selected .toolbar-button {
|
67
|
-
|
68
|
-
|
84
|
+
background-color: var(--secondary-background-color);
|
85
|
+
color: var(--secondary-foreground-color);
|
69
86
|
}
|
70
87
|
|
71
|
-
.toolbar-toolContainer:not(.selected):not(.dropdownShowable) .toolbar-showHideDropdownIcon {
|
72
|
-
|
88
|
+
.toolbar-toolContainer:not(.selected):not(.dropdownShowable) > .toolbar-button > .toolbar-showHideDropdownIcon {
|
89
|
+
display: none;
|
73
90
|
}
|
74
91
|
|
75
|
-
.toolbar-toolContainer .toolbar-showHideDropdownIcon {
|
76
|
-
|
77
|
-
|
92
|
+
.toolbar-toolContainer > .toolbar-button > .toolbar-showHideDropdownIcon {
|
93
|
+
height: 10px;
|
94
|
+
transition: transform 0.5s ease;
|
78
95
|
}
|
79
96
|
|
80
|
-
.toolbar-toolContainer.dropdownVisible .toolbar-showHideDropdownIcon {
|
81
|
-
|
97
|
+
.toolbar-toolContainer.dropdownVisible > .toolbar-button > .toolbar-showHideDropdownIcon {
|
98
|
+
transform: rotate(180deg);
|
82
99
|
}
|
83
100
|
|
84
101
|
.toolbar-dropdown.hidden,
|
85
102
|
.toolbar-toolContainer:not(.selected):not(.dropdownShowable) > .toolbar-dropdown {
|
86
|
-
|
103
|
+
display: none;
|
87
104
|
}
|
88
105
|
|
89
106
|
.toolbar-dropdown {
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
107
|
+
position: absolute;
|
108
|
+
padding: 15px;
|
109
|
+
padding-top: 5px;
|
110
|
+
|
111
|
+
/* Prevent overlap/being displayed under the undo/redo buttons */
|
112
|
+
z-index: 2;
|
113
|
+
background-color: var(--primary-background-color);
|
114
|
+
box-shadow: 0px 3px 3px var(--primary-shadow-color);
|
97
115
|
}
|
98
116
|
|
99
117
|
.toolbar-buttonGroup {
|
100
|
-
|
101
|
-
|
102
|
-
|
118
|
+
display: flex;
|
119
|
+
flex-direction: row;
|
120
|
+
justify-content: center;
|
103
121
|
}
|
104
122
|
|
105
123
|
.toolbar-closeColorPickerOverlay {
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
124
|
+
display: none;
|
125
|
+
position: fixed;
|
126
|
+
top: 0;
|
127
|
+
left: 0;
|
128
|
+
bottom: 0;
|
129
|
+
right: 0;
|
112
130
|
|
113
|
-
|
114
|
-
|
131
|
+
background-color: var(--primary-background-color);
|
132
|
+
opacity: 0.3;
|
115
133
|
}
|
116
134
|
|
117
135
|
/* Make color selection buttons fill their containing label */
|
118
136
|
.toolbar-dropdown .clr-field button {
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
137
|
+
width: 100%;
|
138
|
+
height: 100%;
|
139
|
+
border-radius: 2px;
|
140
|
+
margin-left: 0;
|
141
|
+
margin-right: 0;
|
124
142
|
}
|
125
143
|
|
126
144
|
.toolbar-root .toolbar-zoomLevelEditor {
|
127
|
-
|
128
|
-
|
145
|
+
display: flex;
|
146
|
+
flex-direction: row;
|
129
147
|
}
|
130
148
|
|
131
149
|
.toolbar-root .toolbar-zoomLevelEditor .zoomDisplay {
|
132
|
-
|
150
|
+
flex-grow: 1;
|
133
151
|
}
|
134
152
|
|
135
153
|
.toolbar-root .toolbar-zoomLevelEditor button {
|
136
|
-
|
137
|
-
|
154
|
+
width: min-content;
|
155
|
+
height: min-content;
|
156
|
+
}
|
157
|
+
|
158
|
+
.color-input-container {
|
159
|
+
display: inline-flex;
|
160
|
+
flex-direction: row;
|
161
|
+
}
|
162
|
+
|
163
|
+
.color-input-container .pipetteButton {
|
164
|
+
width: 30px;
|
165
|
+
height: 30px;
|
166
|
+
padding: 0;
|
167
|
+
display: inline-flex;
|
168
|
+
}
|
169
|
+
|
170
|
+
.color-input-container .pipetteButton > svg {
|
171
|
+
width: 100%;
|
172
|
+
}
|
173
|
+
|
174
|
+
.color-input-container .pipetteButton.active {
|
175
|
+
background-color: var(--secondary-background-color);
|
138
176
|
}
|