js-draw 0.22.1 → 0.23.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 +8 -0
- package/dist/bundle.js +2 -2
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/src/Pointer.d.ts +3 -1
- package/dist/cjs/src/Pointer.js +27 -2
- package/dist/cjs/src/commands/Duplicate.d.ts +24 -0
- package/dist/cjs/src/commands/Duplicate.js +26 -0
- package/dist/cjs/src/commands/Erase.d.ts +20 -0
- package/dist/cjs/src/commands/Erase.js +20 -0
- package/dist/cjs/src/commands/invertCommand.js +6 -0
- package/dist/cjs/src/commands/uniteCommands.js +9 -13
- package/dist/cjs/src/components/BackgroundComponent.js +9 -2
- package/dist/cjs/src/components/lib.d.ts +1 -1
- package/dist/cjs/src/components/lib.js +2 -2
- package/dist/cjs/src/components/localization.d.ts +1 -0
- package/dist/cjs/src/components/localization.js +1 -0
- package/dist/cjs/src/math/Vec2.d.ts +20 -0
- package/dist/cjs/src/math/Vec2.js +20 -0
- package/dist/cjs/src/rendering/renderers/AbstractRenderer.d.ts +17 -0
- package/dist/cjs/src/rendering/renderers/AbstractRenderer.js +21 -3
- package/dist/cjs/src/rendering/renderers/CanvasRenderer.js +12 -7
- package/dist/cjs/src/toolbar/widgets/BaseWidget.d.ts +1 -0
- package/dist/cjs/src/toolbar/widgets/BaseWidget.js +22 -4
- package/dist/cjs/src/tools/Pen.d.ts +4 -0
- package/dist/cjs/src/tools/Pen.js +24 -1
- package/dist/cjs/src/tools/SelectionTool/SelectionTool.d.ts +1 -0
- package/dist/cjs/src/tools/SelectionTool/SelectionTool.js +8 -0
- package/dist/cjs/src/util/waitForAll.d.ts +6 -0
- package/dist/cjs/src/util/waitForAll.js +17 -0
- package/dist/mjs/src/Pointer.d.ts +3 -1
- package/dist/mjs/src/Pointer.mjs +27 -2
- package/dist/mjs/src/commands/Duplicate.d.ts +24 -0
- package/dist/mjs/src/commands/Duplicate.mjs +26 -0
- package/dist/mjs/src/commands/Erase.d.ts +20 -0
- package/dist/mjs/src/commands/Erase.mjs +20 -0
- package/dist/mjs/src/commands/invertCommand.mjs +6 -0
- package/dist/mjs/src/commands/uniteCommands.mjs +9 -13
- package/dist/mjs/src/components/BackgroundComponent.mjs +9 -2
- package/dist/mjs/src/components/lib.d.ts +1 -1
- package/dist/mjs/src/components/lib.mjs +3 -1
- package/dist/mjs/src/components/localization.d.ts +1 -0
- package/dist/mjs/src/components/localization.mjs +1 -0
- package/dist/mjs/src/math/Vec2.d.ts +20 -0
- package/dist/mjs/src/math/Vec2.mjs +20 -0
- package/dist/mjs/src/rendering/renderers/AbstractRenderer.d.ts +17 -0
- package/dist/mjs/src/rendering/renderers/AbstractRenderer.mjs +21 -3
- package/dist/mjs/src/rendering/renderers/CanvasRenderer.mjs +12 -7
- package/dist/mjs/src/toolbar/widgets/BaseWidget.d.ts +1 -0
- package/dist/mjs/src/toolbar/widgets/BaseWidget.mjs +22 -4
- package/dist/mjs/src/tools/Pen.d.ts +4 -0
- package/dist/mjs/src/tools/Pen.mjs +24 -1
- package/dist/mjs/src/tools/SelectionTool/SelectionTool.d.ts +1 -0
- package/dist/mjs/src/tools/SelectionTool/SelectionTool.mjs +8 -0
- package/dist/mjs/src/util/waitForAll.d.ts +6 -0
- package/dist/mjs/src/util/waitForAll.mjs +15 -0
- package/package.json +9 -9
- package/src/toolbar/toolbar.css +35 -1
@@ -19,6 +19,11 @@ export interface RenderableImage {
|
|
19
19
|
base64Url: string;
|
20
20
|
label?: string;
|
21
21
|
}
|
22
|
+
/**
|
23
|
+
* Abstract base class for renderers.
|
24
|
+
*
|
25
|
+
* @see {@link EditorImage.render}
|
26
|
+
*/
|
22
27
|
export default abstract class AbstractRenderer {
|
23
28
|
private viewport;
|
24
29
|
private selfTransform;
|
@@ -40,9 +45,21 @@ export default abstract class AbstractRenderer {
|
|
40
45
|
protected objectLevel: number;
|
41
46
|
private currentPaths;
|
42
47
|
private flushPath;
|
48
|
+
/**
|
49
|
+
* Draws a styled path. If within an object started by {@link startObject},
|
50
|
+
* the resultant path may not be visible until {@link endObject} is called.
|
51
|
+
*/
|
43
52
|
drawPath(path: RenderablePathSpec): void;
|
44
53
|
drawRect(rect: Rect2, lineWidth: number, lineFill: RenderingStyle): void;
|
54
|
+
/** Draws a filled rectangle. */
|
45
55
|
fillRect(rect: Rect2, fill: Color4): void;
|
56
|
+
/**
|
57
|
+
* This should be called whenever a new object is being drawn.
|
58
|
+
*
|
59
|
+
* @param _boundingBox The bounding box of the object to be drawn.
|
60
|
+
* @param _clip Whether content outside `_boundingBox` should be drawn. Renderers
|
61
|
+
* that override this method are not required to support `_clip`.
|
62
|
+
*/
|
46
63
|
startObject(_boundingBox: Rect2, _clip?: boolean): void;
|
47
64
|
/**
|
48
65
|
* Notes the end of an object.
|
@@ -1,6 +1,11 @@
|
|
1
1
|
import Path, { PathCommandType } from '../../math/Path.mjs';
|
2
2
|
import { Vec2 } from '../../math/Vec2.mjs';
|
3
3
|
import { stylesEqual } from '../RenderingStyle.mjs';
|
4
|
+
/**
|
5
|
+
* Abstract base class for renderers.
|
6
|
+
*
|
7
|
+
* @see {@link EditorImage.render}
|
8
|
+
*/
|
4
9
|
export default class AbstractRenderer {
|
5
10
|
constructor(viewport) {
|
6
11
|
this.viewport = viewport;
|
@@ -49,7 +54,12 @@ export default class AbstractRenderer {
|
|
49
54
|
if (lastStyle) {
|
50
55
|
this.endPath(lastStyle);
|
51
56
|
}
|
57
|
+
this.currentPaths = [];
|
52
58
|
}
|
59
|
+
/**
|
60
|
+
* Draws a styled path. If within an object started by {@link startObject},
|
61
|
+
* the resultant path may not be visible until {@link endObject} is called.
|
62
|
+
*/
|
53
63
|
drawPath(path) {
|
54
64
|
// If we're being called outside of an object,
|
55
65
|
// we can't delay rendering
|
@@ -70,14 +80,22 @@ export default class AbstractRenderer {
|
|
70
80
|
const path = Path.fromRect(rect, lineWidth);
|
71
81
|
this.drawPath(path.toRenderable(lineFill));
|
72
82
|
}
|
73
|
-
|
83
|
+
/** Draws a filled rectangle. */
|
74
84
|
fillRect(rect, fill) {
|
75
85
|
const path = Path.fromRect(rect);
|
76
86
|
this.drawPath(path.toRenderable({ fill }));
|
77
87
|
}
|
78
|
-
|
79
|
-
|
88
|
+
/**
|
89
|
+
* This should be called whenever a new object is being drawn.
|
90
|
+
*
|
91
|
+
* @param _boundingBox The bounding box of the object to be drawn.
|
92
|
+
* @param _clip Whether content outside `_boundingBox` should be drawn. Renderers
|
93
|
+
* that override this method are not required to support `_clip`.
|
94
|
+
*/
|
80
95
|
startObject(_boundingBox, _clip) {
|
96
|
+
if (this.objectLevel > 0) {
|
97
|
+
this.flushPath();
|
98
|
+
}
|
81
99
|
this.currentPaths = [];
|
82
100
|
this.objectLevel++;
|
83
101
|
}
|
@@ -175,14 +175,19 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
175
175
|
super.startObject(boundingBox);
|
176
176
|
this.currentObjectBBox = boundingBox;
|
177
177
|
if (!this.ignoringObject && clip) {
|
178
|
-
|
179
|
-
|
180
|
-
this.
|
181
|
-
|
182
|
-
|
183
|
-
this.ctx.
|
178
|
+
// Don't clip if it would only remove content already trimmed by
|
179
|
+
// the edge of the screen.
|
180
|
+
const clippedIsOutsideScreen = boundingBox.containsRect(this.getViewport().visibleRect);
|
181
|
+
if (!clippedIsOutsideScreen) {
|
182
|
+
this.clipLevels.push(this.objectLevel);
|
183
|
+
this.ctx.save();
|
184
|
+
this.ctx.beginPath();
|
185
|
+
for (const corner of boundingBox.corners) {
|
186
|
+
const screenCorner = this.canvasToScreen(corner);
|
187
|
+
this.ctx.lineTo(screenCorner.x, screenCorner.y);
|
188
|
+
}
|
189
|
+
this.ctx.clip();
|
184
190
|
}
|
185
|
-
this.ctx.clip();
|
186
191
|
}
|
187
192
|
}
|
188
193
|
endObject() {
|
@@ -46,6 +46,7 @@ export default abstract class BaseWidget {
|
|
46
46
|
protected updateIcon(): void;
|
47
47
|
setDisabled(disabled: boolean): void;
|
48
48
|
setSelected(selected: boolean): void;
|
49
|
+
private hideDropdownTimeout;
|
49
50
|
protected setDropdownVisible(visible: boolean): void;
|
50
51
|
canBeInOverflowMenu(): boolean;
|
51
52
|
getButtonWidth(): number;
|
@@ -23,6 +23,7 @@ export default class BaseWidget {
|
|
23
23
|
this.subWidgets = {};
|
24
24
|
this.toplevel = true;
|
25
25
|
this.toolbarWidgetToggleListener = null;
|
26
|
+
this.hideDropdownTimeout = null;
|
26
27
|
this.localizationTable = localizationTable !== null && localizationTable !== void 0 ? localizationTable : editor.localization;
|
27
28
|
this.icon = null;
|
28
29
|
this.container = document.createElement('div');
|
@@ -230,6 +231,13 @@ export default class BaseWidget {
|
|
230
231
|
if (currentlyVisible === visible) {
|
231
232
|
return;
|
232
233
|
}
|
234
|
+
// If waiting to hide the dropdown, cancel it.
|
235
|
+
if (this.hideDropdownTimeout) {
|
236
|
+
clearTimeout(this.hideDropdownTimeout);
|
237
|
+
this.hideDropdownTimeout = null;
|
238
|
+
this.dropdownContainer.classList.remove('hiding');
|
239
|
+
}
|
240
|
+
const animationDuration = 150; // ms
|
233
241
|
if (visible) {
|
234
242
|
this.dropdownContainer.classList.remove('hidden');
|
235
243
|
this.container.classList.add('dropdownVisible');
|
@@ -240,10 +248,20 @@ export default class BaseWidget {
|
|
240
248
|
});
|
241
249
|
}
|
242
250
|
else {
|
243
|
-
this.dropdownContainer.classList.add('hidden');
|
244
251
|
this.container.classList.remove('dropdownVisible');
|
245
252
|
this.editor.announceForAccessibility(this.localizationTable.dropdownHidden(this.getTitle()));
|
253
|
+
this.dropdownContainer.classList.add('hiding');
|
254
|
+
// Hide the dropdown *slightly* before the animation finishes. This
|
255
|
+
// prevents flickering in some browsers.
|
256
|
+
const hideDelay = animationDuration * 0.95;
|
257
|
+
this.hideDropdownTimeout = setTimeout(() => {
|
258
|
+
this.dropdownContainer.classList.add('hidden');
|
259
|
+
this.dropdownContainer.classList.remove('hiding');
|
260
|
+
}, hideDelay);
|
246
261
|
}
|
262
|
+
// Animate
|
263
|
+
const animationName = `var(--dropdown-${visible ? 'show' : 'hide'}-animation)`;
|
264
|
+
this.dropdownContainer.style.animation = `${animationDuration}ms ease ${animationName}`;
|
247
265
|
this.repositionDropdown();
|
248
266
|
}
|
249
267
|
canBeInOverflowMenu() {
|
@@ -262,11 +280,11 @@ export default class BaseWidget {
|
|
262
280
|
const dropdownBBox = this.dropdownContainer.getBoundingClientRect();
|
263
281
|
const screenWidth = document.body.clientWidth;
|
264
282
|
if (dropdownBBox.left > screenWidth / 2) {
|
265
|
-
|
266
|
-
|
283
|
+
// Use .translate so as not to conflict with CSS animating the
|
284
|
+
// transform property.
|
285
|
+
this.dropdownContainer.style.translate = `calc(${this.button.clientWidth + 'px'} - 100%) 0`;
|
267
286
|
}
|
268
287
|
else {
|
269
|
-
this.dropdownContainer.style.marginLeft = '';
|
270
288
|
this.dropdownContainer.style.transform = '';
|
271
289
|
}
|
272
290
|
}
|
@@ -14,9 +14,12 @@ export default class Pen extends BaseTool {
|
|
14
14
|
private builderFactory;
|
15
15
|
protected builder: ComponentBuilder | null;
|
16
16
|
private lastPoint;
|
17
|
+
private startPoint;
|
17
18
|
private ctrlKeyPressed;
|
19
|
+
private shiftKeyPressed;
|
18
20
|
constructor(editor: Editor, description: string, style: PenStyle, builderFactory?: ComponentBuilderFactory);
|
19
21
|
private getPressureMultiplier;
|
22
|
+
private xyAxesSnap;
|
20
23
|
protected toStrokePoint(pointer: Pointer): StrokeDataPoint;
|
21
24
|
protected previewStroke(): void;
|
22
25
|
protected addPointToStroke(point: StrokeDataPoint): void;
|
@@ -34,6 +37,7 @@ export default class Pen extends BaseTool {
|
|
34
37
|
getStrokeFactory(): ComponentBuilderFactory;
|
35
38
|
setEnabled(enabled: boolean): void;
|
36
39
|
private isSnappingToGrid;
|
40
|
+
private isAngleLocked;
|
37
41
|
onKeyPress({ key, ctrlKey }: KeyPressEvent): boolean;
|
38
42
|
onKeyUp({ key }: KeyUpEvent): boolean;
|
39
43
|
}
|
@@ -11,14 +11,26 @@ export default class Pen extends BaseTool {
|
|
11
11
|
this.builderFactory = builderFactory;
|
12
12
|
this.builder = null;
|
13
13
|
this.lastPoint = null;
|
14
|
+
this.startPoint = null;
|
14
15
|
this.ctrlKeyPressed = false;
|
16
|
+
this.shiftKeyPressed = false;
|
15
17
|
}
|
16
18
|
getPressureMultiplier() {
|
17
19
|
return 1 / this.editor.viewport.getScaleFactor() * this.style.thickness;
|
18
20
|
}
|
21
|
+
// Snap the given pointer to the nearer of the x/y axes.
|
22
|
+
xyAxesSnap(pointer) {
|
23
|
+
if (!this.startPoint) {
|
24
|
+
return pointer;
|
25
|
+
}
|
26
|
+
return pointer.lockedToXYAxes(this.startPoint.pos, this.editor.viewport);
|
27
|
+
}
|
19
28
|
// Converts a `pointer` to a `StrokeDataPoint`.
|
20
29
|
toStrokePoint(pointer) {
|
21
30
|
var _a;
|
31
|
+
if (this.isAngleLocked() && this.lastPoint) {
|
32
|
+
pointer = this.xyAxesSnap(pointer);
|
33
|
+
}
|
22
34
|
if (this.isSnappingToGrid()) {
|
23
35
|
pointer = pointer.snappedToGrid(this.editor.viewport);
|
24
36
|
}
|
@@ -64,7 +76,8 @@ export default class Pen extends BaseTool {
|
|
64
76
|
}
|
65
77
|
}
|
66
78
|
if ((allPointers.length === 1 && !isEraser) || anyDeviceIsStylus) {
|
67
|
-
this.
|
79
|
+
this.startPoint = this.toStrokePoint(current);
|
80
|
+
this.builder = this.builderFactory(this.startPoint, this.editor.viewport);
|
68
81
|
return true;
|
69
82
|
}
|
70
83
|
return false;
|
@@ -104,6 +117,7 @@ export default class Pen extends BaseTool {
|
|
104
117
|
}
|
105
118
|
}
|
106
119
|
this.builder = null;
|
120
|
+
this.lastPoint = null;
|
107
121
|
this.editor.clearWetInk();
|
108
122
|
}
|
109
123
|
noteUpdated() {
|
@@ -138,6 +152,7 @@ export default class Pen extends BaseTool {
|
|
138
152
|
this.ctrlKeyPressed = false;
|
139
153
|
}
|
140
154
|
isSnappingToGrid() { return this.ctrlKeyPressed; }
|
155
|
+
isAngleLocked() { return this.shiftKeyPressed; }
|
141
156
|
onKeyPress({ key, ctrlKey }) {
|
142
157
|
key = key.toLowerCase();
|
143
158
|
let newThickness;
|
@@ -156,6 +171,10 @@ export default class Pen extends BaseTool {
|
|
156
171
|
this.ctrlKeyPressed = true;
|
157
172
|
return true;
|
158
173
|
}
|
174
|
+
if (key === 'shift') {
|
175
|
+
this.shiftKeyPressed = true;
|
176
|
+
return true;
|
177
|
+
}
|
159
178
|
// Ctrl+Z: End the stroke so that it can be undone/redone.
|
160
179
|
if (key === 'z' && ctrlKey && this.builder) {
|
161
180
|
this.finalizeStroke();
|
@@ -168,6 +187,10 @@ export default class Pen extends BaseTool {
|
|
168
187
|
this.ctrlKeyPressed = false;
|
169
188
|
return true;
|
170
189
|
}
|
190
|
+
if (key === 'shift') {
|
191
|
+
this.shiftKeyPressed = false;
|
192
|
+
return true;
|
193
|
+
}
|
171
194
|
return false;
|
172
195
|
}
|
173
196
|
}
|
@@ -14,6 +14,7 @@ class SelectionTool extends BaseTool {
|
|
14
14
|
super(editor.notifier, description);
|
15
15
|
this.editor = editor;
|
16
16
|
this.lastEvtTarget = null;
|
17
|
+
this.startPoint = null; // canvas position
|
17
18
|
this.expandingSelectionBox = false;
|
18
19
|
this.shiftKeyPressed = false;
|
19
20
|
this.ctrlKeyPressed = false;
|
@@ -24,6 +25,9 @@ class SelectionTool extends BaseTool {
|
|
24
25
|
this.handleOverlay.classList.add('handleOverlay');
|
25
26
|
editor.notifier.on(EditorEventType.ViewportChanged, _data => {
|
26
27
|
var _a;
|
28
|
+
// The selection box could be using the wet ink display if its transformation
|
29
|
+
// hasn't been finalized yet. Clear before updating the UI.
|
30
|
+
this.editor.clearWetInk();
|
27
31
|
(_a = this.selectionBox) === null || _a === void 0 ? void 0 : _a.updateUI();
|
28
32
|
});
|
29
33
|
this.editor.handleKeyEventsFrom(this.handleOverlay);
|
@@ -61,6 +65,7 @@ class SelectionTool extends BaseTool {
|
|
61
65
|
current = current.snappedToGrid(this.editor.viewport);
|
62
66
|
}
|
63
67
|
if (allPointers.length === 1 && current.isPrimary) {
|
68
|
+
this.startPoint = current.canvasPos;
|
64
69
|
let transforming = false;
|
65
70
|
if (this.lastEvtTarget && this.selectionBox) {
|
66
71
|
if (snapToGrid) {
|
@@ -86,6 +91,9 @@ class SelectionTool extends BaseTool {
|
|
86
91
|
if (!this.selectionBox)
|
87
92
|
return;
|
88
93
|
let currentPointer = event.current;
|
94
|
+
if (!this.expandingSelectionBox && this.shiftKeyPressed && this.startPoint) {
|
95
|
+
currentPointer = currentPointer.lockedToXYAxes(this.startPoint, this.editor.viewport);
|
96
|
+
}
|
89
97
|
if (this.ctrlKeyPressed) {
|
90
98
|
currentPointer = currentPointer.snappedToGrid(this.editor.viewport);
|
91
99
|
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/**
|
2
|
+
* Resolves when all given promises have resolved. If no promises are given,
|
3
|
+
* does not return a Promise.
|
4
|
+
*/
|
5
|
+
const waitForAll = (results) => {
|
6
|
+
// If any are Promises...
|
7
|
+
if (results.some(command => command && command['then'])) {
|
8
|
+
// Wait for all commands to finish.
|
9
|
+
return Promise.all(results)
|
10
|
+
// Ensure we return a Promise<void> and not a Promise<void[]>
|
11
|
+
.then(() => { });
|
12
|
+
}
|
13
|
+
return;
|
14
|
+
};
|
15
|
+
export default waitForAll;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "js-draw",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.23.0",
|
4
4
|
"description": "Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript. ",
|
5
5
|
"types": "./dist/mjs/src/lib.d.ts",
|
6
6
|
"main": "./dist/cjs/src/lib.js",
|
@@ -89,25 +89,25 @@
|
|
89
89
|
"@types/bezier-js": "^4.1.0",
|
90
90
|
"@types/jest": "^29.5.1",
|
91
91
|
"@types/jsdom": "^21.1.1",
|
92
|
-
"@types/node": "^
|
93
|
-
"@typescript-eslint/eslint-plugin": "^5.59.
|
94
|
-
"@typescript-eslint/parser": "^5.59.
|
92
|
+
"@types/node": "^20.1.0",
|
93
|
+
"@typescript-eslint/eslint-plugin": "^5.59.2",
|
94
|
+
"@typescript-eslint/parser": "^5.59.2",
|
95
95
|
"css-loader": "^6.7.3",
|
96
|
-
"eslint": "^8.
|
96
|
+
"eslint": "^8.40.0",
|
97
97
|
"husky": "^8.0.3",
|
98
98
|
"jest": "^29.5.0",
|
99
99
|
"jest-environment-jsdom": "^29.5.0",
|
100
|
-
"jsdom": "^
|
100
|
+
"jsdom": "^22.0.0",
|
101
101
|
"lint-staged": "^13.2.2",
|
102
102
|
"pinst": "^3.0.0",
|
103
103
|
"style-loader": "^3.3.2",
|
104
|
-
"terser-webpack-plugin": "^5.3.
|
104
|
+
"terser-webpack-plugin": "^5.3.8",
|
105
105
|
"ts-jest": "^29.1.0",
|
106
106
|
"ts-loader": "^9.4.2",
|
107
107
|
"ts-node": "^10.9.1",
|
108
|
-
"typedoc": "^0.24.
|
108
|
+
"typedoc": "^0.24.7",
|
109
109
|
"typescript": "^5.0.4",
|
110
|
-
"webpack": "^5.
|
110
|
+
"webpack": "^5.82.0"
|
111
111
|
},
|
112
112
|
"bugs": {
|
113
113
|
"url": "https://github.com/personalizedrefrigerator/js-draw/issues"
|
package/src/toolbar/toolbar.css
CHANGED
@@ -81,6 +81,7 @@
|
|
81
81
|
|
82
82
|
.toolbar-button > label {
|
83
83
|
cursor: inherit;
|
84
|
+
user-select: none;
|
84
85
|
}
|
85
86
|
|
86
87
|
/* Decrease the font size of labels in the main toolbar if they're long. */
|
@@ -105,6 +106,7 @@
|
|
105
106
|
|
106
107
|
.toolbar-root .toolbar-icon {
|
107
108
|
flex-shrink: 1;
|
109
|
+
user-select: none;
|
108
110
|
|
109
111
|
width: 100%;
|
110
112
|
min-width: 30px;
|
@@ -131,7 +133,8 @@
|
|
131
133
|
}
|
132
134
|
|
133
135
|
.toolbar-dropdown.hidden,
|
134
|
-
.toolbar-toolContainer:not(.selected):not(.dropdownShowable)
|
136
|
+
.toolbar-toolContainer:not(.selected):not(.dropdownShowable)
|
137
|
+
> .toolbar-dropdown:not(.hiding) {
|
135
138
|
display: none;
|
136
139
|
}
|
137
140
|
|
@@ -153,6 +156,37 @@
|
|
153
156
|
box-shadow: 0px 3px 3px var(--primary-shadow-color);
|
154
157
|
}
|
155
158
|
|
159
|
+
/* Animate showing/hiding the dropdown. Animations triggered in JavaScript. */
|
160
|
+
@keyframes dropdown-transition-in {
|
161
|
+
0% {
|
162
|
+
opacity: 0;
|
163
|
+
transform: scale(1, 0);
|
164
|
+
}
|
165
|
+
100% {
|
166
|
+
opacity: 1;
|
167
|
+
transform: scale(1, 1);
|
168
|
+
}
|
169
|
+
}
|
170
|
+
|
171
|
+
@keyframes dropdown-transition-out {
|
172
|
+
0% {
|
173
|
+
opacity: 1;
|
174
|
+
transform: scale(1, 1);
|
175
|
+
}
|
176
|
+
100% {
|
177
|
+
opacity: 0;
|
178
|
+
transform: scale(1, 0);
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
182
|
+
.toolbar-dropdown {
|
183
|
+
/* Ensure the animation begins from the correct location. */
|
184
|
+
transform-origin: top left;
|
185
|
+
|
186
|
+
--dropdown-show-animation: dropdown-transition-in;
|
187
|
+
--dropdown-hide-animation: dropdown-transition-out;
|
188
|
+
}
|
189
|
+
|
156
190
|
.toolbar-buttonGroup {
|
157
191
|
display: flex;
|
158
192
|
flex-direction: row;
|