js-draw 1.11.2 → 1.13.1
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/dist/Editor.css +66 -118
- package/dist/bundle.js +2 -2
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/Editor.d.ts +4 -2
- package/dist/cjs/Editor.js +61 -39
- package/dist/cjs/components/AbstractComponent.d.ts +7 -0
- package/dist/cjs/components/AbstractComponent.js +7 -5
- package/dist/cjs/components/util/StrokeSmoother.js +11 -4
- package/dist/cjs/localizations/es.js +11 -22
- package/dist/cjs/rendering/Display.d.ts +2 -0
- package/dist/cjs/rendering/Display.js +4 -0
- package/dist/cjs/toolbar/DropdownToolbar.d.ts +3 -1
- package/dist/cjs/toolbar/DropdownToolbar.js +2 -0
- package/dist/cjs/toolbar/EdgeToolbar.js +6 -5
- package/dist/cjs/toolbar/widgets/BaseWidget.js +2 -0
- package/dist/cjs/toolbar/widgets/components/makeGridSelector.js +10 -0
- package/dist/cjs/util/addLongPressOrHoverCssClasses.d.ts +10 -0
- package/dist/cjs/util/addLongPressOrHoverCssClasses.js +34 -0
- package/dist/cjs/util/listenForLongPressOrHover.d.ts +13 -0
- package/dist/cjs/util/listenForLongPressOrHover.js +80 -0
- package/dist/cjs/util/listenForLongPressOrHover.test.d.ts +1 -0
- package/dist/cjs/util/mitLicenseAttribution.d.ts +2 -0
- package/dist/cjs/util/mitLicenseAttribution.js +28 -0
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/Editor.d.ts +4 -2
- package/dist/mjs/Editor.mjs +61 -39
- package/dist/mjs/components/AbstractComponent.d.ts +7 -0
- package/dist/mjs/components/AbstractComponent.mjs +7 -5
- package/dist/mjs/components/util/StrokeSmoother.mjs +11 -4
- package/dist/mjs/localizations/es.mjs +11 -22
- package/dist/mjs/rendering/Display.d.ts +2 -0
- package/dist/mjs/rendering/Display.mjs +4 -0
- package/dist/mjs/toolbar/DropdownToolbar.d.ts +3 -1
- package/dist/mjs/toolbar/DropdownToolbar.mjs +2 -0
- package/dist/mjs/toolbar/EdgeToolbar.mjs +6 -5
- package/dist/mjs/toolbar/widgets/BaseWidget.mjs +2 -0
- package/dist/mjs/toolbar/widgets/components/makeGridSelector.mjs +10 -0
- package/dist/mjs/util/addLongPressOrHoverCssClasses.d.ts +10 -0
- package/dist/mjs/util/addLongPressOrHoverCssClasses.mjs +29 -0
- package/dist/mjs/util/listenForLongPressOrHover.d.ts +13 -0
- package/dist/mjs/util/listenForLongPressOrHover.mjs +78 -0
- package/dist/mjs/util/listenForLongPressOrHover.test.d.ts +1 -0
- package/dist/mjs/util/mitLicenseAttribution.d.ts +2 -0
- package/dist/mjs/util/mitLicenseAttribution.mjs +26 -0
- package/dist/mjs/version.mjs +1 -1
- package/package.json +2 -2
- package/src/toolbar/EdgeToolbar.scss +19 -97
- package/src/toolbar/utils/labelVisibleOnHover.scss +114 -0
- package/src/toolbar/widgets/components/makeGridSelector.scss +1 -0
@@ -1,5 +1,6 @@
|
|
1
1
|
import { MutableReactiveValue } from '../../../util/ReactiveValue.mjs';
|
2
2
|
import stopPropagationOfScrollingWheelEvents from '../../../util/stopPropagationOfScrollingWheelEvents.mjs';
|
3
|
+
import addLongPressOrHoverCssClasses from '../../../util/addLongPressOrHoverCssClasses.mjs';
|
3
4
|
import { toolbarCSSPrefix } from '../../constants.mjs';
|
4
5
|
let idCounter = 0;
|
5
6
|
/**
|
@@ -31,6 +32,9 @@ labelText, defaultId, choices) => {
|
|
31
32
|
const button = document.createElement('input');
|
32
33
|
button.type = 'radio';
|
33
34
|
button.id = `${toolbarCSSPrefix}-grid-select-button-${idCounter++}`;
|
35
|
+
// Some toolbars only show the label on hover. Having long press or hover
|
36
|
+
// CSS classes are helpful here.
|
37
|
+
addLongPressOrHoverCssClasses(buttonContainer);
|
34
38
|
// Clicking any part of labelContainer triggers the radio button.
|
35
39
|
const labelContainer = document.createElement('label');
|
36
40
|
const rebuildLabel = () => {
|
@@ -76,6 +80,12 @@ labelText, defaultId, choices) => {
|
|
76
80
|
button.onblur = () => {
|
77
81
|
buttonContainer.classList.remove('focus-visible');
|
78
82
|
};
|
83
|
+
// Prevent the right-click menu from being shown on long-press
|
84
|
+
// (important for some toolbars that use long-press gestures to
|
85
|
+
// show grid selector labels).
|
86
|
+
buttonContainer.oncontextmenu = event => {
|
87
|
+
event.preventDefault();
|
88
|
+
};
|
79
89
|
buttonContainer.replaceChildren(button, labelContainer);
|
80
90
|
menuContainer.appendChild(buttonContainer);
|
81
91
|
// Set whether the current button is checked
|
@@ -0,0 +1,10 @@
|
|
1
|
+
/**
|
2
|
+
* When a pointer is inside `element`, after a delay, adds the `has-long-press-or-hover`
|
3
|
+
* CSS class to `element`.
|
4
|
+
*
|
5
|
+
* When no pointers are inside `element`, adds the CSS class `no-long-press-or-hover`.
|
6
|
+
*/
|
7
|
+
declare const addLongPressOrHoverCssClasses: (element: HTMLElement) => {
|
8
|
+
removeEventListeners: () => void;
|
9
|
+
};
|
10
|
+
export default addLongPressOrHoverCssClasses;
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import listenForLongPressOrHover from './listenForLongPressOrHover.mjs';
|
2
|
+
/**
|
3
|
+
* When a pointer is inside `element`, after a delay, adds the `has-long-press-or-hover`
|
4
|
+
* CSS class to `element`.
|
5
|
+
*
|
6
|
+
* When no pointers are inside `element`, adds the CSS class `no-long-press-or-hover`.
|
7
|
+
*/
|
8
|
+
const addLongPressOrHoverCssClasses = (element) => {
|
9
|
+
const hasLongPressClass = 'has-long-press-or-hover';
|
10
|
+
const noLongPressClass = 'no-long-press-or-hover';
|
11
|
+
element.classList.add('no-long-press-or-hover');
|
12
|
+
const { removeListeners } = listenForLongPressOrHover(element, {
|
13
|
+
onStart() {
|
14
|
+
element.classList.remove(noLongPressClass);
|
15
|
+
element.classList.add(hasLongPressClass);
|
16
|
+
},
|
17
|
+
onEnd() {
|
18
|
+
element.classList.add(noLongPressClass);
|
19
|
+
element.classList.remove(hasLongPressClass);
|
20
|
+
},
|
21
|
+
});
|
22
|
+
return {
|
23
|
+
removeEventListeners: () => {
|
24
|
+
element.classList.remove(noLongPressClass);
|
25
|
+
removeListeners();
|
26
|
+
},
|
27
|
+
};
|
28
|
+
};
|
29
|
+
export default addLongPressOrHoverCssClasses;
|
@@ -0,0 +1,13 @@
|
|
1
|
+
type Options = {
|
2
|
+
onStart: () => void;
|
3
|
+
onEnd: () => void;
|
4
|
+
longPressTimeout?: number;
|
5
|
+
};
|
6
|
+
/**
|
7
|
+
* Calls `options.onStart` at the start of a long press or hover.
|
8
|
+
* Calls `options.onEnd` when no pointers are within the container.
|
9
|
+
*/
|
10
|
+
declare const listenForLongPressOrHover: (target: HTMLElement, options: Options) => {
|
11
|
+
removeListeners: () => void;
|
12
|
+
};
|
13
|
+
export default listenForLongPressOrHover;
|
@@ -0,0 +1,78 @@
|
|
1
|
+
/**
|
2
|
+
* Calls `options.onStart` at the start of a long press or hover.
|
3
|
+
* Calls `options.onEnd` when no pointers are within the container.
|
4
|
+
*/
|
5
|
+
const listenForLongPressOrHover = (target, options) => {
|
6
|
+
const pointersInside = new Map();
|
7
|
+
let timeoutId = null;
|
8
|
+
let isLongPressInProgress = false;
|
9
|
+
const updateTimeout = () => {
|
10
|
+
if (pointersInside.size === 0) {
|
11
|
+
if (isLongPressInProgress) {
|
12
|
+
isLongPressInProgress = false;
|
13
|
+
options.onEnd();
|
14
|
+
}
|
15
|
+
else if (timeoutId !== null) {
|
16
|
+
clearTimeout(timeoutId);
|
17
|
+
timeoutId = null;
|
18
|
+
}
|
19
|
+
}
|
20
|
+
else {
|
21
|
+
const nowTime = Date.now();
|
22
|
+
let timeSinceFirstPointer = 0; // ms
|
23
|
+
for (const record of pointersInside.values()) {
|
24
|
+
const timeSince = nowTime - record.timeEnter;
|
25
|
+
timeSinceFirstPointer = Math.max(timeSince, timeSinceFirstPointer);
|
26
|
+
}
|
27
|
+
const longPressTimeout = options.longPressTimeout ?? 700; // ms
|
28
|
+
if (timeoutId !== null) {
|
29
|
+
clearTimeout(timeoutId);
|
30
|
+
timeoutId = null;
|
31
|
+
}
|
32
|
+
const timeLeft = longPressTimeout - timeSinceFirstPointer;
|
33
|
+
if (timeLeft <= 0) {
|
34
|
+
options.onStart();
|
35
|
+
isLongPressInProgress = true;
|
36
|
+
}
|
37
|
+
else {
|
38
|
+
timeoutId = setTimeout(() => {
|
39
|
+
timeoutId = null;
|
40
|
+
updateTimeout();
|
41
|
+
}, timeLeft);
|
42
|
+
}
|
43
|
+
}
|
44
|
+
};
|
45
|
+
// Detects long press
|
46
|
+
const pointerEventListener = (event) => {
|
47
|
+
const eventRecord = {
|
48
|
+
timeEnter: Date.now(),
|
49
|
+
};
|
50
|
+
if (event.type === 'pointerenter') {
|
51
|
+
pointersInside.set(event.pointerId, eventRecord);
|
52
|
+
}
|
53
|
+
else if (event.type === 'pointerleave' || event.type === 'pointercancel') {
|
54
|
+
// In some cases (for example, a click with a stylus on Android/Chrome), moving the pen
|
55
|
+
// over the target, clicking, then moving the pen out of the target produces input
|
56
|
+
// similar to this:
|
57
|
+
// - pointerenter (pointerId: 4)
|
58
|
+
// - pointerleave (pointerId: 4)
|
59
|
+
// - pointerenter (pointerId: 6)
|
60
|
+
// - pointerenter (pointerId: 1)
|
61
|
+
// - pointerleave (pointerId: 6)
|
62
|
+
// Observe that no pointerleave event was fired for the pointer with ID 1.
|
63
|
+
pointersInside.clear();
|
64
|
+
}
|
65
|
+
updateTimeout();
|
66
|
+
};
|
67
|
+
target.addEventListener('pointerenter', pointerEventListener);
|
68
|
+
target.addEventListener('pointerleave', pointerEventListener);
|
69
|
+
target.addEventListener('pointercancel', pointerEventListener);
|
70
|
+
return {
|
71
|
+
removeListeners: () => {
|
72
|
+
target.removeEventListener('pointerenter', pointerEventListener);
|
73
|
+
target.removeEventListener('pointerleave', pointerEventListener);
|
74
|
+
target.removeEventListener('pointercancel', pointerEventListener);
|
75
|
+
},
|
76
|
+
};
|
77
|
+
};
|
78
|
+
export default listenForLongPressOrHover;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,26 @@
|
|
1
|
+
const mitLicenseAttribution = (copyright) => {
|
2
|
+
const removeSingleLineBreaks = (text) => text.replace(/([^\n])[\n]([^\n])/g, '$1 $2');
|
3
|
+
return removeSingleLineBreaks(`
|
4
|
+
MIT License
|
5
|
+
|
6
|
+
Copyright (c) ${copyright}
|
7
|
+
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
of this software and associated documentation files (the "Software"), to deal
|
10
|
+
in the Software without restriction, including without limitation the rights
|
11
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
+
copies of the Software, and to permit persons to whom the Software is
|
13
|
+
furnished to do so, subject to the following conditions:
|
14
|
+
|
15
|
+
The above copyright notice and this permission notice shall be included in all
|
16
|
+
copies or substantial portions of the Software.
|
17
|
+
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
24
|
+
SOFTWARE.`);
|
25
|
+
};
|
26
|
+
export default mitLicenseAttribution;
|
package/dist/mjs/version.mjs
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "js-draw",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.13.1",
|
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/lib.d.ts",
|
6
6
|
"main": "./dist/cjs/lib.js",
|
@@ -86,5 +86,5 @@
|
|
86
86
|
"freehand",
|
87
87
|
"svg"
|
88
88
|
],
|
89
|
-
"gitHead": "
|
89
|
+
"gitHead": "814f5d57897fd16fd45860522d9ecbc6b8b04a38"
|
90
90
|
}
|
@@ -1,3 +1,5 @@
|
|
1
|
+
@use "./utils/labelVisibleOnHover.scss";
|
2
|
+
|
1
3
|
@keyframes toolbar--edgemenu-transition-in {
|
2
4
|
from { transform: translate(0, 100%); }
|
3
5
|
to { transform: translate(0, 0); }
|
@@ -21,94 +23,6 @@
|
|
21
23
|
to { overflow-y: hidden; }
|
22
24
|
}
|
23
25
|
|
24
|
-
// Shows a lable on hover
|
25
|
-
// Uses the --button-label-hover-offset-y and --button-label-hover-offset-x
|
26
|
-
// variables to determine the label's position.
|
27
|
-
//
|
28
|
-
// If the label is in a scrolling container, that container should have position: relative;
|
29
|
-
// as this allows absolutely positioned children to scroll with the container (rather than
|
30
|
-
// remaining stationary).
|
31
|
-
@mixin label-visible-on-hover($label-selector) {
|
32
|
-
$label-visible-opacity: 0.8;
|
33
|
-
|
34
|
-
// Make the label hide only when hovering.
|
35
|
-
|
36
|
-
@keyframes rehide-label {
|
37
|
-
0% { opacity: $label-visible-opacity; }
|
38
|
-
80% { opacity: $label-visible-opacity; }
|
39
|
-
100% { opacity: 0.1; }
|
40
|
-
}
|
41
|
-
|
42
|
-
@keyframes show-label {
|
43
|
-
0% { opacity: 0; }
|
44
|
-
// Keep the label hidden before showing
|
45
|
-
80% { opacity: 0; }
|
46
|
-
100% { opacity: $label-visible-opacity; }
|
47
|
-
}
|
48
|
-
|
49
|
-
@keyframes keep-label-hidden {
|
50
|
-
0% { opacity: 0; }
|
51
|
-
100% { opacity: 0; }
|
52
|
-
}
|
53
|
-
|
54
|
-
|
55
|
-
$hover-active-animation: ease show-label;
|
56
|
-
|
57
|
-
// Avoids sticky hover on touch devices. See
|
58
|
-
// https://css-tricks.com/solving-sticky-hover-states-with-media-hover-hover/
|
59
|
-
// When the primary device supports hover:
|
60
|
-
@media (hover: hover) {
|
61
|
-
// Only show an animation when opening the label due to a hover --
|
62
|
-
// show the label immediately otherwise.
|
63
|
-
&:hover:not(:focus-visible) > #{$label-selector} {
|
64
|
-
opacity: $label-visible-opacity;
|
65
|
-
animation: 1s $hover-active-animation;
|
66
|
-
}
|
67
|
-
}
|
68
|
-
|
69
|
-
// When the user is pressing/long-pressing the button
|
70
|
-
&:active > #{$label-selector} {
|
71
|
-
opacity: $label-visible-opacity;
|
72
|
-
animation: 1s $hover-active-animation;
|
73
|
-
}
|
74
|
-
|
75
|
-
$keyboard-hide-animation: 1.5s ease rehide-label;
|
76
|
-
|
77
|
-
&:focus-visible > #{$label-selector} {
|
78
|
-
animation: $keyboard-hide-animation;
|
79
|
-
opacity: 0;
|
80
|
-
}
|
81
|
-
|
82
|
-
// Make the :has selector separate its own statement -- some browsers don't
|
83
|
-
// support :has, which would make the entire statement block have no effect.
|
84
|
-
&:has(:focus-visible) > #{$label-selector} {
|
85
|
-
animation: $keyboard-hide-animation;
|
86
|
-
opacity: 0;
|
87
|
-
}
|
88
|
-
|
89
|
-
& > #{$label-selector} {
|
90
|
-
opacity: 0;
|
91
|
-
position: absolute;
|
92
|
-
margin-top: var(--button-label-hover-offset-y);
|
93
|
-
margin-left: var(--button-label-hover-offset-x);
|
94
|
-
|
95
|
-
// The label is often mostly invisible/just below a toolbar item.
|
96
|
-
// If there are multiple toolbar rows, ensure that a label doesn't prevent
|
97
|
-
// clicking on items in the second row:
|
98
|
-
pointer-events: none;
|
99
|
-
|
100
|
-
background-color: var(--background-color-1);
|
101
|
-
color: var(--foreground-color-1);
|
102
|
-
border-radius: 25px;
|
103
|
-
padding: 10px;
|
104
|
-
|
105
|
-
transition: 0.2s ease opacity, 0.2s ease margin-top;
|
106
|
-
|
107
|
-
@media (prefers-reduced-motion: reduce) {
|
108
|
-
transition: none;
|
109
|
-
}
|
110
|
-
}
|
111
|
-
}
|
112
26
|
|
113
27
|
// The toolbar portion (the bar along the top of the screen)
|
114
28
|
.toolbar-edge-toolbar {
|
@@ -134,7 +48,7 @@
|
|
134
48
|
}
|
135
49
|
}
|
136
50
|
|
137
|
-
// Hide labels when above a certain width
|
51
|
+
// Hide inline labels when above a certain width
|
138
52
|
@media screen and (max-width: 700px) {
|
139
53
|
&.one-row > * > .toolbar-toolContainer.label-inline {
|
140
54
|
font-size: 0.9em;
|
@@ -152,7 +66,8 @@
|
|
152
66
|
animation: 0.2s linear hide-initially;
|
153
67
|
}
|
154
68
|
|
155
|
-
|
69
|
+
// DO show the labels on hover.
|
70
|
+
@include labelVisibleOnHover.label-visible-on-hover(label);
|
156
71
|
|
157
72
|
// Clear additional margins added because of the left/right side labels.
|
158
73
|
// (Repeat selector to increase specificity).
|
@@ -296,14 +211,21 @@
|
|
296
211
|
--button-label-hover-offset-y: var(--label-hover-offset-size);
|
297
212
|
--button-label-hover-offset-x: 0;
|
298
213
|
|
299
|
-
.toolbar-toolContainer:not(.no-icon):not(.label-inline)
|
300
|
-
|
214
|
+
.toolbar-toolContainer:not(.no-icon):not(.label-inline) {
|
215
|
+
|
216
|
+
.toolbar-button {
|
217
|
+
width: calc(var(--toolbar-button-size) + var(--extra-left-right-padding));
|
218
|
+
|
219
|
+
// Note: EdgeToolbar.ts currently assumes that the height of a button is
|
220
|
+
// equivalent to --toolbar-button-size.
|
221
|
+
height: var(--toolbar-button-size);
|
301
222
|
|
302
|
-
|
303
|
-
|
304
|
-
height: var(--toolbar-button-size);
|
223
|
+
@include labelVisibleOnHover.label-visible-on-hover(label);
|
224
|
+
}
|
305
225
|
|
306
|
-
|
226
|
+
&.dropdownVisible > .toolbar-button {
|
227
|
+
@include labelVisibleOnHover.show-label-now(label);
|
228
|
+
}
|
307
229
|
}
|
308
230
|
|
309
231
|
& > div > .toolbar-toolContainer:not(.selected):not(.dropdownShowable) > .toolbar-button > .toolbar-showHideDropdownIcon {
|
@@ -388,7 +310,7 @@
|
|
388
310
|
// Special styles for the enum selector
|
389
311
|
.toolbar-grid-selector .choice-button {
|
390
312
|
--button-label-hover-offset-y: var(--button-size);
|
391
|
-
@include label-visible-on-hover('label > .button-label-text');
|
313
|
+
@include labelVisibleOnHover.label-visible-on-hover('label > .button-label-text');
|
392
314
|
}
|
393
315
|
}
|
394
316
|
|
@@ -0,0 +1,114 @@
|
|
1
|
+
|
2
|
+
$label-visible-opacity: 0.8;
|
3
|
+
$show-delay: 0.3s;
|
4
|
+
|
5
|
+
@keyframes rehide-label {
|
6
|
+
0% { opacity: $label-visible-opacity; }
|
7
|
+
80% { opacity: $label-visible-opacity; }
|
8
|
+
100% { opacity: 0.1; }
|
9
|
+
}
|
10
|
+
|
11
|
+
@keyframes show-label-delayed {
|
12
|
+
0% { opacity: 0; }
|
13
|
+
// Keep the label hidden before showing
|
14
|
+
80% { opacity: 0; }
|
15
|
+
100% { opacity: $label-visible-opacity; }
|
16
|
+
}
|
17
|
+
|
18
|
+
@keyframes show-label-now {
|
19
|
+
0% { opacity: 0; }
|
20
|
+
5% { opacity: 0; }
|
21
|
+
100% { opacity: $label-visible-opacity; }
|
22
|
+
}
|
23
|
+
|
24
|
+
@keyframes keep-label-hidden {
|
25
|
+
0% { opacity: 0; }
|
26
|
+
100% { opacity: 0; }
|
27
|
+
}
|
28
|
+
|
29
|
+
// Shows a lable on hover
|
30
|
+
// Uses the --button-label-hover-offset-y and --button-label-hover-offset-x
|
31
|
+
// variables to determine the label's position.
|
32
|
+
//
|
33
|
+
// If the label is in a scrolling container, that container should have position: relative;
|
34
|
+
// as this allows absolutely positioned children to scroll with the container (rather than
|
35
|
+
// remaining stationary).
|
36
|
+
@mixin label-visible-on-hover($label-selector) {
|
37
|
+
// Using CSS classes set by addLongPressOrHoverCssClasses avoids sticky
|
38
|
+
// hover on touch devices.
|
39
|
+
&:not(.no-long-press-or-hover):not(.has-long-press-or-hover) {
|
40
|
+
// Only show an animation when opening the label due to a hover --
|
41
|
+
// show the label immediately otherwise.
|
42
|
+
&:hover:not(:focus-visible) > #{$label-selector},
|
43
|
+
// When the user is pressing/long-pressing the button
|
44
|
+
&:active > #{$label-selector}
|
45
|
+
{
|
46
|
+
opacity: $label-visible-opacity;
|
47
|
+
animation: 1s ease show-label-delayed;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
&.has-long-press-or-hover > #{$label-selector} {
|
52
|
+
opacity: $label-visible-opacity;
|
53
|
+
}
|
54
|
+
|
55
|
+
$keyboard-hide-animation: 1.5s ease rehide-label;
|
56
|
+
|
57
|
+
// .focus-visible: Allow setting focus-visible from JS (in cases where this
|
58
|
+
// element isn't the focus target and the browser doesn't support :has).
|
59
|
+
&:focus-visible, &.focus-visible {
|
60
|
+
& > #{$label-selector} {
|
61
|
+
animation: $keyboard-hide-animation;
|
62
|
+
opacity: 0;
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
// Make the :has selector separate its own statement -- some browsers don't
|
67
|
+
// support :has, which would make the entire statement block have no effect.
|
68
|
+
&:has(:focus-visible) > #{$label-selector} {
|
69
|
+
animation: $keyboard-hide-animation;
|
70
|
+
opacity: 0;
|
71
|
+
}
|
72
|
+
|
73
|
+
& > #{$label-selector} {
|
74
|
+
opacity: 0;
|
75
|
+
position: absolute;
|
76
|
+
margin-top: var(--button-label-hover-offset-y);
|
77
|
+
margin-left: var(--button-label-hover-offset-x);
|
78
|
+
z-index: 1;
|
79
|
+
|
80
|
+
// The label is often mostly invisible/just below a toolbar item.
|
81
|
+
// If there are multiple toolbar rows, ensure that a label doesn't prevent
|
82
|
+
// clicking on items in the second row:
|
83
|
+
pointer-events: none;
|
84
|
+
|
85
|
+
background-color: var(--background-color-1);
|
86
|
+
color: var(--foreground-color-1);
|
87
|
+
border-radius: 25px;
|
88
|
+
padding: 10px;
|
89
|
+
|
90
|
+
transition: $show-delay ease opacity, 0.2s ease margin-top;
|
91
|
+
|
92
|
+
@media (prefers-reduced-motion: reduce) {
|
93
|
+
transition: none;
|
94
|
+
}
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
// Allows manually showing the label
|
99
|
+
@mixin show-label-now($label-selector) {
|
100
|
+
& > #{$label-selector} {
|
101
|
+
// Set opacity manually (rather than in an animation) to take advantage of
|
102
|
+
// `transition`. Without this, when animations are cancelled and replaced,
|
103
|
+
// the label may flicker.
|
104
|
+
opacity: $label-visible-opacity;
|
105
|
+
|
106
|
+
$hide-animation-length: 1.5s;
|
107
|
+
$keep-hidden-delay: calc($hide-animation-length + $show-delay);
|
108
|
+
|
109
|
+
$hide-animation: 1.5s ease rehide-label $show-delay;
|
110
|
+
$keep-hidden-animation: 1s ease keep-label-hidden $keep-hidden-delay infinite;
|
111
|
+
|
112
|
+
animation: $hide-animation, $keep-hidden-animation;
|
113
|
+
}
|
114
|
+
}
|