js-draw 0.1.4 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/.eslintrc.js +1 -0
  2. package/CHANGELOG.md +15 -0
  3. package/README.md +2 -2
  4. package/dist/bundle.js +1 -1
  5. package/dist/src/Color4.js +6 -2
  6. package/dist/src/Editor.d.ts +1 -0
  7. package/dist/src/Editor.js +20 -9
  8. package/dist/src/EditorImage.d.ts +8 -13
  9. package/dist/src/EditorImage.js +51 -29
  10. package/dist/src/SVGLoader.js +6 -2
  11. package/dist/src/Viewport.d.ts +10 -2
  12. package/dist/src/Viewport.js +8 -6
  13. package/dist/src/commands/Command.d.ts +9 -8
  14. package/dist/src/commands/Command.js +15 -14
  15. package/dist/src/commands/Duplicate.d.ts +14 -0
  16. package/dist/src/commands/Duplicate.js +34 -0
  17. package/dist/src/commands/Erase.d.ts +5 -2
  18. package/dist/src/commands/Erase.js +28 -9
  19. package/dist/src/commands/SerializableCommand.d.ts +13 -0
  20. package/dist/src/commands/SerializableCommand.js +28 -0
  21. package/dist/src/commands/localization.d.ts +2 -0
  22. package/dist/src/commands/localization.js +2 -0
  23. package/dist/src/components/AbstractComponent.d.ts +15 -2
  24. package/dist/src/components/AbstractComponent.js +122 -26
  25. package/dist/src/components/SVGGlobalAttributesObject.d.ts +6 -1
  26. package/dist/src/components/SVGGlobalAttributesObject.js +23 -1
  27. package/dist/src/components/Stroke.d.ts +5 -0
  28. package/dist/src/components/Stroke.js +32 -1
  29. package/dist/src/components/Text.d.ts +11 -4
  30. package/dist/src/components/Text.js +57 -3
  31. package/dist/src/components/UnknownSVGObject.d.ts +2 -0
  32. package/dist/src/components/UnknownSVGObject.js +12 -1
  33. package/dist/src/components/util/describeComponentList.d.ts +4 -0
  34. package/dist/src/components/util/describeComponentList.js +14 -0
  35. package/dist/src/geometry/Path.d.ts +4 -1
  36. package/dist/src/geometry/Path.js +4 -0
  37. package/dist/src/localization.d.ts +2 -1
  38. package/dist/src/localization.js +2 -1
  39. package/dist/src/rendering/Display.d.ts +5 -0
  40. package/dist/src/rendering/Display.js +32 -0
  41. package/dist/src/rendering/RenderingStyle.d.ts +24 -0
  42. package/dist/src/rendering/RenderingStyle.js +32 -0
  43. package/dist/src/rendering/localization.d.ts +5 -0
  44. package/dist/src/rendering/localization.js +4 -0
  45. package/dist/src/rendering/renderers/AbstractRenderer.d.ts +1 -8
  46. package/dist/src/rendering/renderers/AbstractRenderer.js +1 -6
  47. package/dist/src/rendering/renderers/CanvasRenderer.d.ts +2 -1
  48. package/dist/src/rendering/renderers/DummyRenderer.d.ts +2 -1
  49. package/dist/src/rendering/renderers/SVGRenderer.d.ts +2 -1
  50. package/dist/src/rendering/renderers/TextOnlyRenderer.d.ts +25 -0
  51. package/dist/src/rendering/renderers/TextOnlyRenderer.js +40 -0
  52. package/dist/src/toolbar/HTMLToolbar.d.ts +1 -1
  53. package/dist/src/toolbar/HTMLToolbar.js +52 -534
  54. package/dist/src/toolbar/icons.d.ts +5 -0
  55. package/dist/src/toolbar/icons.js +186 -13
  56. package/dist/src/toolbar/localization.d.ts +4 -0
  57. package/dist/src/toolbar/localization.js +4 -0
  58. package/dist/src/toolbar/makeColorInput.d.ts +5 -0
  59. package/dist/src/toolbar/makeColorInput.js +81 -0
  60. package/dist/src/toolbar/widgets/BaseToolWidget.d.ts +12 -0
  61. package/dist/src/toolbar/widgets/BaseToolWidget.js +44 -0
  62. package/dist/src/toolbar/widgets/BaseWidget.d.ts +32 -0
  63. package/dist/src/toolbar/widgets/BaseWidget.js +148 -0
  64. package/dist/src/toolbar/widgets/EraserWidget.d.ts +6 -0
  65. package/dist/src/toolbar/widgets/EraserWidget.js +14 -0
  66. package/dist/src/toolbar/widgets/HandToolWidget.d.ts +13 -0
  67. package/dist/src/toolbar/widgets/HandToolWidget.js +133 -0
  68. package/dist/src/toolbar/widgets/PenWidget.d.ts +20 -0
  69. package/dist/src/toolbar/widgets/PenWidget.js +131 -0
  70. package/dist/src/toolbar/widgets/SelectionWidget.d.ts +11 -0
  71. package/dist/src/toolbar/widgets/SelectionWidget.js +56 -0
  72. package/dist/src/toolbar/widgets/TextToolWidget.d.ts +13 -0
  73. package/dist/src/toolbar/widgets/TextToolWidget.js +72 -0
  74. package/dist/src/tools/Pen.js +1 -1
  75. package/dist/src/tools/PipetteTool.d.ts +20 -0
  76. package/dist/src/tools/PipetteTool.js +40 -0
  77. package/dist/src/tools/SelectionTool.d.ts +2 -0
  78. package/dist/src/tools/SelectionTool.js +41 -23
  79. package/dist/src/tools/TextTool.d.ts +1 -0
  80. package/dist/src/tools/TextTool.js +5 -4
  81. package/dist/src/tools/ToolController.d.ts +3 -1
  82. package/dist/src/tools/ToolController.js +4 -0
  83. package/dist/src/tools/localization.d.ts +2 -1
  84. package/dist/src/tools/localization.js +3 -2
  85. package/dist/src/types.d.ts +7 -2
  86. package/dist/src/types.js +1 -0
  87. package/jest.config.js +2 -0
  88. package/package.json +6 -6
  89. package/src/Color4.ts +9 -3
  90. package/src/Editor.css +10 -0
  91. package/src/Editor.ts +24 -12
  92. package/src/EditorImage.test.ts +4 -4
  93. package/src/EditorImage.ts +61 -20
  94. package/src/SVGLoader.ts +9 -3
  95. package/src/Viewport.ts +7 -6
  96. package/src/commands/Command.ts +21 -19
  97. package/src/commands/Duplicate.ts +49 -0
  98. package/src/commands/Erase.ts +34 -13
  99. package/src/commands/SerializableCommand.ts +41 -0
  100. package/src/commands/localization.ts +5 -0
  101. package/src/components/AbstractComponent.ts +168 -26
  102. package/src/components/SVGGlobalAttributesObject.ts +34 -2
  103. package/src/components/Stroke.test.ts +53 -0
  104. package/src/components/Stroke.ts +37 -2
  105. package/src/components/Text.test.ts +38 -0
  106. package/src/components/Text.ts +80 -5
  107. package/src/components/UnknownSVGObject.test.ts +10 -0
  108. package/src/components/UnknownSVGObject.ts +15 -1
  109. package/src/components/builders/FreehandLineBuilder.ts +2 -1
  110. package/src/components/util/describeComponentList.ts +18 -0
  111. package/src/geometry/Path.ts +8 -1
  112. package/src/localization.ts +3 -1
  113. package/src/rendering/Display.ts +43 -1
  114. package/src/rendering/RenderingStyle.test.ts +68 -0
  115. package/src/rendering/RenderingStyle.ts +46 -0
  116. package/src/rendering/caching/RenderingCache.test.ts +1 -1
  117. package/src/rendering/localization.ts +10 -0
  118. package/src/rendering/renderers/AbstractRenderer.ts +1 -15
  119. package/src/rendering/renderers/CanvasRenderer.ts +2 -1
  120. package/src/rendering/renderers/DummyRenderer.ts +2 -1
  121. package/src/rendering/renderers/SVGRenderer.ts +2 -1
  122. package/src/rendering/renderers/TextOnlyRenderer.ts +52 -0
  123. package/src/toolbar/HTMLToolbar.ts +58 -660
  124. package/src/toolbar/icons.ts +205 -13
  125. package/src/toolbar/localization.ts +10 -2
  126. package/src/toolbar/makeColorInput.ts +105 -0
  127. package/src/toolbar/toolbar.css +116 -78
  128. package/src/toolbar/widgets/BaseToolWidget.ts +53 -0
  129. package/src/toolbar/widgets/BaseWidget.ts +175 -0
  130. package/src/toolbar/widgets/EraserWidget.ts +16 -0
  131. package/src/toolbar/widgets/HandToolWidget.ts +186 -0
  132. package/src/toolbar/widgets/PenWidget.ts +165 -0
  133. package/src/toolbar/widgets/SelectionWidget.ts +72 -0
  134. package/src/toolbar/widgets/TextToolWidget.ts +90 -0
  135. package/src/tools/Pen.ts +1 -1
  136. package/src/tools/PipetteTool.ts +56 -0
  137. package/src/tools/SelectionTool.test.ts +2 -4
  138. package/src/tools/SelectionTool.ts +47 -27
  139. package/src/tools/TextTool.ts +7 -3
  140. package/src/tools/ToolController.ts +10 -6
  141. package/src/tools/UndoRedoShortcut.test.ts +1 -1
  142. package/src/tools/localization.ts +6 -3
  143. package/src/types.ts +12 -1
