js-draw 1.11.0 → 1.11.2
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 +6 -2
- package/dist/bundle.js +3 -3
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/Editor.d.ts +7 -0
- package/dist/cjs/Editor.js +23 -6
- package/dist/cjs/components/BackgroundComponent.d.ts +1 -1
- package/dist/cjs/components/ImageComponent.d.ts +1 -1
- package/dist/cjs/components/SVGGlobalAttributesObject.d.ts +1 -1
- package/dist/cjs/components/Stroke.d.ts +1 -1
- package/dist/cjs/rendering/Display.js +3 -1
- package/dist/cjs/rendering/renderers/DummyRenderer.d.ts +1 -0
- package/dist/cjs/rendering/renderers/DummyRenderer.js +3 -0
- package/dist/cjs/tools/FindTool.js +1 -1
- package/dist/cjs/tools/Pen.js +8 -2
- package/dist/cjs/tools/SelectionTool/SelectionTool.js +16 -2
- package/dist/cjs/tools/localization.d.ts +2 -0
- package/dist/cjs/tools/localization.js +2 -0
- package/dist/cjs/util/listenForKeyboardEventsFrom.d.ts +5 -0
- package/dist/cjs/util/listenForKeyboardEventsFrom.js +5 -1
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/Editor.d.ts +7 -0
- package/dist/mjs/Editor.mjs +23 -6
- package/dist/mjs/components/BackgroundComponent.d.ts +1 -1
- package/dist/mjs/components/ImageComponent.d.ts +1 -1
- package/dist/mjs/components/SVGGlobalAttributesObject.d.ts +1 -1
- package/dist/mjs/components/Stroke.d.ts +1 -1
- package/dist/mjs/rendering/Display.mjs +3 -1
- package/dist/mjs/rendering/renderers/DummyRenderer.d.ts +1 -0
- package/dist/mjs/rendering/renderers/DummyRenderer.mjs +3 -0
- package/dist/mjs/tools/FindTool.mjs +1 -1
- package/dist/mjs/tools/Pen.mjs +8 -2
- package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +16 -2
- package/dist/mjs/tools/localization.d.ts +2 -0
- package/dist/mjs/tools/localization.mjs +2 -0
- package/dist/mjs/util/listenForKeyboardEventsFrom.d.ts +5 -0
- package/dist/mjs/util/listenForKeyboardEventsFrom.mjs +5 -1
- package/dist/mjs/version.mjs +1 -1
- package/package.json +5 -5
- package/src/toolbar/AbstractToolbar.scss +3 -2
- package/src/toolbar/widgets/components/makeColorInput.scss +8 -0
package/dist/mjs/Editor.mjs
CHANGED
@@ -327,6 +327,17 @@ export class Editor {
|
|
327
327
|
}
|
328
328
|
return res;
|
329
329
|
}
|
330
|
+
/**
|
331
|
+
* A protected method that can override setPointerCapture in environments where it may fail
|
332
|
+
* (e.g. with synthetic events). @internal
|
333
|
+
*/
|
334
|
+
setPointerCapture(target, pointerId) {
|
335
|
+
target.setPointerCapture(pointerId);
|
336
|
+
}
|
337
|
+
/** Can be overridden in a testing environment to handle synthetic events. @internal */
|
338
|
+
releasePointerCapture(target, pointerId) {
|
339
|
+
target.releasePointerCapture(pointerId);
|
340
|
+
}
|
330
341
|
/**
|
331
342
|
* Dispatches a `PointerEvent` to the editor. The target element for `evt` must have the same top left
|
332
343
|
* as the content of the editor.
|
@@ -337,7 +348,7 @@ export class Editor {
|
|
337
348
|
if (eventType === 'pointerdown') {
|
338
349
|
const pointer = Pointer.ofEvent(evt, true, this.viewport, eventsRelativeTo);
|
339
350
|
this.pointers[pointer.id] = pointer;
|
340
|
-
|
351
|
+
this.setPointerCapture(eventTarget, pointer.id);
|
341
352
|
const event = {
|
342
353
|
kind: InputEvtType.PointerDownEvt,
|
343
354
|
current: pointer,
|
@@ -374,7 +385,7 @@ export class Editor {
|
|
374
385
|
return false;
|
375
386
|
}
|
376
387
|
this.pointers[pointer.id] = pointer;
|
377
|
-
|
388
|
+
this.releasePointerCapture(eventTarget, pointer.id);
|
378
389
|
if (this.toolController.dispatchInputEvent({
|
379
390
|
kind: InputEvtType.PointerUpEvt,
|
380
391
|
current: pointer,
|
@@ -551,7 +562,8 @@ export class Editor {
|
|
551
562
|
return false;
|
552
563
|
}
|
553
564
|
// Position of the current event.
|
554
|
-
|
565
|
+
// jsdom doesn't seem to support pageX/pageY -- use clientX/clientY if unavailable
|
566
|
+
const currentPos = Vec2.of(event.pageX ?? event.clientX, event.pageY ?? event.clientY);
|
555
567
|
const pointerId = event.pointerId ?? 0;
|
556
568
|
// Whether to send the current event to the editor
|
557
569
|
let sendToEditor = true;
|
@@ -561,9 +573,10 @@ export class Editor {
|
|
561
573
|
gestureData[pointerId] = {
|
562
574
|
eventBuffer: [[eventName, event]],
|
563
575
|
startPoint: currentPos,
|
576
|
+
hasMovedSignificantly: false,
|
564
577
|
};
|
565
578
|
// Capture the pointer so we receive future events even if the overlay is hidden.
|
566
|
-
|
579
|
+
this.setPointerCapture(elem, event.pointerId);
|
567
580
|
// Don't send to the editor.
|
568
581
|
sendToEditor = false;
|
569
582
|
}
|
@@ -573,7 +586,7 @@ export class Editor {
|
|
573
586
|
// Skip if the pointer hasn't moved enough to not be a "click".
|
574
587
|
const strokeStartThreshold = 10;
|
575
588
|
const isWithinClickThreshold = gestureStartPos && currentPos.minus(gestureStartPos).magnitude() < strokeStartThreshold;
|
576
|
-
if (isWithinClickThreshold) {
|
589
|
+
if (isWithinClickThreshold && !gestureData[pointerId].hasMovedSignificantly) {
|
577
590
|
eventBuffer.push([eventName, event]);
|
578
591
|
sendToEditor = false;
|
579
592
|
}
|
@@ -583,6 +596,7 @@ export class Editor {
|
|
583
596
|
this.handleHTMLPointerEvent(eventName, event);
|
584
597
|
}
|
585
598
|
gestureData[pointerId].eventBuffer = [];
|
599
|
+
gestureData[pointerId].hasMovedSignificantly = true;
|
586
600
|
sendToEditor = true;
|
587
601
|
}
|
588
602
|
}
|
@@ -595,7 +609,7 @@ export class Editor {
|
|
595
609
|
// pointercancel event.
|
596
610
|
else if ((eventName === 'pointerup' || eventName === 'pointercancel')
|
597
611
|
&& gestureData[pointerId] && gestureData[pointerId].eventBuffer.length > 0) {
|
598
|
-
|
612
|
+
this.releasePointerCapture(elem, event.pointerId);
|
599
613
|
// Don't send to the editor.
|
600
614
|
sendToEditor = false;
|
601
615
|
delete gestureData[pointerId];
|
@@ -643,6 +657,9 @@ export class Editor {
|
|
643
657
|
handleKeyUp: (htmlEvent) => {
|
644
658
|
this.handleHTMLKeyUpEvent(htmlEvent);
|
645
659
|
},
|
660
|
+
getHandlesKeyEventsFrom: (element) => {
|
661
|
+
return this.eventListenerTargets.includes(element);
|
662
|
+
},
|
646
663
|
});
|
647
664
|
// Allow drop.
|
648
665
|
elem.ondragover = evt => {
|
@@ -64,5 +64,5 @@ export default class BackgroundComponent extends AbstractComponent implements Re
|
|
64
64
|
protected applyTransformation(_affineTransfm: Mat33): void;
|
65
65
|
description(localizationTable: ImageComponentLocalization): string;
|
66
66
|
protected createClone(): AbstractComponent;
|
67
|
-
static deserializeFromJSON(json: any): BackgroundComponent;
|
67
|
+
static deserializeFromJSON(this: void, json: any): BackgroundComponent;
|
68
68
|
}
|
@@ -15,6 +15,6 @@ export default class SVGGlobalAttributesObject extends AbstractComponent {
|
|
15
15
|
protected createClone(): SVGGlobalAttributesObject;
|
16
16
|
description(localization: ImageComponentLocalization): string;
|
17
17
|
protected serializeToJSON(): string | null;
|
18
|
-
static deserializeFromString(_data: string): AbstractComponent;
|
18
|
+
static deserializeFromString(this: void, _data: string): AbstractComponent;
|
19
19
|
}
|
20
20
|
export {};
|
@@ -70,7 +70,9 @@ export default class Display {
|
|
70
70
|
},
|
71
71
|
blockResolution: cacheBlockResolution,
|
72
72
|
cacheSize: 600 * 600 * 4 * 90,
|
73
|
-
|
73
|
+
// On higher resolution displays, don't scale cache blocks as much to decrease blurriness.
|
74
|
+
// TODO: Decrease the minimum cache scale as well.
|
75
|
+
maxScale: Math.max(1, 1.3 / window.devicePixelRatio),
|
74
76
|
// Require about 20 strokes with 4 parts each to cache an image in one of the
|
75
77
|
// parts of the cache grid.
|
76
78
|
minProportionalRenderTimePerCache: 20 * 4,
|
@@ -29,4 +29,5 @@ export default class DummyRenderer extends AbstractRenderer {
|
|
29
29
|
isTooSmallToRender(_rect: Rect2): boolean;
|
30
30
|
canRenderFromWithoutDataLoss(other: AbstractRenderer): boolean;
|
31
31
|
renderFromOtherOfSameType(transform: Mat33, other: AbstractRenderer): void;
|
32
|
+
toString(): string;
|
32
33
|
}
|
@@ -34,7 +34,7 @@ export default class FindTool extends BaseTool {
|
|
34
34
|
}
|
35
35
|
if (matchIdx < matches.length) {
|
36
36
|
const undoable = false;
|
37
|
-
this.editor.dispatch(this.editor.viewport.zoomTo(matches[matchIdx], true, true), undoable);
|
37
|
+
void this.editor.dispatch(this.editor.viewport.zoomTo(matches[matchIdx], true, true), undoable);
|
38
38
|
this.editor.announceForAccessibility(this.editor.localization.focusedFoundText(matchIdx + 1, matches.length));
|
39
39
|
}
|
40
40
|
}
|
package/dist/mjs/tools/Pen.mjs
CHANGED
@@ -97,8 +97,8 @@ export default class Pen extends BaseTool {
|
|
97
97
|
this.currentDeviceType = current.device;
|
98
98
|
if (this.shapeAutocompletionEnabled) {
|
99
99
|
const stationaryDetectionConfig = {
|
100
|
-
maxSpeed: 5,
|
101
|
-
maxRadius:
|
100
|
+
maxSpeed: 8.5,
|
101
|
+
maxRadius: 11,
|
102
102
|
minTimeSeconds: 0.5, // s
|
103
103
|
};
|
104
104
|
this.stationaryDetector = new StationaryPenDetector(current, stationaryDetectionConfig, pointer => this.autocorrectShape(pointer));
|
@@ -141,6 +141,7 @@ export default class Pen extends BaseTool {
|
|
141
141
|
if (this.autocorrectedShape) {
|
142
142
|
this.removedAutocorrectedShapeTime = performance.now();
|
143
143
|
this.autocorrectedShape = null;
|
144
|
+
this.editor.announceForAccessibility(this.editor.localization.autocorrectionCanceled);
|
144
145
|
}
|
145
146
|
}
|
146
147
|
}
|
@@ -192,6 +193,8 @@ export default class Pen extends BaseTool {
|
|
192
193
|
if (bboxArea === 0 || !isFinite(bboxArea)) {
|
193
194
|
return;
|
194
195
|
}
|
196
|
+
const shapeDescription = correctedShape.description(this.editor.localization);
|
197
|
+
this.editor.announceForAccessibility(this.editor.localization.autocorrectedTo(shapeDescription));
|
195
198
|
this.autocorrectedShape = correctedShape;
|
196
199
|
this.lastAutocorrectedShape = correctedShape;
|
197
200
|
this.previewStroke();
|
@@ -206,6 +209,9 @@ export default class Pen extends BaseTool {
|
|
206
209
|
const stroke = this.autocorrectedShape ?? this.builder.build();
|
207
210
|
this.previewStroke();
|
208
211
|
if (stroke.getBBox().area > 0) {
|
212
|
+
if (stroke === this.autocorrectedShape) {
|
213
|
+
this.editor.announceForAccessibility(this.editor.localization.autocorrectedTo(stroke.description(this.editor.localization)));
|
214
|
+
}
|
209
215
|
const canFlatten = true;
|
210
216
|
const action = EditorImage.addElement(stroke, canFlatten);
|
211
217
|
this.editor.dispatch(action);
|
@@ -231,9 +231,11 @@ class SelectionTool extends BaseTool {
|
|
231
231
|
// Pass it to another tool, if apliccable.
|
232
232
|
return false;
|
233
233
|
}
|
234
|
-
else if (event.key === 'Shift') {
|
234
|
+
else if (event.shiftKey || event.key === 'Shift') {
|
235
235
|
this.shiftKeyPressed = true;
|
236
|
-
|
236
|
+
if (event.key === 'Shift') {
|
237
|
+
return true;
|
238
|
+
}
|
237
239
|
}
|
238
240
|
let rotationSteps = 0;
|
239
241
|
let xTranslateSteps = 0;
|
@@ -335,6 +337,14 @@ class SelectionTool extends BaseTool {
|
|
335
337
|
}
|
336
338
|
return true;
|
337
339
|
}
|
340
|
+
// Here, we check if shiftKey === false because, as of this writing,
|
341
|
+
// evt.shiftKey is an optional property. Being falsey could just mean
|
342
|
+
// that it wasn't set.
|
343
|
+
if (evt.shiftKey === false) {
|
344
|
+
this.shiftKeyPressed = false;
|
345
|
+
// Don't return immediately -- event may be otherwise handled
|
346
|
+
}
|
347
|
+
// Also check for key === 'Shift' (for the case where shiftKey is undefined)
|
338
348
|
if (evt.key === 'Shift') {
|
339
349
|
this.shiftKeyPressed = false;
|
340
350
|
return true;
|
@@ -379,7 +389,11 @@ class SelectionTool extends BaseTool {
|
|
379
389
|
return true;
|
380
390
|
}
|
381
391
|
setEnabled(enabled) {
|
392
|
+
const wasEnabled = this.isEnabled();
|
382
393
|
super.setEnabled(enabled);
|
394
|
+
if (wasEnabled === enabled) {
|
395
|
+
return;
|
396
|
+
}
|
383
397
|
// Clear the selection
|
384
398
|
this.selectionBox?.cancelSelection();
|
385
399
|
this.onSelectionUpdated();
|
@@ -9,6 +9,8 @@ export interface ToolLocalization {
|
|
9
9
|
undoRedoTool: string;
|
10
10
|
pipetteTool: string;
|
11
11
|
rightClickDragPanTool: string;
|
12
|
+
autocorrectedTo: (description: string) => string;
|
13
|
+
autocorrectionCanceled: string;
|
12
14
|
textTool: string;
|
13
15
|
enterTextToInsert: string;
|
14
16
|
changeTool: string;
|
@@ -9,6 +9,8 @@ export const defaultToolLocalization = {
|
|
9
9
|
rightClickDragPanTool: 'Right-click drag',
|
10
10
|
pipetteTool: 'Pick color from screen',
|
11
11
|
keyboardPanZoom: 'Keyboard pan/zoom shortcuts',
|
12
|
+
autocorrectedTo: (strokeDescription) => `Autocorrected to ${strokeDescription}`,
|
13
|
+
autocorrectionCanceled: 'Autocorrect cancelled',
|
12
14
|
textTool: 'Text',
|
13
15
|
enterTextToInsert: 'Text to insert',
|
14
16
|
changeTool: 'Change tool',
|
@@ -2,6 +2,11 @@ interface Callbacks {
|
|
2
2
|
filter(event: KeyboardEvent): boolean;
|
3
3
|
handleKeyDown(event: KeyboardEvent): void;
|
4
4
|
handleKeyUp(event: KeyboardEvent): void;
|
5
|
+
/**
|
6
|
+
* Should return `true` iff `source` is also registered as an event listener source.
|
7
|
+
* If `false` and focus leaves the original source, keyup events are fired.
|
8
|
+
*/
|
9
|
+
getHandlesKeyEventsFrom(source: Node): boolean;
|
5
10
|
}
|
6
11
|
/**
|
7
12
|
* Calls `callbacks` when different keys are known to be pressed.
|
@@ -65,7 +65,11 @@ const listenForKeyboardEventsFrom = (elem, callbacks) => {
|
|
65
65
|
handleKeyEvent(htmlEvent);
|
66
66
|
});
|
67
67
|
elem.addEventListener('focusout', (focusEvent) => {
|
68
|
-
|
68
|
+
let stillHasFocus = false;
|
69
|
+
if (focusEvent.relatedTarget) {
|
70
|
+
const relatedTarget = focusEvent.relatedTarget;
|
71
|
+
stillHasFocus = elem.contains(relatedTarget) || callbacks.getHandlesKeyEventsFrom(relatedTarget);
|
72
|
+
}
|
69
73
|
if (!stillHasFocus) {
|
70
74
|
for (const event of keysDown) {
|
71
75
|
callbacks.handleKeyUp(new KeyboardEvent('keyup', {
|
package/dist/mjs/version.mjs
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "js-draw",
|
3
|
-
"version": "1.11.
|
3
|
+
"version": "1.11.2",
|
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",
|
@@ -64,11 +64,11 @@
|
|
64
64
|
"postpack": "ts-node tools/copyREADME.ts revert"
|
65
65
|
},
|
66
66
|
"dependencies": {
|
67
|
-
"@js-draw/math": "^1.
|
68
|
-
"@melloware/coloris": "0.
|
67
|
+
"@js-draw/math": "^1.11.1",
|
68
|
+
"@melloware/coloris": "0.22.0"
|
69
69
|
},
|
70
70
|
"devDependencies": {
|
71
|
-
"@js-draw/build-tool": "^1.
|
71
|
+
"@js-draw/build-tool": "^1.11.1",
|
72
72
|
"@types/jest": "29.5.5",
|
73
73
|
"@types/jsdom": "21.1.3"
|
74
74
|
},
|
@@ -86,5 +86,5 @@
|
|
86
86
|
"freehand",
|
87
87
|
"svg"
|
88
88
|
],
|
89
|
-
"gitHead": "
|
89
|
+
"gitHead": "e1d593def957ec85633ff89166f9af25ae03f862"
|
90
90
|
}
|
@@ -45,7 +45,8 @@
|
|
45
45
|
}
|
46
46
|
|
47
47
|
.toolbar-button.disabled {
|
48
|
-
filter:
|
48
|
+
filter: sepia(0.2);
|
49
|
+
opacity: 0.45;
|
49
50
|
cursor: unset;
|
50
51
|
}
|
51
52
|
|
@@ -103,7 +104,7 @@
|
|
103
104
|
|
104
105
|
.toolbar-root button:disabled {
|
105
106
|
cursor: inherit;
|
106
|
-
|
107
|
+
opacity: 0.5;
|
107
108
|
}
|
108
109
|
|
109
110
|
.toolbar-root .toolbar-icon {
|
@@ -29,6 +29,14 @@
|
|
29
29
|
display: inline-flex;
|
30
30
|
flex-direction: row;
|
31
31
|
|
32
|
+
.coloris_input {
|
33
|
+
// Ensure that the region that can be clicked to open the input is roughly
|
34
|
+
// the full height of the container.
|
35
|
+
// Because the color picker is always shown below or above the input, 5px is
|
36
|
+
// subtracted to make the picker better align with the input's container.
|
37
|
+
height: calc(100% - 6px);
|
38
|
+
}
|
39
|
+
|
32
40
|
&.picker-open .clr-field {
|
33
41
|
// Work around what seems to be a Coloris bug -- clicking on the input button
|
34
42
|
// keeps the color picker open (while clicking anywhere else closes the picker).
|