js-draw 1.9.0 → 1.10.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/dist/Editor.css +48 -1
- package/dist/bundle.js +2 -2
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/Editor.d.ts +41 -0
- package/dist/cjs/Editor.js +33 -13
- package/dist/cjs/Pointer.js +1 -1
- package/dist/cjs/Viewport.d.ts +6 -0
- package/dist/cjs/Viewport.js +6 -1
- package/dist/cjs/commands/Erase.d.ts +22 -2
- package/dist/cjs/commands/Erase.js +22 -2
- package/dist/cjs/commands/uniteCommands.d.ts +36 -0
- package/dist/cjs/commands/uniteCommands.js +36 -0
- package/dist/cjs/components/ImageComponent.d.ts +12 -0
- package/dist/cjs/components/ImageComponent.js +16 -9
- package/dist/cjs/components/Stroke.d.ts +16 -2
- package/dist/cjs/components/Stroke.js +17 -1
- package/dist/cjs/components/builders/ArrowBuilder.js +3 -3
- package/dist/cjs/components/builders/CircleBuilder.js +3 -3
- package/dist/cjs/components/builders/FreehandLineBuilder.js +3 -3
- package/dist/cjs/components/builders/LineBuilder.js +3 -3
- package/dist/cjs/components/builders/PressureSensitiveFreehandLineBuilder.js +3 -3
- package/dist/cjs/components/builders/RectangleBuilder.js +5 -6
- package/dist/cjs/components/builders/autocorrect/makeShapeFitAutocorrect.d.ts +3 -0
- package/dist/cjs/components/builders/autocorrect/makeShapeFitAutocorrect.js +168 -0
- package/dist/cjs/components/builders/autocorrect/makeSnapToGridAutocorrect.d.ts +3 -0
- package/dist/cjs/components/builders/autocorrect/makeSnapToGridAutocorrect.js +46 -0
- package/dist/cjs/components/builders/types.d.ts +1 -0
- package/dist/cjs/image/EditorImage.d.ts +32 -1
- package/dist/cjs/image/EditorImage.js +32 -1
- package/dist/cjs/rendering/Display.js +8 -1
- package/dist/cjs/rendering/RenderablePathSpec.d.ts +5 -1
- package/dist/cjs/rendering/RenderablePathSpec.js +4 -0
- package/dist/cjs/toolbar/IconProvider.d.ts +2 -0
- package/dist/cjs/toolbar/IconProvider.js +17 -0
- package/dist/cjs/toolbar/localization.d.ts +3 -0
- package/dist/cjs/toolbar/localization.js +4 -1
- package/dist/cjs/toolbar/widgets/InsertImageWidget.d.ts +2 -1
- package/dist/cjs/toolbar/widgets/InsertImageWidget.js +102 -22
- package/dist/cjs/toolbar/widgets/PenToolWidget.d.ts +1 -2
- package/dist/cjs/toolbar/widgets/PenToolWidget.js +50 -20
- package/dist/cjs/tools/Pen.d.ts +9 -0
- package/dist/cjs/tools/Pen.js +77 -3
- package/dist/cjs/tools/TextTool.js +5 -1
- package/dist/cjs/tools/util/StationaryPenDetector.d.ts +22 -0
- package/dist/cjs/tools/util/StationaryPenDetector.js +95 -0
- package/dist/cjs/util/ReactiveValue.d.ts +2 -0
- package/dist/cjs/util/ReactiveValue.js +2 -0
- package/dist/cjs/util/lib.d.ts +1 -0
- package/dist/cjs/util/lib.js +4 -1
- package/dist/cjs/util/waitForImageLoaded.d.ts +2 -0
- package/dist/cjs/util/waitForImageLoaded.js +12 -0
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/Editor.d.ts +41 -0
- package/dist/mjs/Editor.mjs +33 -13
- package/dist/mjs/Pointer.mjs +1 -1
- package/dist/mjs/Viewport.d.ts +6 -0
- package/dist/mjs/Viewport.mjs +6 -1
- package/dist/mjs/commands/Erase.d.ts +22 -2
- package/dist/mjs/commands/Erase.mjs +22 -2
- package/dist/mjs/commands/uniteCommands.d.ts +36 -0
- package/dist/mjs/commands/uniteCommands.mjs +36 -0
- package/dist/mjs/components/ImageComponent.d.ts +12 -0
- package/dist/mjs/components/ImageComponent.mjs +16 -9
- package/dist/mjs/components/Stroke.d.ts +16 -2
- package/dist/mjs/components/Stroke.mjs +17 -1
- package/dist/mjs/components/builders/ArrowBuilder.mjs +3 -2
- package/dist/mjs/components/builders/CircleBuilder.mjs +3 -2
- package/dist/mjs/components/builders/FreehandLineBuilder.mjs +3 -2
- package/dist/mjs/components/builders/LineBuilder.mjs +3 -2
- package/dist/mjs/components/builders/PressureSensitiveFreehandLineBuilder.mjs +3 -2
- package/dist/mjs/components/builders/RectangleBuilder.mjs +5 -4
- package/dist/mjs/components/builders/autocorrect/makeShapeFitAutocorrect.d.ts +3 -0
- package/dist/mjs/components/builders/autocorrect/makeShapeFitAutocorrect.mjs +166 -0
- package/dist/mjs/components/builders/autocorrect/makeSnapToGridAutocorrect.d.ts +3 -0
- package/dist/mjs/components/builders/autocorrect/makeSnapToGridAutocorrect.mjs +44 -0
- package/dist/mjs/components/builders/types.d.ts +1 -0
- package/dist/mjs/image/EditorImage.d.ts +32 -1
- package/dist/mjs/image/EditorImage.mjs +32 -1
- package/dist/mjs/rendering/Display.mjs +8 -1
- package/dist/mjs/rendering/RenderablePathSpec.d.ts +5 -1
- package/dist/mjs/rendering/RenderablePathSpec.mjs +4 -0
- package/dist/mjs/toolbar/IconProvider.d.ts +2 -0
- package/dist/mjs/toolbar/IconProvider.mjs +17 -0
- package/dist/mjs/toolbar/localization.d.ts +3 -0
- package/dist/mjs/toolbar/localization.mjs +4 -1
- package/dist/mjs/toolbar/widgets/InsertImageWidget.d.ts +2 -1
- package/dist/mjs/toolbar/widgets/InsertImageWidget.mjs +102 -22
- package/dist/mjs/toolbar/widgets/PenToolWidget.d.ts +1 -2
- package/dist/mjs/toolbar/widgets/PenToolWidget.mjs +50 -20
- package/dist/mjs/tools/Pen.d.ts +9 -0
- package/dist/mjs/tools/Pen.mjs +77 -3
- package/dist/mjs/tools/TextTool.mjs +5 -1
- package/dist/mjs/tools/util/StationaryPenDetector.d.ts +22 -0
- package/dist/mjs/tools/util/StationaryPenDetector.mjs +92 -0
- package/dist/mjs/util/ReactiveValue.d.ts +2 -0
- package/dist/mjs/util/ReactiveValue.mjs +2 -0
- package/dist/mjs/util/lib.d.ts +1 -0
- package/dist/mjs/util/lib.mjs +1 -0
- package/dist/mjs/util/waitForImageLoaded.d.ts +2 -0
- package/dist/mjs/util/waitForImageLoaded.mjs +10 -0
- package/dist/mjs/version.mjs +1 -1
- package/package.json +3 -3
- package/src/Editor.scss +7 -0
- package/src/toolbar/AbstractToolbar.scss +20 -0
- package/src/toolbar/toolbar.scss +1 -1
- package/src/toolbar/widgets/InsertImageWidget.scss +6 -1
- package/src/toolbar/widgets/PenToolWidget.scss +33 -0
- package/src/tools/SelectionTool/SelectionTool.scss +6 -0
- package/src/toolbar/widgets/PenToolWidget.css +0 -2
@@ -24,8 +24,7 @@ export default class PenToolWidget extends BaseToolWidget {
|
|
24
24
|
private createIconForRecord;
|
25
25
|
protected createIcon(): Element;
|
26
26
|
private createPenTypeSelector;
|
27
|
-
|
28
|
-
protected createStabilizationOption(): {
|
27
|
+
protected createStrokeCorrectionOptions(): {
|
29
28
|
update: () => void;
|
30
29
|
addTo: (parent: HTMLElement) => void;
|
31
30
|
};
|
@@ -147,27 +147,51 @@ class PenToolWidget extends BaseToolWidget {
|
|
147
147
|
},
|
148
148
|
};
|
149
149
|
}
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
150
|
+
createStrokeCorrectionOptions() {
|
151
|
+
const container = document.createElement('div');
|
152
|
+
container.classList.add('action-button-row', `${toolbarCSSPrefix}-pen-tool-toggle-buttons`);
|
153
|
+
const addToggleButton = (labelText, icon) => {
|
154
|
+
const button = document.createElement('button');
|
155
|
+
button.classList.add(`${toolbarCSSPrefix}-toggle-button`);
|
156
|
+
const iconElement = icon.cloneNode(true);
|
157
|
+
iconElement.classList.add('icon');
|
158
|
+
const label = document.createElement('span');
|
159
|
+
label.innerText = labelText;
|
160
|
+
button.replaceChildren(iconElement, label);
|
161
|
+
button.setAttribute('role', 'switch');
|
162
|
+
container.appendChild(button);
|
163
|
+
let checked = false;
|
164
|
+
let onChangeListener = (_checked) => { };
|
165
|
+
const result = {
|
166
|
+
setChecked(newChecked) {
|
167
|
+
checked = newChecked;
|
168
|
+
button.setAttribute('aria-checked', `${checked}`);
|
169
|
+
onChangeListener(checked);
|
170
|
+
},
|
171
|
+
setOnInputListener(listener) {
|
172
|
+
onChangeListener = listener;
|
173
|
+
},
|
174
|
+
};
|
175
|
+
button.onclick = () => {
|
176
|
+
result.setChecked(!checked);
|
177
|
+
};
|
178
|
+
return result;
|
164
179
|
};
|
180
|
+
const stabilizationOption = addToggleButton(this.localizationTable.inputStabilization, this.editor.icons.makeStrokeSmoothingIcon());
|
181
|
+
stabilizationOption.setOnInputListener(enabled => {
|
182
|
+
this.tool.setHasStabilization(enabled);
|
183
|
+
});
|
184
|
+
const autocorrectOption = addToggleButton(this.localizationTable.strokeAutocorrect, this.editor.icons.makeShapeAutocorrectIcon());
|
185
|
+
autocorrectOption.setOnInputListener(enabled => {
|
186
|
+
this.tool.setStrokeAutocorrectEnabled(enabled);
|
187
|
+
});
|
165
188
|
return {
|
166
189
|
update: () => {
|
167
|
-
|
190
|
+
stabilizationOption.setChecked(!!this.tool.getInputMapper());
|
191
|
+
autocorrectOption.setChecked(this.tool.getStrokeAutocorrectionEnabled());
|
168
192
|
},
|
169
193
|
addTo: (parent) => {
|
170
|
-
parent.appendChild(
|
194
|
+
parent.appendChild(container);
|
171
195
|
}
|
172
196
|
};
|
173
197
|
}
|
@@ -189,20 +213,22 @@ class PenToolWidget extends BaseToolWidget {
|
|
189
213
|
colorLabel.setAttribute('for', colorInput.id);
|
190
214
|
colorRow.appendChild(colorLabel);
|
191
215
|
colorRow.appendChild(colorInputContainer);
|
192
|
-
const
|
216
|
+
const toggleButtonRow = this.createStrokeCorrectionOptions();
|
193
217
|
this.updateInputs = () => {
|
194
218
|
setColorInputValue(this.tool.getColor());
|
195
219
|
setThickness(this.tool.getThickness());
|
196
220
|
penTypeSelect.updateIcons();
|
197
221
|
// Update the selected stroke factory.
|
198
222
|
penTypeSelect.setValue(this.getCurrentPenTypeIdx());
|
199
|
-
|
223
|
+
toggleButtonRow.update();
|
200
224
|
};
|
201
225
|
this.updateInputs();
|
202
226
|
container.replaceChildren(colorRow, thicknessRow);
|
203
227
|
penTypeSelect.addTo(container);
|
204
|
-
stabilizationOption.addTo(container);
|
205
228
|
dropdown.replaceChildren(container);
|
229
|
+
// Add the toggle button row *outside* of the main content (use different
|
230
|
+
// spacing with respect to the sides of the container).
|
231
|
+
toggleButtonRow.addTo(dropdown);
|
206
232
|
return true;
|
207
233
|
}
|
208
234
|
onKeyPress(event) {
|
@@ -232,6 +258,7 @@ class PenToolWidget extends BaseToolWidget {
|
|
232
258
|
thickness: this.tool.getThickness(),
|
233
259
|
strokeFactoryId: this.getCurrentPenType()?.id,
|
234
260
|
inputStabilization: !!this.tool.getInputMapper(),
|
261
|
+
strokeAutocorrect: this.tool.getStrokeAutocorrectionEnabled(),
|
235
262
|
};
|
236
263
|
}
|
237
264
|
deserializeFrom(state) {
|
@@ -262,7 +289,10 @@ class PenToolWidget extends BaseToolWidget {
|
|
262
289
|
}
|
263
290
|
}
|
264
291
|
if (state.inputStabilization !== undefined) {
|
265
|
-
this.
|
292
|
+
this.tool.setHasStabilization(!!state.inputStabilization);
|
293
|
+
}
|
294
|
+
if (state.strokeAutocorrect !== undefined) {
|
295
|
+
this.tool.setStrokeAutocorrectEnabled(!!state.strokeAutocorrect);
|
266
296
|
}
|
267
297
|
}
|
268
298
|
}
|
package/dist/mjs/tools/Pen.d.ts
CHANGED
@@ -19,6 +19,11 @@ export default class Pen extends BaseTool {
|
|
19
19
|
private currentDeviceType;
|
20
20
|
private styleValue;
|
21
21
|
private style;
|
22
|
+
private shapeAutocompletionEnabled;
|
23
|
+
private autocorrectedShape;
|
24
|
+
private lastAutocorrectedShape;
|
25
|
+
private removedAutocorrectedShapeTime;
|
26
|
+
private stationaryDetector;
|
22
27
|
constructor(editor: Editor, description: string, style: Partial<PenStyle>);
|
23
28
|
private getPressureMultiplier;
|
24
29
|
protected toStrokePoint(pointer: Pointer): StrokeDataPoint;
|
@@ -30,12 +35,16 @@ export default class Pen extends BaseTool {
|
|
30
35
|
onPointerMove({ current }: PointerEvt): void;
|
31
36
|
onPointerUp({ current }: PointerEvt): boolean;
|
32
37
|
onGestureCancel(): void;
|
38
|
+
private removedAutocorrectedShapeRecently;
|
39
|
+
private autocorrectShape;
|
33
40
|
private finalizeStroke;
|
34
41
|
private noteUpdated;
|
35
42
|
setColor(color: Color4): void;
|
36
43
|
setThickness(thickness: number): void;
|
37
44
|
setStrokeFactory(factory: ComponentBuilderFactory): void;
|
38
45
|
setHasStabilization(hasStabilization: boolean): void;
|
46
|
+
setStrokeAutocorrectEnabled(enabled: boolean): void;
|
47
|
+
getStrokeAutocorrectionEnabled(): boolean;
|
39
48
|
getThickness(): number;
|
40
49
|
getColor(): Color4;
|
41
50
|
getStrokeFactory(): ComponentBuilderFactory;
|
package/dist/mjs/tools/Pen.mjs
CHANGED
@@ -8,6 +8,7 @@ import { undoKeyboardShortcutId } from './keybindings.mjs';
|
|
8
8
|
import { decreaseSizeKeyboardShortcutId, increaseSizeKeyboardShortcutId } from './keybindings.mjs';
|
9
9
|
import InputStabilizer from './InputFilter/InputStabilizer.mjs';
|
10
10
|
import { ReactiveValue } from '../util/ReactiveValue.mjs';
|
11
|
+
import StationaryPenDetector from './util/StationaryPenDetector.mjs';
|
11
12
|
export default class Pen extends BaseTool {
|
12
13
|
constructor(editor, description, style) {
|
13
14
|
super(editor.notifier, description);
|
@@ -16,6 +17,11 @@ export default class Pen extends BaseTool {
|
|
16
17
|
this.lastPoint = null;
|
17
18
|
this.startPoint = null;
|
18
19
|
this.currentDeviceType = null;
|
20
|
+
this.shapeAutocompletionEnabled = false;
|
21
|
+
this.autocorrectedShape = null;
|
22
|
+
this.lastAutocorrectedShape = null;
|
23
|
+
this.removedAutocorrectedShapeTime = 0;
|
24
|
+
this.stationaryDetector = null;
|
19
25
|
this.styleValue = ReactiveValue.fromInitialValue({
|
20
26
|
factory: makeFreehandLineBuilder,
|
21
27
|
color: Color4.blue,
|
@@ -53,7 +59,14 @@ export default class Pen extends BaseTool {
|
|
53
59
|
// Displays the stroke that is currently being built with the display's `wetInkRenderer`.
|
54
60
|
previewStroke() {
|
55
61
|
this.editor.clearWetInk();
|
56
|
-
this.
|
62
|
+
const wetInkRenderer = this.editor.display.getWetInkRenderer();
|
63
|
+
if (this.autocorrectedShape) {
|
64
|
+
const visibleRect = this.editor.viewport.visibleRect;
|
65
|
+
this.autocorrectedShape.render(wetInkRenderer, visibleRect);
|
66
|
+
}
|
67
|
+
else {
|
68
|
+
this.builder?.preview(wetInkRenderer);
|
69
|
+
}
|
57
70
|
}
|
58
71
|
// Throws if no stroke builder exists.
|
59
72
|
addPointToStroke(point) {
|
@@ -82,6 +95,19 @@ export default class Pen extends BaseTool {
|
|
82
95
|
this.startPoint = this.toStrokePoint(current);
|
83
96
|
this.builder = this.style.factory(this.startPoint, this.editor.viewport);
|
84
97
|
this.currentDeviceType = current.device;
|
98
|
+
if (this.shapeAutocompletionEnabled) {
|
99
|
+
const stationaryDetectionConfig = {
|
100
|
+
maxSpeed: 5,
|
101
|
+
maxRadius: 10,
|
102
|
+
minTimeSeconds: 0.5, // s
|
103
|
+
};
|
104
|
+
this.stationaryDetector = new StationaryPenDetector(current, stationaryDetectionConfig, pointer => this.autocorrectShape(pointer));
|
105
|
+
}
|
106
|
+
else {
|
107
|
+
this.stationaryDetector = null;
|
108
|
+
}
|
109
|
+
this.lastAutocorrectedShape = null;
|
110
|
+
this.removedAutocorrectedShapeTime = 0;
|
85
111
|
return true;
|
86
112
|
}
|
87
113
|
return false;
|
@@ -109,7 +135,14 @@ export default class Pen extends BaseTool {
|
|
109
135
|
return;
|
110
136
|
if (current.device !== this.currentDeviceType)
|
111
137
|
return;
|
112
|
-
this.
|
138
|
+
const isStationary = this.stationaryDetector?.onPointerMove(current);
|
139
|
+
if (!isStationary) {
|
140
|
+
this.addPointToStroke(this.toStrokePoint(current));
|
141
|
+
if (this.autocorrectedShape) {
|
142
|
+
this.removedAutocorrectedShapeTime = performance.now();
|
143
|
+
this.autocorrectedShape = null;
|
144
|
+
}
|
145
|
+
}
|
113
146
|
}
|
114
147
|
onPointerUp({ current }) {
|
115
148
|
if (!this.builder)
|
@@ -119,6 +152,7 @@ export default class Pen extends BaseTool {
|
|
119
152
|
// device type.
|
120
153
|
return true;
|
121
154
|
}
|
155
|
+
this.stationaryDetector?.onPointerUp(current);
|
122
156
|
// onPointerUp events can have zero pressure. Use the last pressure instead.
|
123
157
|
const currentPoint = this.toStrokePoint(current);
|
124
158
|
const strokePoint = {
|
@@ -134,10 +168,37 @@ export default class Pen extends BaseTool {
|
|
134
168
|
onGestureCancel() {
|
135
169
|
this.builder = null;
|
136
170
|
this.editor.clearWetInk();
|
171
|
+
this.stationaryDetector?.destroy();
|
172
|
+
this.stationaryDetector = null;
|
173
|
+
}
|
174
|
+
removedAutocorrectedShapeRecently() {
|
175
|
+
return this.removedAutocorrectedShapeTime > performance.now() - 320;
|
176
|
+
}
|
177
|
+
async autocorrectShape(_lastPointer) {
|
178
|
+
if (!this.builder || !this.builder.autocorrectShape)
|
179
|
+
return;
|
180
|
+
if (!this.shapeAutocompletionEnabled)
|
181
|
+
return;
|
182
|
+
// If already corrected, do nothing
|
183
|
+
if (this.autocorrectedShape)
|
184
|
+
return;
|
185
|
+
// Activate stroke fitting
|
186
|
+
const correctedShape = await this.builder.autocorrectShape();
|
187
|
+
if (!this.builder || !correctedShape) {
|
188
|
+
return;
|
189
|
+
}
|
190
|
+
this.autocorrectedShape = correctedShape;
|
191
|
+
this.lastAutocorrectedShape = correctedShape;
|
192
|
+
this.previewStroke();
|
137
193
|
}
|
138
194
|
finalizeStroke() {
|
139
195
|
if (this.builder) {
|
140
|
-
|
196
|
+
// If autocorrectedShape was cleared recently enough, it was
|
197
|
+
// probably by mistake. Reset it.
|
198
|
+
if (this.lastAutocorrectedShape && this.removedAutocorrectedShapeRecently()) {
|
199
|
+
this.autocorrectedShape = this.lastAutocorrectedShape;
|
200
|
+
}
|
201
|
+
const stroke = this.autocorrectedShape ?? this.builder.build();
|
141
202
|
this.previewStroke();
|
142
203
|
if (stroke.getBBox().area > 0) {
|
143
204
|
const canFlatten = true;
|
@@ -150,7 +211,11 @@ export default class Pen extends BaseTool {
|
|
150
211
|
}
|
151
212
|
this.builder = null;
|
152
213
|
this.lastPoint = null;
|
214
|
+
this.autocorrectedShape = null;
|
215
|
+
this.lastAutocorrectedShape = null;
|
153
216
|
this.editor.clearWetInk();
|
217
|
+
this.stationaryDetector?.destroy();
|
218
|
+
this.stationaryDetector = null;
|
154
219
|
}
|
155
220
|
noteUpdated() {
|
156
221
|
this.editor.notifier.dispatch(EditorEventType.ToolUpdated, {
|
@@ -196,6 +261,15 @@ export default class Pen extends BaseTool {
|
|
196
261
|
}
|
197
262
|
this.noteUpdated();
|
198
263
|
}
|
264
|
+
setStrokeAutocorrectEnabled(enabled) {
|
265
|
+
if (enabled !== this.shapeAutocompletionEnabled) {
|
266
|
+
this.shapeAutocompletionEnabled = enabled;
|
267
|
+
this.noteUpdated();
|
268
|
+
}
|
269
|
+
}
|
270
|
+
getStrokeAutocorrectionEnabled() {
|
271
|
+
return this.shapeAutocompletionEnabled;
|
272
|
+
}
|
199
273
|
getThickness() { return this.style.thickness; }
|
200
274
|
getColor() { return this.style.color; }
|
201
275
|
getStrokeFactory() { return this.style.factory; }
|
@@ -39,6 +39,10 @@ export default class TextTool extends BaseTool {
|
|
39
39
|
.${overlayCSSClass} {
|
40
40
|
height: 0;
|
41
41
|
overflow: visible;
|
42
|
+
|
43
|
+
/* Allows absolutely-positioned textareas to scroll with
|
44
|
+
the containing overlay. */
|
45
|
+
position: relative;
|
42
46
|
}
|
43
47
|
|
44
48
|
.${overlayCSSClass} textarea {
|
@@ -121,7 +125,7 @@ export default class TextTool extends BaseTool {
|
|
121
125
|
this.textInputElem.style.fontWeight = this.textStyle.fontWeight ?? '';
|
122
126
|
this.textInputElem.style.fontSize = `${this.textStyle.size}px`;
|
123
127
|
this.textInputElem.style.color = this.textStyle.renderingStyle.fill.toHexString();
|
124
|
-
this.textInputElem.style.position = '
|
128
|
+
this.textInputElem.style.position = 'absolute';
|
125
129
|
this.textInputElem.style.left = `${textScreenPos.x}px`;
|
126
130
|
this.textInputElem.style.top = `${textScreenPos.y}px`;
|
127
131
|
this.textInputElem.style.margin = '0';
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import Pointer from '../../Pointer';
|
2
|
+
interface Config {
|
3
|
+
maxSpeed: number;
|
4
|
+
minTimeSeconds: number;
|
5
|
+
maxRadius: number;
|
6
|
+
}
|
7
|
+
type OnStationaryCallback = (lastPointer: Pointer) => void;
|
8
|
+
export default class StationaryPenDetector {
|
9
|
+
private config;
|
10
|
+
private onStationary;
|
11
|
+
private stationaryStartPointer;
|
12
|
+
private lastPointer;
|
13
|
+
private averageVelocity;
|
14
|
+
private timeout;
|
15
|
+
constructor(startPointer: Pointer, config: Config, onStationary: OnStationaryCallback);
|
16
|
+
onPointerMove(currentPointer: Pointer): boolean | undefined;
|
17
|
+
onPointerUp(pointer: Pointer): void;
|
18
|
+
destroy(): void;
|
19
|
+
private cancelStationaryTimeout;
|
20
|
+
private setStationaryTimeout;
|
21
|
+
}
|
22
|
+
export {};
|
@@ -0,0 +1,92 @@
|
|
1
|
+
import { Vec2 } from '@js-draw/math';
|
2
|
+
export default class StationaryPenDetector {
|
3
|
+
// Only handles one pen. As such, `startPointer` should be the same device/finger
|
4
|
+
// as `updatedPointer` in `onPointerMove`.
|
5
|
+
//
|
6
|
+
// A new `StationaryPenDetector` should be created for each gesture.
|
7
|
+
constructor(startPointer, config, onStationary) {
|
8
|
+
this.config = config;
|
9
|
+
this.onStationary = onStationary;
|
10
|
+
this.timeout = null;
|
11
|
+
this.stationaryStartPointer = startPointer;
|
12
|
+
this.lastPointer = startPointer;
|
13
|
+
this.averageVelocity = Vec2.zero;
|
14
|
+
}
|
15
|
+
// Returns true if stationary
|
16
|
+
onPointerMove(currentPointer) {
|
17
|
+
if (!this.stationaryStartPointer) {
|
18
|
+
// Destoroyed
|
19
|
+
return;
|
20
|
+
}
|
21
|
+
if (currentPointer.id !== this.stationaryStartPointer.id) {
|
22
|
+
return false;
|
23
|
+
}
|
24
|
+
// dx: "Δx" Displacement from last.
|
25
|
+
const dxFromLast = currentPointer.screenPos.minus(this.lastPointer.screenPos);
|
26
|
+
const dxFromStationaryStart = currentPointer.screenPos.minus(this.stationaryStartPointer.screenPos);
|
27
|
+
// dt: Delta time:
|
28
|
+
// /1000: Convert to s.
|
29
|
+
let dtFromLast = (currentPointer.timeStamp - this.lastPointer.timeStamp) / 1000; // s
|
30
|
+
// Don't divide by zero
|
31
|
+
if (dtFromLast === 0) {
|
32
|
+
dtFromLast = 1;
|
33
|
+
}
|
34
|
+
const currentVelocity = dxFromLast.times(1 / dtFromLast); // px/s
|
35
|
+
// Slight smoothing of the velocity to prevent input jitter from affecting the
|
36
|
+
// velocity too significantly.
|
37
|
+
this.averageVelocity = this.averageVelocity.lerp(currentVelocity, 0.5); // px/s
|
38
|
+
const dtFromStart = currentPointer.timeStamp - this.stationaryStartPointer.timeStamp; // ms
|
39
|
+
// If not stationary
|
40
|
+
if (dxFromStationaryStart.length() > this.config.maxRadius
|
41
|
+
|| this.averageVelocity.length() > this.config.maxSpeed
|
42
|
+
|| dtFromStart < this.config.minTimeSeconds) {
|
43
|
+
this.stationaryStartPointer = currentPointer;
|
44
|
+
this.lastPointer = currentPointer;
|
45
|
+
this.setStationaryTimeout(this.config.minTimeSeconds * 1000);
|
46
|
+
return false;
|
47
|
+
}
|
48
|
+
const stationaryTimeoutMs = this.config.minTimeSeconds * 1000 - dtFromStart;
|
49
|
+
this.lastPointer = currentPointer;
|
50
|
+
return stationaryTimeoutMs <= 0;
|
51
|
+
}
|
52
|
+
onPointerUp(pointer) {
|
53
|
+
if (pointer.id !== this.stationaryStartPointer?.id) {
|
54
|
+
this.cancelStationaryTimeout();
|
55
|
+
}
|
56
|
+
}
|
57
|
+
destroy() {
|
58
|
+
this.cancelStationaryTimeout();
|
59
|
+
this.stationaryStartPointer = null;
|
60
|
+
}
|
61
|
+
cancelStationaryTimeout() {
|
62
|
+
if (this.timeout !== null) {
|
63
|
+
clearTimeout(this.timeout);
|
64
|
+
this.timeout = null;
|
65
|
+
}
|
66
|
+
}
|
67
|
+
setStationaryTimeout(timeoutMs) {
|
68
|
+
if (this.timeout !== null) {
|
69
|
+
return;
|
70
|
+
}
|
71
|
+
if (timeoutMs <= 0) {
|
72
|
+
this.onStationary(this.lastPointer);
|
73
|
+
}
|
74
|
+
else {
|
75
|
+
this.timeout = setTimeout(() => {
|
76
|
+
this.timeout = null;
|
77
|
+
if (!this.stationaryStartPointer) {
|
78
|
+
// Destroyed
|
79
|
+
return;
|
80
|
+
}
|
81
|
+
const timeSinceStationaryStart = performance.now() - this.stationaryStartPointer.timeStamp;
|
82
|
+
const timeRemaining = this.config.minTimeSeconds * 1000 - timeSinceStationaryStart;
|
83
|
+
if (timeRemaining <= 0) {
|
84
|
+
this.onStationary(this.lastPointer);
|
85
|
+
}
|
86
|
+
else {
|
87
|
+
this.setStationaryTimeout(timeRemaining);
|
88
|
+
}
|
89
|
+
}, timeoutMs);
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
@@ -13,6 +13,8 @@ type UpdateCallback<T> = (value: T) => void;
|
|
13
13
|
*
|
14
14
|
* Static methods in the `ReactiveValue` and `MutableReactiveValue` classes are
|
15
15
|
* constructors (e.g. `fromImmutable`).
|
16
|
+
*
|
17
|
+
* Avoid extending this class from an external library, as that may not be stable.
|
16
18
|
*/
|
17
19
|
export declare abstract class ReactiveValue<T> {
|
18
20
|
/**
|
@@ -31,6 +31,8 @@ const noOpSetUpdateListener = () => {
|
|
31
31
|
*
|
32
32
|
* Static methods in the `ReactiveValue` and `MutableReactiveValue` classes are
|
33
33
|
* constructors (e.g. `fromImmutable`).
|
34
|
+
*
|
35
|
+
* Avoid extending this class from an external library, as that may not be stable.
|
34
36
|
*/
|
35
37
|
export class ReactiveValue {
|
36
38
|
/** Creates a `ReactiveValue` with an initial value, `initialValue`. */
|
package/dist/mjs/util/lib.d.ts
CHANGED
package/dist/mjs/util/lib.mjs
CHANGED
@@ -0,0 +1,10 @@
|
|
1
|
+
const waitForImageLoad = async (image) => {
|
2
|
+
if (!image.complete) {
|
3
|
+
await new Promise((resolve, reject) => {
|
4
|
+
image.onload = event => resolve(event);
|
5
|
+
image.onerror = event => reject(event);
|
6
|
+
image.onabort = event => reject(event);
|
7
|
+
});
|
8
|
+
}
|
9
|
+
};
|
10
|
+
export default waitForImageLoad;
|
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.10.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/lib.d.ts",
|
6
6
|
"main": "./dist/cjs/lib.js",
|
@@ -64,7 +64,7 @@
|
|
64
64
|
"postpack": "ts-node tools/copyREADME.ts revert"
|
65
65
|
},
|
66
66
|
"dependencies": {
|
67
|
-
"@js-draw/math": "^1.
|
67
|
+
"@js-draw/math": "^1.10.0",
|
68
68
|
"@melloware/coloris": "0.21.0"
|
69
69
|
},
|
70
70
|
"devDependencies": {
|
@@ -86,5 +86,5 @@
|
|
86
86
|
"freehand",
|
87
87
|
"svg"
|
88
88
|
],
|
89
|
-
"gitHead": "
|
89
|
+
"gitHead": "ccf1d0634e902c731fcd794df11cd001c3a30585"
|
90
90
|
}
|
package/src/Editor.scss
CHANGED
@@ -74,6 +74,13 @@
|
|
74
74
|
min-height: 220px;
|
75
75
|
min-width: 100px;
|
76
76
|
|
77
|
+
// A vertical writing mode breaks assumptions about how toolbars/overlays
|
78
|
+
// are aligned, resulting in unusable toolbars/text.
|
79
|
+
//
|
80
|
+
// TODO: Fix this.
|
81
|
+
//
|
82
|
+
writing-mode: horizontal-tb;
|
83
|
+
|
77
84
|
box-sizing: border-box;
|
78
85
|
|
79
86
|
display: flex;
|
@@ -217,6 +217,26 @@
|
|
217
217
|
justify-content: center;
|
218
218
|
}
|
219
219
|
|
220
|
+
.toolbar-element .toolbar--toggle-button {
|
221
|
+
color: var(--foreground-color-1);
|
222
|
+
font-weight: normal;
|
223
|
+
|
224
|
+
&[aria-checked=true] {
|
225
|
+
background: var(--selection-background-color);
|
226
|
+
color: var(--selection-foreground-color);
|
227
|
+
}
|
228
|
+
|
229
|
+
> .icon {
|
230
|
+
width: 25px;
|
231
|
+
height: 25px;
|
232
|
+
margin: 0 5px;
|
233
|
+
}
|
234
|
+
|
235
|
+
> * {
|
236
|
+
vertical-align: middle;
|
237
|
+
}
|
238
|
+
}
|
239
|
+
|
220
240
|
.toolbar-closeColorPickerOverlay {
|
221
241
|
display: none;
|
222
242
|
position: fixed;
|
package/src/toolbar/toolbar.scss
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
@use "./widgets/InsertImageWidget.scss";
|
2
2
|
@use "./widgets/OverflowWidget.css";
|
3
|
-
@use "./widgets/PenToolWidget.
|
3
|
+
@use "./widgets/PenToolWidget.scss";
|
4
4
|
@use "./widgets/HandToolWidget.scss";
|
5
5
|
@use "./widgets/SelectionToolWidget.scss";
|
6
6
|
@use "./widgets/DocumentPropertiesWidget.scss";
|
@@ -12,7 +12,7 @@ $image-widget-selector: '.insert-image-widget-dropdown-content';
|
|
12
12
|
|
13
13
|
img {
|
14
14
|
max-width: min(50vw, 75%);
|
15
|
-
max-height: 50vh;
|
15
|
+
max-height: min(300px, 50vh);
|
16
16
|
|
17
17
|
/* Center */
|
18
18
|
display: block;
|
@@ -20,6 +20,11 @@ $image-widget-selector: '.insert-image-widget-dropdown-content';
|
|
20
20
|
margin-right: auto;
|
21
21
|
}
|
22
22
|
|
23
|
+
.insert-image-image-status-view {
|
24
|
+
display: flex;
|
25
|
+
justify-content: space-between;
|
26
|
+
}
|
27
|
+
|
23
28
|
.action-button-row {
|
24
29
|
margin-top: 4px;
|
25
30
|
display: flex;
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
// Repeat selector for extra specificity
|
4
|
+
:root .toolbar--pen-tool-toggle-buttons.toolbar--pen-tool-toggle-buttons {
|
5
|
+
display: flex;
|
6
|
+
justify-content: stretch;
|
7
|
+
padding-top: 0;
|
8
|
+
padding-bottom: 5px;
|
9
|
+
|
10
|
+
// Some styles rely on left being start.
|
11
|
+
direction: ltr;
|
12
|
+
|
13
|
+
& > * {
|
14
|
+
flex-grow: 1;
|
15
|
+
text-align: start;
|
16
|
+
margin-inline-end: 5px;
|
17
|
+
|
18
|
+
.icon {
|
19
|
+
margin: 0;
|
20
|
+
margin-inline-start: 4px;
|
21
|
+
margin-inline-end: 10px;
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
& > :nth-child(1) {
|
26
|
+
direction: ltr;
|
27
|
+
}
|
28
|
+
|
29
|
+
& > :last-child {
|
30
|
+
// Reverse the last (show icon on the right)
|
31
|
+
direction: rtl;
|
32
|
+
}
|
33
|
+
}
|
@@ -83,6 +83,12 @@
|
|
83
83
|
.overlay.handleOverlay {
|
84
84
|
touch-action: none;
|
85
85
|
|
86
|
+
// The selection tool makes some assumptions about margin-left, margin-top,
|
87
|
+
// and the initial (unmodified) position of the selection.
|
88
|
+
//
|
89
|
+
// For now, as we're not showing text in the selection box, force LTR.
|
90
|
+
direction: ltr;
|
91
|
+
|
86
92
|
// When expanding a selection with shift+click&drag, multiple selection boxes
|
87
93
|
// can be present in the same handleOverlay. As such, so that other overlayed
|
88
94
|
// selection boxes are in the correct place, the outer container needs to have
|