@@ -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
- <pattern
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='url(#checkerboard)'/>
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,105 @@
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
+ handleColorInput();
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
+ editor.announceForAccessibility(
36
+ editor.localization.colorChangedAnnouncement(currentColor.toHexString())
37
+ );
38
+ onColorChange(currentColor);
39
+
40
+ editor.notifier.dispatch(EditorEventType.ColorPickerColorSelected, {
41
+ kind: EditorEventType.ColorPickerColorSelected,
42
+ color: currentColor,
43
+ });
44
+ };
45
+
46
+ colorInput.oninput = handleColorInput;
47
+ colorInput.addEventListener('open', () => {
48
+ editor.notifier.dispatch(EditorEventType.ColorPickerToggled, {
49
+ kind: EditorEventType.ColorPickerToggled,
50
+ open: true,
51
+ });
52
+ });
53
+ colorInput.addEventListener('close', () => {
54
+ editor.notifier.dispatch(EditorEventType.ColorPickerToggled, {
55
+ kind: EditorEventType.ColorPickerToggled,
56
+ open: false,
57
+ });
58
+ });
59
+
60
+ return [ colorInput, colorInputContainer ];
61
+ };
62
+
63
+ const addPipetteTool = (editor: Editor, container: HTMLElement, onColorChange: OnColorChangeListener) => {
64
+ const pipetteButton = document.createElement('button');
65
+ pipetteButton.classList.add('pipetteButton');
66
+ pipetteButton.title = editor.localization.pickColorFronScreen;
67
+ pipetteButton.setAttribute('alt', pipetteButton.title);
68
+
69
+ const updatePipetteIcon = (color?: Color4) => {
70
+ pipetteButton.replaceChildren(makePipetteIcon(color));
71
+ };
72
+ updatePipetteIcon();
73
+
74
+ const pipetteTool: PipetteTool|undefined = editor.toolController.getMatchingTools(ToolType.Pipette)[0] as PipetteTool|undefined;
75
+ const pipetteColorSelect = (color: Color4|null) => {
76
+ pipetteTool?.clearColorListener();
77
+ updatePipetteIcon();
78
+ pipetteButton.classList.remove('active');
79
+
80
+ if (color) {
81
+ onColorChange(color);
82
+ }
83
+ };
84
+ const pipetteColorPreview = (color: Color4|null) => {
85
+ if (color) {
86
+ updatePipetteIcon(color);
87
+ } else {
88
+ updatePipetteIcon();
89
+ }
90
+ };
91
+
92
+ pipetteButton.onclick = () => {
93
+ pipetteTool?.setColorListener(
94
+ pipetteColorPreview,
95
+ pipetteColorSelect,
96
+ );
97
+ if (pipetteTool) {
98
+ pipetteButton.classList.add('active');
99
+ }
100
+ };
101
+
102
+ container.appendChild(pipetteButton);
103
+ };
104
+
105
+ export default makeColorInput;
@@ -1,138 +1,176 @@
1
1
  .toolbar-root {
2
- background-color: var(--primary-background-color);
2
+ background-color: var(--primary-background-color);
3
3
 
4
- border: 1px solid var(--secondary-background-color);
5
- border-radius: 2px;
6
- flex-wrap: wrap;
4
+ border: 1px solid var(--secondary-background-color);
5
+ border-radius: 2px;
6
+ flex-wrap: wrap;
7
7
 
8
- box-sizing: border-box;
9
- width: 100%;
8
+ box-sizing: border-box;
9
+ width: 100%;
10
10
 
11
- display: flex;
12
- flex-direction: row;
13
- justify-content: center;
11
+ display: flex;
12
+ flex-direction: row;
13
+ justify-content: center;
14
14
 
15
- /* Display above selection dialogs, etc. */
16
- z-index: 1000;
15
+ /* Display above selection dialogs, etc. */
16
+ z-index: 2;
17
17
 
18
- font-family: system-ui, -apple-system, sans-serif;
18
+ font-family: system-ui, -apple-system, sans-serif;
19
19
  }
