js-draw 1.12.0 → 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 +48 -37
- package/dist/cjs/components/AbstractComponent.d.ts +7 -0
- package/dist/cjs/components/AbstractComponent.js +7 -5
- package/dist/cjs/components/util/StrokeSmoother.d.ts +0 -1
- package/dist/cjs/components/util/StrokeSmoother.js +6 -17
- 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/version.js +1 -1
- package/dist/mjs/Editor.d.ts +4 -2
- package/dist/mjs/Editor.mjs +48 -37
- package/dist/mjs/components/AbstractComponent.d.ts +7 -0
- package/dist/mjs/components/AbstractComponent.mjs +7 -5
- package/dist/mjs/components/util/StrokeSmoother.d.ts +0 -1
- package/dist/mjs/components/util/StrokeSmoother.mjs +6 -17
- 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/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
@@ -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 {};
|
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
|
+
}
|