js-draw 0.24.1 → 0.25.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/bundle.js +2 -2
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/Pointer.js +5 -3
- package/dist/cjs/localizations/de.js +1 -1
- package/dist/cjs/localizations/es.js +1 -1
- package/dist/cjs/rendering/renderers/CanvasRenderer.js +2 -0
- package/dist/cjs/rendering/renderers/SVGRenderer.d.ts +1 -1
- package/dist/cjs/rendering/renderers/SVGRenderer.js +4 -2
- package/dist/cjs/testing/getUniquePointerId.d.ts +4 -0
- package/dist/cjs/testing/getUniquePointerId.js +16 -0
- package/dist/cjs/testing/sendPenEvent.d.ts +1 -1
- package/dist/cjs/testing/sendPenEvent.js +4 -1
- package/dist/cjs/testing/sendTouchEvent.js +2 -9
- package/dist/cjs/toolbar/IconProvider.d.ts +1 -1
- package/dist/cjs/toolbar/IconProvider.js +76 -10
- package/dist/cjs/toolbar/localization.d.ts +2 -2
- package/dist/cjs/toolbar/localization.js +2 -2
- package/dist/cjs/toolbar/widgets/BaseToolWidget.d.ts +2 -0
- package/dist/cjs/toolbar/widgets/BaseToolWidget.js +7 -0
- package/dist/cjs/toolbar/widgets/PenToolWidget.d.ts +3 -1
- package/dist/cjs/toolbar/widgets/PenToolWidget.js +125 -41
- package/dist/cjs/toolbar/widgets/SelectionToolWidget.js +4 -0
- package/dist/cjs/tools/BaseTool.d.ts +17 -1
- package/dist/cjs/tools/BaseTool.js +18 -0
- package/dist/cjs/tools/Pen.d.ts +5 -2
- package/dist/cjs/tools/Pen.js +37 -4
- package/dist/cjs/tools/ToolController.js +14 -2
- package/dist/mjs/Pointer.mjs +5 -3
- package/dist/mjs/localizations/de.mjs +1 -1
- package/dist/mjs/localizations/es.mjs +1 -1
- package/dist/mjs/rendering/renderers/CanvasRenderer.mjs +2 -0
- package/dist/mjs/rendering/renderers/SVGRenderer.d.ts +1 -1
- package/dist/mjs/rendering/renderers/SVGRenderer.mjs +4 -2
- package/dist/mjs/testing/getUniquePointerId.d.ts +4 -0
- package/dist/mjs/testing/getUniquePointerId.mjs +14 -0
- package/dist/mjs/testing/sendPenEvent.d.ts +1 -1
- package/dist/mjs/testing/sendPenEvent.mjs +4 -1
- package/dist/mjs/testing/sendTouchEvent.mjs +2 -9
- package/dist/mjs/toolbar/IconProvider.d.ts +1 -1
- package/dist/mjs/toolbar/IconProvider.mjs +76 -10
- package/dist/mjs/toolbar/localization.d.ts +2 -2
- package/dist/mjs/toolbar/localization.mjs +2 -2
- package/dist/mjs/toolbar/widgets/BaseToolWidget.d.ts +2 -0
- package/dist/mjs/toolbar/widgets/BaseToolWidget.mjs +7 -0
- package/dist/mjs/toolbar/widgets/PenToolWidget.d.ts +3 -1
- package/dist/mjs/toolbar/widgets/PenToolWidget.mjs +125 -41
- package/dist/mjs/toolbar/widgets/SelectionToolWidget.mjs +4 -0
- package/dist/mjs/tools/BaseTool.d.ts +17 -1
- package/dist/mjs/tools/BaseTool.mjs +18 -0
- package/dist/mjs/tools/Pen.d.ts +5 -2
- package/dist/mjs/tools/Pen.mjs +37 -4
- package/dist/mjs/tools/ToolController.mjs +14 -2
- package/package.json +2 -2
- package/src/Pointer.ts +5 -2
- package/src/localizations/de.ts +2 -2
- package/src/localizations/es.ts +1 -1
- package/src/rendering/renderers/CanvasRenderer.ts +2 -0
- package/src/rendering/renderers/SVGRenderer.ts +6 -3
- package/src/testing/getUniquePointerId.ts +18 -0
- package/src/testing/sendPenEvent.ts +6 -1
- package/src/testing/sendTouchEvent.ts +2 -9
- package/src/toolbar/IconProvider.ts +92 -23
- package/src/toolbar/localization.ts +4 -4
- package/src/toolbar/toolbar.css +1 -0
- package/src/toolbar/widgets/BaseToolWidget.ts +10 -1
- package/src/toolbar/widgets/PenToolWidget.css +53 -0
- package/src/toolbar/widgets/PenToolWidget.ts +156 -44
- package/src/toolbar/widgets/SelectionToolWidget.ts +4 -0
- package/src/tools/BaseTool.ts +22 -1
- package/src/tools/Pen.test.ts +68 -0
- package/src/tools/Pen.ts +42 -4
- package/src/tools/ToolController.ts +17 -2
package/dist/mjs/tools/Pen.mjs
CHANGED
@@ -42,6 +42,7 @@ var Pen = /** @class */ (function (_super) {
|
|
42
42
|
_this.builder = null;
|
43
43
|
_this.lastPoint = null;
|
44
44
|
_this.startPoint = null;
|
45
|
+
_this.currentDeviceType = null;
|
45
46
|
_this.snapToGridEnabled = false;
|
46
47
|
_this.angleLockEnabled = false;
|
47
48
|
return _this;
|
@@ -98,8 +99,8 @@ var Pen = /** @class */ (function (_super) {
|
|
98
99
|
this.lastPoint = point;
|
99
100
|
this.previewStroke();
|
100
101
|
};
|
101
|
-
Pen.prototype.onPointerDown = function (
|
102
|
-
var current =
|
102
|
+
Pen.prototype.onPointerDown = function (event) {
|
103
|
+
var current = event.current, allPointers = event.allPointers;
|
103
104
|
var isEraser = current.device === PointerDevice.Eraser;
|
104
105
|
var anyDeviceIsStylus = false;
|
105
106
|
for (var _i = 0, allPointers_1 = allPointers; _i < allPointers_1.length; _i++) {
|
@@ -109,24 +110,54 @@ var Pen = /** @class */ (function (_super) {
|
|
109
110
|
break;
|
110
111
|
}
|
111
112
|
}
|
113
|
+
// Avoid canceling an existing stroke
|
114
|
+
if (this.builder && !this.eventCanCancelStroke(event)) {
|
115
|
+
return true;
|
116
|
+
}
|
112
117
|
if ((allPointers.length === 1 && !isEraser) || anyDeviceIsStylus) {
|
113
118
|
this.startPoint = this.toStrokePoint(current);
|
114
119
|
this.builder = this.builderFactory(this.startPoint, this.editor.viewport);
|
120
|
+
this.currentDeviceType = current.device;
|
115
121
|
return true;
|
116
122
|
}
|
117
123
|
return false;
|
118
124
|
};
|
125
|
+
Pen.prototype.eventCanCancelStroke = function (event) {
|
126
|
+
var _a, _b;
|
127
|
+
// If there has been a delay since the last input event,
|
128
|
+
// it's always okay to cancel
|
129
|
+
var lastInputTime = (_b = (_a = this.lastPoint) === null || _a === void 0 ? void 0 : _a.time) !== null && _b !== void 0 ? _b : 0;
|
130
|
+
if (event.current.timeStamp - lastInputTime > 1000) {
|
131
|
+
return true;
|
132
|
+
}
|
133
|
+
var isPenStroke = this.currentDeviceType === PointerDevice.Pen;
|
134
|
+
var isTouchEvent = event.current.device === PointerDevice.Touch;
|
135
|
+
// Don't allow pen strokes to be cancelled by touch events.
|
136
|
+
if (isPenStroke && isTouchEvent) {
|
137
|
+
return false;
|
138
|
+
}
|
139
|
+
return true;
|
140
|
+
};
|
141
|
+
Pen.prototype.eventCanBeDeliveredToNonActiveTool = function (event) {
|
142
|
+
return this.eventCanCancelStroke(event);
|
143
|
+
};
|
119
144
|
Pen.prototype.onPointerMove = function (_a) {
|
120
145
|
var current = _a.current;
|
121
146
|
if (!this.builder)
|
122
147
|
return;
|
148
|
+
if (current.device !== this.currentDeviceType)
|
149
|
+
return;
|
123
150
|
this.addPointToStroke(this.toStrokePoint(current));
|
124
151
|
};
|
125
152
|
Pen.prototype.onPointerUp = function (_a) {
|
126
153
|
var _b, _c;
|
127
154
|
var current = _a.current;
|
128
|
-
if (!this.builder)
|
129
|
-
return;
|
155
|
+
if (!this.builder)
|
156
|
+
return false;
|
157
|
+
if (current.device !== this.currentDeviceType) {
|
158
|
+
// this.builder still exists, so we're handling events from another
|
159
|
+
// device type.
|
160
|
+
return true;
|
130
161
|
}
|
131
162
|
// onPointerUp events can have zero pressure. Use the last pressure instead.
|
132
163
|
var currentPoint = this.toStrokePoint(current);
|
@@ -135,8 +166,10 @@ var Pen = /** @class */ (function (_super) {
|
|
135
166
|
if (current.isPrimary) {
|
136
167
|
this.finalizeStroke();
|
137
168
|
}
|
169
|
+
return false;
|
138
170
|
};
|
139
171
|
Pen.prototype.onGestureCancel = function () {
|
172
|
+
this.builder = null;
|
140
173
|
this.editor.clearWetInk();
|
141
174
|
};
|
142
175
|
Pen.prototype.finalizeStroke = function () {
|
@@ -106,8 +106,15 @@ var ToolController = /** @class */ (function () {
|
|
106
106
|
var _a, _b;
|
107
107
|
var handled = false;
|
108
108
|
if (event.kind === InputEvtType.PointerDownEvt) {
|
109
|
+
var canOnlySendToActiveTool = false;
|
110
|
+
if (this.activeTool && !this.activeTool.eventCanBeDeliveredToNonActiveTool(event)) {
|
111
|
+
canOnlySendToActiveTool = true;
|
112
|
+
}
|
109
113
|
for (var _i = 0, _c = this.tools; _i < _c.length; _i++) {
|
110
114
|
var tool = _c[_i];
|
115
|
+
if (canOnlySendToActiveTool && tool !== this.activeTool) {
|
116
|
+
continue;
|
117
|
+
}
|
111
118
|
if (tool.isEnabled() && tool.onPointerDown(event)) {
|
112
119
|
if (this.activeTool !== tool) {
|
113
120
|
(_a = this.activeTool) === null || _a === void 0 ? void 0 : _a.onGestureCancel();
|
@@ -119,8 +126,13 @@ var ToolController = /** @class */ (function () {
|
|
119
126
|
}
|
120
127
|
}
|
121
128
|
else if (event.kind === InputEvtType.PointerUpEvt) {
|
122
|
-
(_b = this.activeTool) === null || _b === void 0 ? void 0 : _b.onPointerUp(event);
|
123
|
-
|
129
|
+
var upResult = (_b = this.activeTool) === null || _b === void 0 ? void 0 : _b.onPointerUp(event);
|
130
|
+
var continueHandlingEvents = upResult && event.allPointers.length > 1;
|
131
|
+
// Should the active tool continue handling events (without an additional pointer down?)
|
132
|
+
if (!continueHandlingEvents) {
|
133
|
+
// No -- Remove the current tool
|
134
|
+
this.activeTool = null;
|
135
|
+
}
|
124
136
|
handled = true;
|
125
137
|
}
|
126
138
|
else if (event.kind === InputEvtType.PointerMoveEvt) {
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "js-draw",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.25.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",
|
@@ -79,7 +79,7 @@
|
|
79
79
|
"bezier-js": "6.1.3"
|
80
80
|
},
|
81
81
|
"devDependencies": {
|
82
|
-
"@js-draw/build-tool": "^0.
|
82
|
+
"@js-draw/build-tool": "^0.25.0",
|
83
83
|
"@types/bezier-js": "4.1.0",
|
84
84
|
"@types/jest": "29.5.2",
|
85
85
|
"@types/jsdom": "21.1.1"
|
package/src/Pointer.ts
CHANGED
@@ -122,9 +122,12 @@ export default class Pointer {
|
|
122
122
|
if (device === PointerDevice.PrimaryButtonMouse) {
|
123
123
|
if (evt.buttons & 0x2) {
|
124
124
|
device = PointerDevice.RightButtonMouse;
|
125
|
-
} else if (!(evt.buttons & 0x1)) {
|
126
|
-
device = PointerDevice.Other;
|
127
125
|
}
|
126
|
+
// Commented out to work around a bug in old versions of Chrome:
|
127
|
+
// Left mouse up events were being given type "other".
|
128
|
+
// else if (!(evt.buttons & 0x1)) {
|
129
|
+
// device = PointerDevice.Other;
|
130
|
+
//}
|
128
131
|
}
|
129
132
|
|
130
133
|
return new Pointer(
|
package/src/localizations/de.ts
CHANGED
@@ -38,8 +38,8 @@ const localization: EditorLocalization = {
|
|
38
38
|
anyDevicePanning: 'Ansicht mit jedem Eingabegerät verschieben',
|
39
39
|
|
40
40
|
selectPenType: 'Objekt-Typ: ',
|
41
|
-
|
42
|
-
|
41
|
+
roundedTipPen: 'Freihand',
|
42
|
+
flatTipPen: 'Stift (druckempfindlich)',
|
43
43
|
arrowPen: 'Pfeil',
|
44
44
|
linePen: 'Linie',
|
45
45
|
outlinedRectanglePen: 'Umrissenes Rechteck',
|
package/src/localizations/es.ts
CHANGED
@@ -30,7 +30,7 @@ const localization: EditorLocalization = {
|
|
30
30
|
filledRectanglePen: 'Rectángulo sin borde',
|
31
31
|
linePen: 'Línea',
|
32
32
|
arrowPen: 'Flecha',
|
33
|
-
|
33
|
+
roundedTipPen: 'Lapiz Redondeado',
|
34
34
|
selectPenType: 'Forma de dibuja:',
|
35
35
|
handTool: 'Mover',
|
36
36
|
zoom: 'Zoom',
|
@@ -116,6 +116,7 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
116
116
|
}
|
117
117
|
|
118
118
|
protected endPath(style: RenderingStyle) {
|
119
|
+
this.ctx.save();
|
119
120
|
this.ctx.fillStyle = style.fill.toHexString();
|
120
121
|
this.ctx.fill();
|
121
122
|
|
@@ -128,6 +129,7 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
128
129
|
}
|
129
130
|
|
130
131
|
this.ctx.closePath();
|
132
|
+
this.ctx.restore();
|
131
133
|
}
|
132
134
|
|
133
135
|
protected lineTo(point: Point2) {
|
@@ -104,10 +104,11 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
104
104
|
}
|
105
105
|
}
|
106
106
|
|
107
|
-
// Push
|
108
|
-
|
107
|
+
// Push `this.fullPath` to the SVG. Returns the path added to the SVG, if any.
|
108
|
+
// @internal
|
109
|
+
protected addPathToSVG() {
|
109
110
|
if (!this.lastPathStyle || this.lastPathString.length === 0) {
|
110
|
-
return;
|
111
|
+
return null;
|
111
112
|
}
|
112
113
|
|
113
114
|
const pathElem = document.createElementNS(svgNameSpace, 'path');
|
@@ -127,6 +128,8 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
127
128
|
|
128
129
|
this.elem.appendChild(pathElem);
|
129
130
|
this.objectElems?.push(pathElem);
|
131
|
+
|
132
|
+
return pathElem;
|
130
133
|
}
|
131
134
|
|
132
135
|
public override drawPath(pathSpec: RenderablePathSpec) {
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import Pointer from '../Pointer';
|
2
|
+
|
3
|
+
/** Returns the smallest ID not used by the pointers in the given list. */
|
4
|
+
const getUniquePointerId = (pointers: Pointer[]) => {
|
5
|
+
let ptrId = 0;
|
6
|
+
|
7
|
+
const pointerIds = pointers.map(ptr => ptr.id);
|
8
|
+
pointerIds.sort();
|
9
|
+
for (const pointerId of pointerIds) {
|
10
|
+
if (ptrId === pointerId) {
|
11
|
+
ptrId = pointerId + 1;
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
return ptrId;
|
16
|
+
};
|
17
|
+
|
18
|
+
export default getUniquePointerId;
|
@@ -2,6 +2,7 @@ import Editor from '../Editor';
|
|
2
2
|
import { Point2 } from '../math/Vec2';
|
3
3
|
import Pointer from '../Pointer';
|
4
4
|
import { InputEvtType } from '../types';
|
5
|
+
import getUniquePointerId from './getUniquePointerId';
|
5
6
|
|
6
7
|
/**
|
7
8
|
* Dispatch a pen event to the currently selected tool.
|
@@ -16,8 +17,10 @@ const sendPenEvent = (
|
|
16
17
|
|
17
18
|
allPointers?: Pointer[]
|
18
19
|
) => {
|
20
|
+
const id = getUniquePointerId(allPointers ?? []);
|
21
|
+
|
19
22
|
const mainPointer = Pointer.ofCanvasPoint(
|
20
|
-
point, eventType !== InputEvtType.PointerUpEvt, editor.viewport
|
23
|
+
point, eventType !== InputEvtType.PointerUpEvt, editor.viewport, id
|
21
24
|
);
|
22
25
|
|
23
26
|
editor.toolController.dispatchInputEvent({
|
@@ -27,5 +30,7 @@ const sendPenEvent = (
|
|
27
30
|
],
|
28
31
|
current: mainPointer,
|
29
32
|
});
|
33
|
+
|
34
|
+
return mainPointer;
|
30
35
|
};
|
31
36
|
export default sendPenEvent;
|
@@ -2,6 +2,7 @@ import Editor from '../Editor';
|
|
2
2
|
import { Vec2 } from '../math/Vec2';
|
3
3
|
import Pointer, { PointerDevice } from '../Pointer';
|
4
4
|
import { InputEvtType } from '../types';
|
5
|
+
import getUniquePointerId from './getUniquePointerId';
|
5
6
|
|
6
7
|
/**
|
7
8
|
* Dispatch a touch event to the currently selected tool. Intended for unit tests.
|
@@ -47,17 +48,9 @@ const sendTouchEvent = (
|
|
47
48
|
) => {
|
48
49
|
const canvasPos = editor.viewport.screenToCanvas(screenPos);
|
49
50
|
|
50
|
-
let ptrId = 0;
|
51
|
-
let maxPtrId = 0;
|
52
|
-
|
53
51
|
// Get a unique ID for the main pointer
|
54
52
|
// (try to use id=0, but don't use it if it's already in use).
|
55
|
-
|
56
|
-
maxPtrId = Math.max(pointer.id, maxPtrId);
|
57
|
-
if (pointer.id === ptrId) {
|
58
|
-
ptrId = maxPtrId + 1;
|
59
|
-
}
|
60
|
-
}
|
53
|
+
const ptrId = getUniquePointerId(allOtherPointers ?? []);
|
61
54
|
|
62
55
|
const mainPointer = Pointer.ofCanvasPoint(
|
63
56
|
canvasPos, eventType !== InputEvtType.PointerUpEvt, editor.viewport, ptrId, PointerDevice.Touch
|
@@ -16,20 +16,28 @@ const iconColorFill = `
|
|
16
16
|
const iconColorStrokeFill = `
|
17
17
|
style='fill: var(--icon-color); stroke: var(--icon-color);'
|
18
18
|
`;
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
19
|
+
|
20
|
+
let checkerboardIdCounter = 0;
|
21
|
+
const makeCheckerboardPattern = () => {
|
22
|
+
const id = `checkerboard-${checkerboardIdCounter++}`;
|
23
|
+
const patternDef = `
|
24
|
+
<pattern
|
25
|
+
id='${id}'
|
26
|
+
viewBox='0,0,10,10'
|
27
|
+
width='20%'
|
28
|
+
height='20%'
|
29
|
+
patternUnits='userSpaceOnUse'
|
30
|
+
>
|
31
|
+
<rect x=0 y=0 width=10 height=10 fill='white'/>
|
32
|
+
<rect x=0 y=0 width=5 height=5 fill='gray'/>
|
33
|
+
<rect x=5 y=5 width=5 height=5 fill='gray'/>
|
34
|
+
</pattern>
|
35
|
+
`;
|
36
|
+
const patternRef = `url(#${id})`;
|
37
|
+
|
38
|
+
return { patternDef, patternRef };
|
39
|
+
};
|
40
|
+
|
33
41
|
|
34
42
|
/**
|
35
43
|
* Provides icons that can be used in the toolbar, etc.
|
@@ -451,13 +459,15 @@ export default class IconProvider {
|
|
451
459
|
Color4.fromString(color), tipThickness / 40 - 0.1
|
452
460
|
).toHexString();
|
453
461
|
|
462
|
+
const checkerboardPattern = makeCheckerboardPattern();
|
463
|
+
|
454
464
|
const ink = `
|
455
465
|
<path
|
456
|
-
fill="${
|
466
|
+
fill="${checkerboardPattern.patternRef}"
|
457
467
|
d="${inkTipPath}"
|
458
468
|
/>
|
459
469
|
<path
|
460
|
-
fill="${
|
470
|
+
fill="${checkerboardPattern.patternRef}"
|
461
471
|
d="${inkTrailPath}"
|
462
472
|
/>
|
463
473
|
<path
|
@@ -472,7 +482,7 @@ export default class IconProvider {
|
|
472
482
|
|
473
483
|
const penTip = `
|
474
484
|
<path
|
475
|
-
fill="${
|
485
|
+
fill="${checkerboardPattern.patternRef}"
|
476
486
|
d="${penTipPath}"
|
477
487
|
/>
|
478
488
|
<path
|
@@ -500,7 +510,7 @@ export default class IconProvider {
|
|
500
510
|
|
501
511
|
<!-- color bubble -->
|
502
512
|
<path
|
503
|
-
fill="${
|
513
|
+
fill="${checkerboardPattern.patternRef}"
|
504
514
|
d="${colorBubblePath}"
|
505
515
|
/>
|
506
516
|
<path
|
@@ -511,7 +521,7 @@ export default class IconProvider {
|
|
511
521
|
|
512
522
|
icon.innerHTML = `
|
513
523
|
<defs>
|
514
|
-
${
|
524
|
+
${checkerboardPattern.patternDef}
|
515
525
|
</defs>
|
516
526
|
<g>
|
517
527
|
${ink}
|
@@ -522,7 +532,13 @@ export default class IconProvider {
|
|
522
532
|
return icon;
|
523
533
|
}
|
524
534
|
|
525
|
-
public makeIconFromFactory(
|
535
|
+
public makeIconFromFactory(
|
536
|
+
pen: Pen,
|
537
|
+
factory: ComponentBuilderFactory,
|
538
|
+
|
539
|
+
// If true, attempts to guess the location of a transparency grid
|
540
|
+
includeTransparencyGrid: boolean = false
|
541
|
+
): IconType {
|
526
542
|
// Increase the thickness we use to generate the icon less with larger actual thicknesses.
|
527
543
|
// We want the icon to be recognisable with a large range of thicknesses.
|
528
544
|
const thickness = Math.sqrt(pen.getThickness()) * 3;
|
@@ -549,9 +565,60 @@ export default class IconProvider {
|
|
549
565
|
icon.setAttribute('viewBox', '0 0 100 100');
|
550
566
|
viewport.updateScreenSize(Vec2.of(100, 100));
|
551
567
|
|
552
|
-
|
568
|
+
let renderer;
|
569
|
+
|
570
|
+
if (includeTransparencyGrid) {
|
571
|
+
const checkerboardPattern = makeCheckerboardPattern();
|
572
|
+
|
573
|
+
const defs = document.createElementNS(svgNamespace, 'defs');
|
574
|
+
defs.innerHTML = checkerboardPattern.patternDef;
|
575
|
+
icon.appendChild(defs);
|
576
|
+
|
577
|
+
const background = document.createElementNS(svgNamespace, 'g');
|
578
|
+
icon.appendChild(background);
|
579
|
+
|
580
|
+
renderer = new class extends SVGRenderer {
|
581
|
+
public constructor() {
|
582
|
+
super(icon, viewport);
|
583
|
+
}
|
584
|
+
|
585
|
+
protected override addPathToSVG() {
|
586
|
+
const addedPath = super.addPathToSVG();
|
587
|
+
|
588
|
+
if (addedPath) {
|
589
|
+
// Add a copy of the path on the background
|
590
|
+
const copy = addedPath.cloneNode(true) as SVGPathElement;
|
591
|
+
copy.style.zIndex = '-1';
|
592
|
+
|
593
|
+
// Make the
|
594
|
+
if (copy.hasAttribute('fill')
|
595
|
+
&& copy.getAttribute('fill') !== 'transparent'
|
596
|
+
&& copy.getAttribute('fill') !== 'none') {
|
597
|
+
copy.setAttribute('fill', checkerboardPattern.patternRef);
|
598
|
+
}
|
599
|
+
|
600
|
+
if (copy.hasAttribute('stroke')) {
|
601
|
+
copy.setAttribute('stroke', checkerboardPattern.patternRef);
|
602
|
+
}
|
603
|
+
|
604
|
+
background.appendChild(copy);
|
605
|
+
}
|
606
|
+
|
607
|
+
return addedPath;
|
608
|
+
}
|
609
|
+
}();
|
610
|
+
} else {
|
611
|
+
renderer = new SVGRenderer(icon, viewport);
|
612
|
+
}
|
553
613
|
builder.preview(renderer);
|
554
614
|
|
615
|
+
// If only a single path was rendered, try to give it a checkerboard background to
|
616
|
+
// emphasize transparency. TODO: This is very fragile
|
617
|
+
|
618
|
+
|
619
|
+
const bbox = builder.getBBox();
|
620
|
+
icon.setAttribute('viewBox', `${bbox.x} ${bbox.y} ${bbox.w} ${bbox.h}`);
|
621
|
+
|
555
622
|
return icon;
|
556
623
|
}
|
557
624
|
|
@@ -583,8 +650,10 @@ export default class IconProvider {
|
|
583
650
|
pipette.style.fill = 'var(--icon-color)';
|
584
651
|
|
585
652
|
if (color) {
|
653
|
+
const checkerboardPattern = makeCheckerboardPattern();
|
654
|
+
|
586
655
|
const defs = document.createElementNS(svgNamespace, 'defs');
|
587
|
-
defs.innerHTML =
|
656
|
+
defs.innerHTML = checkerboardPattern.patternDef;
|
588
657
|
icon.appendChild(defs);
|
589
658
|
|
590
659
|
const fluidBackground = document.createElementNS(svgNamespace, 'path');
|
@@ -598,7 +667,7 @@ export default class IconProvider {
|
|
598
667
|
fluidBackground.setAttribute('d', fluidPathData);
|
599
668
|
|
600
669
|
fluid.style.fill = color.toHexString();
|
601
|
-
fluidBackground.style.fill =
|
670
|
+
fluidBackground.style.fill = checkerboardPattern.patternRef;
|
602
671
|
|
603
672
|
icon.appendChild(fluidBackground);
|
604
673
|
icon.appendChild(fluid);
|
@@ -15,8 +15,8 @@ export interface ToolbarLocalization {
|
|
15
15
|
chooseFile: string;
|
16
16
|
cancel: string;
|
17
17
|
submit: string;
|
18
|
-
|
19
|
-
|
18
|
+
roundedTipPen: string;
|
19
|
+
flatTipPen: string;
|
20
20
|
selectPenType: string;
|
21
21
|
colorLabel: string;
|
22
22
|
pen: string;
|
@@ -92,8 +92,8 @@ export const defaultToolbarLocalization: ToolbarLocalization = {
|
|
92
92
|
|
93
93
|
touchPanning: 'Touchscreen panning',
|
94
94
|
|
95
|
-
|
96
|
-
|
95
|
+
roundedTipPen: 'Rounded Tip',
|
96
|
+
flatTipPen: 'Flat Tip',
|
97
97
|
arrowPen: 'Arrow',
|
98
98
|
linePen: 'Line',
|
99
99
|
outlinedRectanglePen: 'Outlined rectangle',
|
package/src/toolbar/toolbar.css
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import Editor from '../../Editor';
|
2
2
|
import BaseTool from '../../tools/BaseTool';
|
3
|
-
import { EditorEventType } from '../../types';
|
3
|
+
import { EditorEventType, KeyPressEvent } from '../../types';
|
4
4
|
import { ToolbarLocalization } from '../localization';
|
5
5
|
import BaseWidget from './BaseWidget';
|
6
6
|
|
@@ -47,6 +47,15 @@ export default abstract class BaseToolWidget extends BaseWidget {
|
|
47
47
|
}
|
48
48
|
}
|
49
49
|
|
50
|
+
protected override onKeyPress(event: KeyPressEvent): boolean {
|
51
|
+
if (this.isSelected() && event.key === ' ' && this.hasDropdown) {
|
52
|
+
this.handleClick();
|
53
|
+
return true;
|
54
|
+
}
|
55
|
+
|
56
|
+
return false;
|
57
|
+
}
|
58
|
+
|
50
59
|
public override addTo(parent: HTMLElement) {
|
51
60
|
const result = super.addTo(parent);
|
52
61
|
this.setSelected(this.targetTool.isEnabled());
|
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
.toolbar-pen-type-selector > div {
|
3
|
+
display: flex;
|
4
|
+
flex-direction: row;
|
5
|
+
|
6
|
+
max-width: 350px;
|
7
|
+
flex-wrap: wrap;
|
8
|
+
|
9
|
+
--button-size: 56px;
|
10
|
+
}
|
11
|
+
|
12
|
+
.toolbar-pen-type-selector .pen-type-button {
|
13
|
+
display: flex;
|
14
|
+
flex-direction: column-reverse;
|
15
|
+
box-sizing: border-box;
|
16
|
+
|
17
|
+
flex-shrink: 1;
|
18
|
+
margin: 2px;
|
19
|
+
}
|
20
|
+
|
21
|
+
.toolbar-pen-type-selector .pen-type-button:focus-within {
|
22
|
+
outline: 2px solid var(--primary-foreground-color);
|
23
|
+
}
|
24
|
+
|
25
|
+
.toolbar-pen-type-selector .pen-type-button input {
|
26
|
+
opacity: 0;
|
27
|
+
height: 0;
|
28
|
+
}
|
29
|
+
|
30
|
+
.toolbar-pen-type-selector .pen-type-button label {
|
31
|
+
display: flex;
|
32
|
+
flex-direction: column;
|
33
|
+
|
34
|
+
width: var(--button-size);
|
35
|
+
height: var(--button-size);
|
36
|
+
|
37
|
+
font-size: 0.7rem;
|
38
|
+
align-items: center;
|
39
|
+
justify-content: center;
|
40
|
+
padding: 4px;
|
41
|
+
}
|
42
|
+
|
43
|
+
|
44
|
+
.toolbar-pen-type-selector .pen-type-button .icon {
|
45
|
+
flex-grow: 1;
|
46
|
+
flex-shrink: 1;
|
47
|
+
width: 100%;
|
48
|
+
}
|
49
|
+
|
50
|
+
.toolbar-pen-type-selector .pen-type-button.checked {
|
51
|
+
background-color: var(--secondary-background-color);
|
52
|
+
color: var(--secondary-foreground-color);
|
53
|
+
}
|