20
20
 
21
- .toolbar-button, .toolbar-root button {
22
- display: flex;
23
- flex-direction: column;
24
- align-items: center;
25
- justify-content: center;
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
- text-align: center;
28
- border-radius: 6px;
29
- cursor: pointer;
28
+ .toolbar-dropdown .toolbar-button > .toolbar-icon {
29
+ max-width: 50px;
30
+ }
30
31
 
31
- padding-left: 3px;
32
- padding-right: 3px;
33
- margin-left: 3px;
34
- margin-right: 3px;
32
+ .toolbar-button.disabled {
33
+ filter: opacity(0.8) saturate(0.1);
34
+ }
35
35
 
36
- min-width: 40px;
37
- max-width: 70px;
38
- width: min-content;
39
- font-size: 11pt;
36
+ .toolbar-button, .toolbar-root button {
37
+ cursor: pointer;
38
+ text-align: center;
39
+ border-radius: 6px;
40
40
 
41
- cursor: pointer;
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
- height: min(20vh, 60px);
44
- background-color: var(--primary-background-color);
45
- color: var(--primary-foreground-color);
46
- border: none;
47
- box-shadow: 0px 0px 2px var(--primary-shadow-color);
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
- transition: background-color 0.25s ease, box-shadow 0.25s ease, opacity 0.3s ease;
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
- box-shadow: 0px 2px 4px var(--primary-shadow-color);
69
+ box-shadow: 0px 2px 4px var(--primary-shadow-color);
54
70
  }
55
71
 
56
72
  .toolbar-root button:disabled {
57
- cursor: inherit;
58
- filter: opacity(0.5);
73
+ cursor: inherit;
74
+ filter: opacity(0.5);
59
75
  }
60
76
 
61
77
  .toolbar-root .toolbar-icon {
62
- flex-shrink: 1;
63
- min-width: 30px;
78
+ flex-shrink: 1;
79
+ min-width: 30px;
80
+ min-height: 30px;
64
81
  }
65
82
 
66
83
  .toolbar-toolContainer.selected .toolbar-button {
67
- background-color: var(--secondary-background-color);
68
- color: var(--secondary-foreground-color);
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
- display: none;
88
+ .toolbar-toolContainer:not(.selected):not(.dropdownShowable) > .toolbar-button > .toolbar-showHideDropdownIcon {
89
+ display: none;
73
90
  }
74
91
 
75
- .toolbar-toolContainer .toolbar-showHideDropdownIcon {
76
- height: 10px;
77
- transition: transform 0.5s ease;
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
- transform: rotate(180deg);
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
- display: none;
103
+ display: none;
87
104
  }
88
105
 
89
106
  .toolbar-dropdown {
90
- position: absolute;
91
- padding: 15px;
92
- padding-top: 5px;
93
- /* Prevent overlap/being displayed under the undo/redo buttons */
94
- z-index: 2;
95
- background-color: var(--primary-background-color);
96
- box-shadow: 0px 3px 3px var(--primary-shadow-color);
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
- display: flex;
101
- flex-direction: row;
102
- justify-content: center;
118
+ display: flex;
119
+ flex-direction: row;
120
+ justify-content: center;
103
121
  }
104
122
 
105
123
  .toolbar-closeColorPickerOverlay {
106
- display: none;
107
- position: fixed;
108
- top: 0;
109
- left: 0;
110
- bottom: 0;
111
- right: 0;
124
+ display: none;
125
+ position: fixed;
126
+ top: 0;
127
+ left: 0;
128
+ bottom: 0;
129
+ right: 0;
112
130
 
113
- background-color: var(--primary-background-color);
114
- opacity: 0.3;
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
- width: 100%;
120
- height: 100%;
121
- border-radius: 2px;
122
- margin-left: 0;
123
- margin-right: 0;
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
- display: flex;
128
- flex-direction: row;
145
+ display: flex;
146
+ flex-direction: row;
129
147
  }
130
148
 
131
149
  .toolbar-root .toolbar-zoomLevelEditor .zoomDisplay {
132
- flex-grow: 1;
150
+ flex-grow: 1;
133
151
  }
134
152
 
135
153
  .toolbar-root .toolbar-zoomLevelEditor button {
136
- width: min-content;
137
- height: min-content;
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
  }