js-draw 1.19.1 → 1.20.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/README.md +52 -1
- package/dist/Editor.css +49 -7
- package/dist/bundle.js +2 -2
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/SVGLoader/index.js +3 -1
- package/dist/cjs/image/EditorImage.d.ts +2 -1
- package/dist/cjs/image/EditorImage.js +101 -5
- package/dist/cjs/rendering/renderers/CanvasRenderer.js +4 -4
- package/dist/cjs/toolbar/localization.d.ts +1 -0
- package/dist/cjs/toolbar/localization.js +1 -0
- package/dist/cjs/toolbar/widgets/InsertImageWidget/ImageWrapper.d.ts +1 -0
- package/dist/cjs/toolbar/widgets/InsertImageWidget/ImageWrapper.js +7 -0
- package/dist/cjs/toolbar/widgets/InsertImageWidget/index.js +11 -3
- package/dist/cjs/toolbar/widgets/components/makeFileInput.js +12 -1
- package/dist/cjs/toolbar/widgets/components/makeSnappedList.js +69 -4
- package/dist/cjs/tools/Eraser.js +22 -5
- package/dist/cjs/tools/PanZoom.d.ts +54 -0
- package/dist/cjs/tools/PanZoom.js +54 -2
- package/dist/cjs/util/ReactiveValue.d.ts +4 -0
- package/dist/cjs/util/ReactiveValue.js +5 -0
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/SVGLoader/index.mjs +3 -1
- package/dist/mjs/image/EditorImage.d.ts +2 -1
- package/dist/mjs/image/EditorImage.mjs +101 -5
- package/dist/mjs/rendering/renderers/CanvasRenderer.mjs +4 -4
- package/dist/mjs/toolbar/localization.d.ts +1 -0
- package/dist/mjs/toolbar/localization.mjs +1 -0
- package/dist/mjs/toolbar/widgets/InsertImageWidget/ImageWrapper.d.ts +1 -0
- package/dist/mjs/toolbar/widgets/InsertImageWidget/ImageWrapper.mjs +7 -0
- package/dist/mjs/toolbar/widgets/InsertImageWidget/index.mjs +11 -3
- package/dist/mjs/toolbar/widgets/components/makeFileInput.mjs +12 -1
- package/dist/mjs/toolbar/widgets/components/makeSnappedList.mjs +69 -4
- package/dist/mjs/tools/Eraser.mjs +22 -5
- package/dist/mjs/tools/PanZoom.d.ts +54 -0
- package/dist/mjs/tools/PanZoom.mjs +54 -2
- package/dist/mjs/util/ReactiveValue.d.ts +4 -0
- package/dist/mjs/util/ReactiveValue.mjs +5 -0
- package/dist/mjs/version.mjs +1 -1
- package/docs/img/readme-images/unsupported-elements--in-editor.png +0 -0
- package/package.json +2 -2
- package/src/toolbar/EdgeToolbar.scss +8 -3
- package/src/toolbar/widgets/components/makeFileInput.scss +3 -2
- package/src/toolbar/widgets/components/makeSnappedList.scss +58 -12
@@ -176,13 +176,21 @@ class InsertImageWidget extends BaseWidget {
|
|
176
176
|
currentImage?.reset();
|
177
177
|
};
|
178
178
|
this.statusView.replaceChildren(sizeText);
|
179
|
-
|
180
|
-
if (imageData.length > largeImageThreshold) {
|
179
|
+
if (currentImage?.isLarge()) {
|
181
180
|
this.statusView.appendChild(decreaseSizeButton);
|
182
181
|
}
|
183
182
|
else if (currentImage?.isChanged()) {
|
184
183
|
this.statusView.appendChild(resetSizeButton);
|
185
184
|
}
|
185
|
+
else {
|
186
|
+
const hasLargeOrChangedImages = this.images.get().some(image => image.data?.isChanged() || image.data?.isLarge());
|
187
|
+
if (hasLargeOrChangedImages) {
|
188
|
+
// Still show the button -- prevents the layout from readjusting while
|
189
|
+
// scrolling through the image list
|
190
|
+
decreaseSizeButton.disabled = true;
|
191
|
+
this.statusView.appendChild(decreaseSizeButton);
|
192
|
+
}
|
193
|
+
}
|
186
194
|
}
|
187
195
|
updateInputs() {
|
188
196
|
const resetInputs = () => {
|
@@ -220,7 +228,7 @@ class InsertImageWidget extends BaseWidget {
|
|
220
228
|
}
|
221
229
|
const image = new Image();
|
222
230
|
image.src = imageWrapper.getBase64Url();
|
223
|
-
image.setAttribute('alt',
|
231
|
+
image.setAttribute('alt', imageWrapper.getAltText());
|
224
232
|
let component;
|
225
233
|
try {
|
226
234
|
component = await ImageComponent.fromImage(image, transform);
|
@@ -42,7 +42,18 @@ const makeFileInput = (labelText, context, { accepts = '*', allowMultiSelect = f
|
|
42
42
|
icon.style.display = 'none';
|
43
43
|
}
|
44
44
|
else if (files.length > 0) {
|
45
|
-
|
45
|
+
const fileNames = files.map(file => file.name);
|
46
|
+
const maxNames = 5;
|
47
|
+
if (fileNames.length <= maxNames) {
|
48
|
+
descriptionText.textContent = fileNames.join('\n');
|
49
|
+
}
|
50
|
+
else {
|
51
|
+
const fileNamesToShow = fileNames.slice(0, maxNames - 1);
|
52
|
+
descriptionText.textContent = [
|
53
|
+
...fileNamesToShow,
|
54
|
+
context.localization.fileInput__andNMoreFiles(fileNames.length - fileNamesToShow.length),
|
55
|
+
].join('\n');
|
56
|
+
}
|
46
57
|
// Only show the icon when there are files
|
47
58
|
icon.style.display = 'none';
|
48
59
|
}
|
@@ -6,8 +6,72 @@ import { MutableReactiveValue, ReactiveValue } from '../../../util/ReactiveVal
|
|
6
6
|
const makeSnappedList = (itemsValue) => {
|
7
7
|
const container = document.createElement('div');
|
8
8
|
container.classList.add('toolbar-snapped-scroll-list');
|
9
|
+
const scroller = document.createElement('div');
|
10
|
+
scroller.classList.add('scroller');
|
9
11
|
const visibleIndex = MutableReactiveValue.fromInitialValue(0);
|
10
12
|
let observer = null;
|
13
|
+
const makePageMarkers = () => {
|
14
|
+
const markerContainer = document.createElement('div');
|
15
|
+
markerContainer.classList.add('page-markers');
|
16
|
+
// Keyboard focus should go to the main scrolling list.
|
17
|
+
// TODO: Does it make sense for the page marker list to be focusable?
|
18
|
+
markerContainer.setAttribute('tabindex', '-1');
|
19
|
+
const markers = [];
|
20
|
+
const pairedItems = ReactiveValue.union([visibleIndex, itemsValue]);
|
21
|
+
pairedItems.onUpdateAndNow(([currentVisibleIndex, items]) => {
|
22
|
+
let addedOrRemovedMarkers = false;
|
23
|
+
// Items may have been removed from the list of pages. Make the markers reflect that.
|
24
|
+
while (items.length < markers.length) {
|
25
|
+
markers.pop();
|
26
|
+
addedOrRemovedMarkers = true;
|
27
|
+
}
|
28
|
+
let activeMarker;
|
29
|
+
for (let i = 0; i < items.length; i++) {
|
30
|
+
let marker;
|
31
|
+
if (i >= markers.length) {
|
32
|
+
marker = document.createElement('div');
|
33
|
+
// Use a separate content element to increase the clickable size of
|
34
|
+
// the marker.
|
35
|
+
const content = document.createElement('div');
|
36
|
+
content.classList.add('content');
|
37
|
+
marker.replaceChildren(content);
|
38
|
+
markers.push(marker);
|
39
|
+
addedOrRemovedMarkers = true;
|
40
|
+
}
|
41
|
+
else {
|
42
|
+
marker = markers[i];
|
43
|
+
}
|
44
|
+
marker.classList.add('marker');
|
45
|
+
if (i === currentVisibleIndex) {
|
46
|
+
marker.classList.add('-active');
|
47
|
+
activeMarker = marker;
|
48
|
+
}
|
49
|
+
else {
|
50
|
+
marker.classList.remove('-active');
|
51
|
+
}
|
52
|
+
const markerIndex = i;
|
53
|
+
marker.onclick = () => {
|
54
|
+
wrappedItems.get()[markerIndex]?.element?.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
55
|
+
};
|
56
|
+
}
|
57
|
+
// Only call .replaceChildren when necessary -- doing so on every change would
|
58
|
+
// break transitions.
|
59
|
+
if (addedOrRemovedMarkers) {
|
60
|
+
markerContainer.replaceChildren(...markers);
|
61
|
+
}
|
62
|
+
// Handles the case where there are many markers and the current is offscreen
|
63
|
+
if (activeMarker && markerContainer.scrollHeight > container.clientHeight) {
|
64
|
+
activeMarker.scrollIntoView({ block: 'nearest' });
|
65
|
+
}
|
66
|
+
if (markers.length === 1) {
|
67
|
+
markerContainer.classList.add('-one-element');
|
68
|
+
}
|
69
|
+
else {
|
70
|
+
markerContainer.classList.remove('-one-element');
|
71
|
+
}
|
72
|
+
});
|
73
|
+
return markerContainer;
|
74
|
+
};
|
11
75
|
const createObserver = () => {
|
12
76
|
observer = new IntersectionObserver((entries) => {
|
13
77
|
for (const entry of entries) {
|
@@ -23,7 +87,7 @@ const makeSnappedList = (itemsValue) => {
|
|
23
87
|
}, {
|
24
88
|
// Element to use as the boudning box with which to intersect.
|
25
89
|
// See https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
|
26
|
-
root:
|
90
|
+
root: scroller,
|
27
91
|
// Fraction of an element that must be visible to trigger the callback:
|
28
92
|
threshold: 0.9,
|
29
93
|
});
|
@@ -55,7 +119,7 @@ const makeSnappedList = (itemsValue) => {
|
|
55
119
|
for (const item of lastItems) {
|
56
120
|
observer?.unobserve(item.element);
|
57
121
|
}
|
58
|
-
|
122
|
+
scroller.replaceChildren();
|
59
123
|
// An observer is only necessary if there are multiple items to scroll through.
|
60
124
|
if (items.length > 1) {
|
61
125
|
createObserver();
|
@@ -71,7 +135,7 @@ const makeSnappedList = (itemsValue) => {
|
|
71
135
|
container.classList.remove('-empty');
|
72
136
|
}
|
73
137
|
for (const item of items) {
|
74
|
-
|
138
|
+
scroller.appendChild(item.element);
|
75
139
|
}
|
76
140
|
visibleIndex.set(0);
|
77
141
|
if (observer) {
|
@@ -89,7 +153,8 @@ const makeSnappedList = (itemsValue) => {
|
|
89
153
|
});
|
90
154
|
// makeSnappedList is generally shown within the toolbar. This allows users to
|
91
155
|
// scroll it with a touchpad.
|
92
|
-
stopPropagationOfScrollingWheelEvents(
|
156
|
+
stopPropagationOfScrollingWheelEvents(scroller);
|
157
|
+
container.replaceChildren(makePageMarkers(), scroller);
|
93
158
|
return {
|
94
159
|
container,
|
95
160
|
visibleItem,
|
@@ -65,6 +65,7 @@ export default class Eraser extends BaseTool {
|
|
65
65
|
this.editor = editor;
|
66
66
|
this.lastPoint = null;
|
67
67
|
this.isFirstEraseEvt = true;
|
68
|
+
this.toAdd = new Set();
|
68
69
|
// Commands that each remove one element
|
69
70
|
this.eraseCommands = [];
|
70
71
|
this.addCommands = [];
|
@@ -174,15 +175,17 @@ export default class Eraser extends BaseTool {
|
|
174
175
|
newAddCommands.forEach(command => command.apply(this.editor));
|
175
176
|
const finalToErase = [];
|
176
177
|
for (const item of toErase) {
|
177
|
-
if (this.toAdd.
|
178
|
-
this.toAdd
|
178
|
+
if (this.toAdd.has(item)) {
|
179
|
+
this.toAdd.delete(item);
|
179
180
|
}
|
180
181
|
else {
|
181
182
|
finalToErase.push(item);
|
182
183
|
}
|
183
184
|
}
|
184
185
|
this.toRemove.push(...finalToErase);
|
185
|
-
|
186
|
+
for (const item of toAdd) {
|
187
|
+
this.toAdd.add(item);
|
188
|
+
}
|
186
189
|
this.eraseCommands.push(new Erase(finalToErase));
|
187
190
|
this.addCommands.push(...newAddCommands);
|
188
191
|
}
|
@@ -193,7 +196,7 @@ export default class Eraser extends BaseTool {
|
|
193
196
|
if (event.allPointers.length === 1 || event.current.device === PointerDevice.Eraser) {
|
194
197
|
this.lastPoint = event.current.canvasPos;
|
195
198
|
this.toRemove = [];
|
196
|
-
this.toAdd
|
199
|
+
this.toAdd.clear();
|
197
200
|
this.isFirstEraseEvt = true;
|
198
201
|
this.drawPreviewAt(event.current.canvasPos);
|
199
202
|
return true;
|
@@ -209,7 +212,21 @@ export default class Eraser extends BaseTool {
|
|
209
212
|
const commands = [];
|
210
213
|
if (this.addCommands.length > 0) {
|
211
214
|
this.addCommands.forEach(cmd => cmd.unapply(this.editor));
|
212
|
-
|
215
|
+
// Remove items from toAdd that are also present in toRemove -- adding, then
|
216
|
+
// removing these does nothing, and can break undo/redo.
|
217
|
+
for (const item of this.toAdd) {
|
218
|
+
if (this.toRemove.includes(item)) {
|
219
|
+
this.toAdd.delete(item);
|
220
|
+
this.toRemove = this.toRemove.filter(other => other !== item);
|
221
|
+
}
|
222
|
+
}
|
223
|
+
for (const item of this.toRemove) {
|
224
|
+
if (this.toAdd.has(item)) {
|
225
|
+
this.toAdd.delete(item);
|
226
|
+
this.toRemove = this.toRemove.filter(other => other !== item);
|
227
|
+
}
|
228
|
+
}
|
229
|
+
commands.push(...[...this.toAdd].map(a => EditorImage.addElement(a)));
|
213
230
|
this.addCommands = [];
|
214
231
|
}
|
215
232
|
if (this.eraseCommands.length > 0) {
|
@@ -10,13 +10,27 @@ interface PinchData {
|
|
10
10
|
dist: number;
|
11
11
|
}
|
12
12
|
export declare enum PanZoomMode {
|
13
|
+
/** Touch gestures with a single pointer. Ignores non-touch gestures. */
|
13
14
|
OneFingerTouchGestures = 1,
|
15
|
+
/** Touch gestures with exactly two pointers. Ignores non-touch gestures. */
|
14
16
|
TwoFingerTouchGestures = 2,
|
15
17
|
RightClickDrags = 4,
|
18
|
+
/** Single-pointer gestures of *any* type (including touch). */
|
16
19
|
SinglePointerGestures = 8,
|
20
|
+
/** Keyboard navigation (e.g. LeftArrow to move left). */
|
17
21
|
Keyboard = 16,
|
22
|
+
/** If provided, prevents **this** tool from rotating the viewport (other tools may still do so). */
|
18
23
|
RotationLocked = 32
|
19
24
|
}
|
25
|
+
/**
|
26
|
+
* This tool moves the viewport in response to touchpad, touchscreen, mouse, and keyboard events.
|
27
|
+
*
|
28
|
+
* Which events are handled, and which are skipped, are determined by the tool's `mode`. For example,
|
29
|
+
* a `PanZoom` tool with `mode = PanZoomMode.TwoFingerTouchGestures|PanZoomMode.RightClickDrags` would
|
30
|
+
* respond to right-click drag events and two-finger touch gestures.
|
31
|
+
*
|
32
|
+
* @see {@link setModeEnabled}
|
33
|
+
*/
|
20
34
|
export default class PanZoom extends BaseTool {
|
21
35
|
private editor;
|
22
36
|
private mode;
|
@@ -58,8 +72,48 @@ export default class PanZoom extends BaseTool {
|
|
58
72
|
onWheel({ delta, screenPos }: WheelEvt): boolean;
|
59
73
|
onKeyPress(event: KeyPressEvent): boolean;
|
60
74
|
private isRotationLocked;
|
75
|
+
/**
|
76
|
+
* Changes the types of gestures used by this pan/zoom tool.
|
77
|
+
*
|
78
|
+
* @see {@link PanZoomMode} {@link setMode}
|
79
|
+
*
|
80
|
+
* @example
|
81
|
+
* ```ts,runnable
|
82
|
+
* import { Editor, PanZoomTool, PanZoomMode } from 'js-draw';
|
83
|
+
*
|
84
|
+
* const editor = new Editor(document.body);
|
85
|
+
*
|
86
|
+
* // By default, there are multiple PanZoom tools that handle different events.
|
87
|
+
* // This gets all PanZoomTools.
|
88
|
+
* const panZoomToolList = editor.toolController.getMatchingTools(PanZoomTool);
|
89
|
+
*
|
90
|
+
* // The first PanZoomTool is the highest priority -- by default,
|
91
|
+
* // this tool is responsible for handling multi-finger touch gestures.
|
92
|
+
* //
|
93
|
+
* // Lower-priority PanZoomTools handle one-finger touch gestures and
|
94
|
+
* // key-presses.
|
95
|
+
* const panZoomTool = panZoomToolList[0];
|
96
|
+
*
|
97
|
+
* // Lock rotation for multi-finger touch gestures.
|
98
|
+
* panZoomTool.setModeEnabled(PanZoomMode.RotationLocked, true);
|
99
|
+
* ```
|
100
|
+
*/
|
61
101
|
setModeEnabled(mode: PanZoomMode, enabled: boolean): void;
|
102
|
+
/**
|
103
|
+
* Sets all modes for this tool using a bitmask.
|
104
|
+
*
|
105
|
+
* @see {@link setModeEnabled}
|
106
|
+
*
|
107
|
+
* @example
|
108
|
+
* ```ts
|
109
|
+
* tool.setMode(PanZoomMode.RotationLocked|PanZoomMode.TwoFingerTouchGestures);
|
110
|
+
* ```
|
111
|
+
*/
|
62
112
|
setMode(mode: PanZoomMode): void;
|
113
|
+
/**
|
114
|
+
* Returns a bitmask indicating the currently-enabled modes.
|
115
|
+
* @see {@link setModeEnabled}
|
116
|
+
*/
|
63
117
|
getMode(): PanZoomMode;
|
64
118
|
}
|
65
119
|
export {};
|
@@ -7,11 +7,16 @@ import BaseTool from './BaseTool.mjs';
|
|
7
7
|
import { moveDownKeyboardShortcutId, moveLeftKeyboardShortcutId, moveRightKeyboardShortcutId, moveUpKeyboardShortcutId, rotateClockwiseKeyboardShortcutId, rotateCounterClockwiseKeyboardShortcutId, zoomInKeyboardShortcutId, zoomOutKeyboardShortcutId } from './keybindings.mjs';
|
8
8
|
export var PanZoomMode;
|
9
9
|
(function (PanZoomMode) {
|
10
|
+
/** Touch gestures with a single pointer. Ignores non-touch gestures. */
|
10
11
|
PanZoomMode[PanZoomMode["OneFingerTouchGestures"] = 1] = "OneFingerTouchGestures";
|
12
|
+
/** Touch gestures with exactly two pointers. Ignores non-touch gestures. */
|
11
13
|
PanZoomMode[PanZoomMode["TwoFingerTouchGestures"] = 2] = "TwoFingerTouchGestures";
|
12
14
|
PanZoomMode[PanZoomMode["RightClickDrags"] = 4] = "RightClickDrags";
|
15
|
+
/** Single-pointer gestures of *any* type (including touch). */
|
13
16
|
PanZoomMode[PanZoomMode["SinglePointerGestures"] = 8] = "SinglePointerGestures";
|
17
|
+
/** Keyboard navigation (e.g. LeftArrow to move left). */
|
14
18
|
PanZoomMode[PanZoomMode["Keyboard"] = 16] = "Keyboard";
|
19
|
+
/** If provided, prevents **this** tool from rotating the viewport (other tools may still do so). */
|
15
20
|
PanZoomMode[PanZoomMode["RotationLocked"] = 32] = "RotationLocked";
|
16
21
|
})(PanZoomMode || (PanZoomMode = {}));
|
17
22
|
class InertialScroller {
|
@@ -59,6 +64,15 @@ class InertialScroller {
|
|
59
64
|
}
|
60
65
|
}
|
61
66
|
}
|
67
|
+
/**
|
68
|
+
* This tool moves the viewport in response to touchpad, touchscreen, mouse, and keyboard events.
|
69
|
+
*
|
70
|
+
* Which events are handled, and which are skipped, are determined by the tool's `mode`. For example,
|
71
|
+
* a `PanZoom` tool with `mode = PanZoomMode.TwoFingerTouchGestures|PanZoomMode.RightClickDrags` would
|
72
|
+
* respond to right-click drag events and two-finger touch gestures.
|
73
|
+
*
|
74
|
+
* @see {@link setModeEnabled}
|
75
|
+
*/
|
62
76
|
export default class PanZoom extends BaseTool {
|
63
77
|
constructor(editor, mode, description) {
|
64
78
|
super(editor.notifier, description);
|
@@ -422,8 +436,32 @@ export default class PanZoom extends BaseTool {
|
|
422
436
|
isRotationLocked() {
|
423
437
|
return !!(this.mode & PanZoomMode.RotationLocked);
|
424
438
|
}
|
425
|
-
|
426
|
-
|
439
|
+
/**
|
440
|
+
* Changes the types of gestures used by this pan/zoom tool.
|
441
|
+
*
|
442
|
+
* @see {@link PanZoomMode} {@link setMode}
|
443
|
+
*
|
444
|
+
* @example
|
445
|
+
* ```ts,runnable
|
446
|
+
* import { Editor, PanZoomTool, PanZoomMode } from 'js-draw';
|
447
|
+
*
|
448
|
+
* const editor = new Editor(document.body);
|
449
|
+
*
|
450
|
+
* // By default, there are multiple PanZoom tools that handle different events.
|
451
|
+
* // This gets all PanZoomTools.
|
452
|
+
* const panZoomToolList = editor.toolController.getMatchingTools(PanZoomTool);
|
453
|
+
*
|
454
|
+
* // The first PanZoomTool is the highest priority -- by default,
|
455
|
+
* // this tool is responsible for handling multi-finger touch gestures.
|
456
|
+
* //
|
457
|
+
* // Lower-priority PanZoomTools handle one-finger touch gestures and
|
458
|
+
* // key-presses.
|
459
|
+
* const panZoomTool = panZoomToolList[0];
|
460
|
+
*
|
461
|
+
* // Lock rotation for multi-finger touch gestures.
|
462
|
+
* panZoomTool.setModeEnabled(PanZoomMode.RotationLocked, true);
|
463
|
+
* ```
|
464
|
+
*/
|
427
465
|
setModeEnabled(mode, enabled) {
|
428
466
|
let newMode = this.mode;
|
429
467
|
if (enabled) {
|
@@ -434,6 +472,16 @@ export default class PanZoom extends BaseTool {
|
|
434
472
|
}
|
435
473
|
this.setMode(newMode);
|
436
474
|
}
|
475
|
+
/**
|
476
|
+
* Sets all modes for this tool using a bitmask.
|
477
|
+
*
|
478
|
+
* @see {@link setModeEnabled}
|
479
|
+
*
|
480
|
+
* @example
|
481
|
+
* ```ts
|
482
|
+
* tool.setMode(PanZoomMode.RotationLocked|PanZoomMode.TwoFingerTouchGestures);
|
483
|
+
* ```
|
484
|
+
*/
|
437
485
|
setMode(mode) {
|
438
486
|
if (mode !== this.mode) {
|
439
487
|
this.mode = mode;
|
@@ -443,6 +491,10 @@ export default class PanZoom extends BaseTool {
|
|
443
491
|
});
|
444
492
|
}
|
445
493
|
}
|
494
|
+
/**
|
495
|
+
* Returns a bitmask indicating the currently-enabled modes.
|
496
|
+
* @see {@link setModeEnabled}
|
497
|
+
*/
|
446
498
|
getMode() {
|
447
499
|
return this.mode;
|
448
500
|
}
|
@@ -2,6 +2,9 @@ type ListenerResult = {
|
|
2
2
|
remove(): void;
|
3
3
|
};
|
4
4
|
type UpdateCallback<T> = (value: T) => void;
|
5
|
+
type ReactiveValuesOf<T extends unknown[]> = {
|
6
|
+
[key in keyof T]: ReactiveValue<T[key]>;
|
7
|
+
};
|
5
8
|
/**
|
6
9
|
* A `ReactiveValue` is a value that
|
7
10
|
* - updates periodically,
|
@@ -56,6 +59,7 @@ export declare abstract class ReactiveValue<T> {
|
|
56
59
|
* Returns a reactive value derived from a single `source`.
|
57
60
|
*/
|
58
61
|
static map<A, B>(source: ReactiveValue<A>, map: (a: A) => B, inverseMap: (b: B) => A): MutableReactiveValue<B>;
|
62
|
+
static union<Values extends [...unknown[]]>(values: ReactiveValuesOf<Values>): ReactiveValue<Values>;
|
59
63
|
}
|
60
64
|
export declare abstract class MutableReactiveValue<T> extends ReactiveValue<T> {
|
61
65
|
/**
|
@@ -103,6 +103,11 @@ export class ReactiveValue {
|
|
103
103
|
}
|
104
104
|
return result;
|
105
105
|
}
|
106
|
+
static union(values) {
|
107
|
+
return ReactiveValue.fromCallback(() => {
|
108
|
+
return values.map(value => value.get());
|
109
|
+
}, values);
|
110
|
+
}
|
106
111
|
}
|
107
112
|
export class MutableReactiveValue extends ReactiveValue {
|
108
113
|
static fromProperty(sourceValue, propertyName) {
|
package/dist/mjs/version.mjs
CHANGED
Binary file
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "js-draw",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.20.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": "a981ae8ef5f2ac8ef27c8ce2872a2dbf0c25050d"
|
90
90
|
}
|
@@ -307,14 +307,19 @@
|
|
307
307
|
border: none;
|
308
308
|
padding: 10px;
|
309
309
|
|
310
|
-
color: var(--foreground-color-1);
|
311
|
-
|
312
310
|
transition: 0.2s ease box-shadow;
|
313
311
|
|
314
|
-
&:hover {
|
312
|
+
&:not(:disabled):hover {
|
315
313
|
box-shadow: 0 1px 2px var(--shadow-color);
|
316
314
|
}
|
317
315
|
|
316
|
+
&:disabled {
|
317
|
+
opacity: 0.5;
|
318
|
+
font-weight: unset;
|
319
|
+
cursor: unset;
|
320
|
+
color: var(--foreground-color-1);
|
321
|
+
}
|
322
|
+
|
318
323
|
font-weight: bold;
|
319
324
|
color: var(--primary-action-foreground-color);
|
320
325
|
}
|
@@ -2,27 +2,73 @@
|
|
2
2
|
// Repeat for specificity.
|
3
3
|
// TODO(v2): Refactor everything to use RCSS.
|
4
4
|
:root .toolbar-snapped-scroll-list.toolbar-snapped-scroll-list.toolbar-snapped-scroll-list {
|
5
|
-
overflow-y: auto;
|
6
|
-
scroll-snap-type: y mandatory;
|
7
5
|
height: min(200px, 50vh);
|
6
|
+
position: relative;
|
8
7
|
display: flex;
|
9
|
-
|
8
|
+
align-items: center;
|
9
|
+
|
10
|
+
> .scroller {
|
11
|
+
display: flex;
|
12
|
+
flex-direction: column;
|
13
|
+
overflow-y: auto;
|
14
|
+
scroll-snap-type: y mandatory;
|
10
15
|
|
11
|
-
> .item {
|
12
16
|
height: 100%;
|
13
17
|
width: 100%;
|
14
|
-
flex-
|
15
|
-
|
16
|
-
display: flex;
|
17
|
-
justify-content: center;
|
18
|
-
align-items: center;
|
18
|
+
flex-grow: 1;
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
> .item {
|
21
|
+
height: 100%;
|
22
|
+
width: 100%;
|
23
|
+
flex-shrink: 0;
|
24
|
+
|
25
|
+
display: flex;
|
26
|
+
justify-content: center;
|
27
|
+
align-items: center;
|
28
|
+
|
29
|
+
scroll-snap-align: start;
|
30
|
+
scroll-snap-stop: always;
|
31
|
+
box-sizing: border-box;
|
32
|
+
}
|
23
33
|
}
|
24
34
|
|
25
35
|
&.-empty {
|
26
36
|
display: none;
|
27
37
|
}
|
38
|
+
|
39
|
+
> .page-markers {
|
40
|
+
overflow: hidden;
|
41
|
+
|
42
|
+
display: flex;
|
43
|
+
flex-direction: column;
|
44
|
+
align-items: center;
|
45
|
+
|
46
|
+
max-height: 100%;
|
47
|
+
min-height: 0;
|
48
|
+
|
49
|
+
&.-one-element {
|
50
|
+
visibility: hidden;
|
51
|
+
}
|
52
|
+
|
53
|
+
> .marker {
|
54
|
+
> .content {
|
55
|
+
background-color: var(--foreground-color-1);
|
56
|
+
border-radius: 2px;
|
57
|
+
padding: 2px;
|
58
|
+
}
|
59
|
+
|
60
|
+
padding: 2px;
|
61
|
+
opacity: 0.1;
|
62
|
+
cursor: pointer;
|
63
|
+
|
64
|
+
left: 0;
|
65
|
+
transition: left 0.2s ease;
|
66
|
+
|
67
|
+
&.-active {
|
68
|
+
position: relative;
|
69
|
+
left: 2px;
|
70
|
+
opacity: 0.2;
|
71
|
+
}
|
72
|
+
}
|
73
|
+
}
|
28
74
|
}
|