@lvl99dotcom/paintcanvas 0.0.0-stage → 0.3.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/LICENSE +201 -0
- package/README.md +131 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/dist/lib/brushes/BrushTipCache/index.d.ts +32 -0
- package/dist/lib/brushes/BrushTipCache/index.js +60 -0
- package/dist/lib/brushes/StrokeEngine/index.d.ts +171 -0
- package/dist/lib/brushes/StrokeEngine/index.js +324 -0
- package/dist/lib/brushes/StrokePath/index.d.ts +53 -0
- package/dist/lib/brushes/StrokePath/index.js +161 -0
- package/dist/lib/brushes/StrokeSampler/index.d.ts +55 -0
- package/dist/lib/brushes/StrokeSampler/index.js +75 -0
- package/dist/lib/brushes/authors/index.d.ts +55 -0
- package/dist/lib/brushes/authors/index.js +60 -0
- package/dist/lib/brushes/configRanges/index.d.ts +52 -0
- package/dist/lib/brushes/configRanges/index.js +206 -0
- package/dist/lib/brushes/index.d.ts +5 -0
- package/dist/lib/brushes/index.js +19 -0
- package/dist/lib/brushes/params/index.d.ts +108 -0
- package/dist/lib/brushes/params/index.js +139 -0
- package/dist/lib/brushes/presetFile/index.d.ts +32 -0
- package/dist/lib/brushes/presetFile/index.js +55 -0
- package/dist/lib/brushes/presets/drawing/brush-pen.js +3 -0
- package/dist/lib/brushes/presets/drawing/felt-pen.js +3 -0
- package/dist/lib/brushes/presets/drawing/index.d.ts +13 -0
- package/dist/lib/brushes/presets/drawing/index.js +88 -0
- package/dist/lib/brushes/presets/drawing/pencil-2b.js +3 -0
- package/dist/lib/brushes/presets/drawing/pencil-hb.js +3 -0
- package/dist/lib/brushes/presets/painting/grass.js +3 -0
- package/dist/lib/brushes/presets/painting/hair.js +3 -0
- package/dist/lib/brushes/presets/painting/index.d.ts +18 -0
- package/dist/lib/brushes/presets/painting/index.js +118 -0
- package/dist/lib/brushes/presets/painting/splatter.js +3 -0
- package/dist/lib/brushes/presets/painting/textured-paint.js +3 -0
- package/dist/lib/brushes/presets/painting/watery-paint.js +3 -0
- package/dist/lib/brushes/presets/round/index.d.ts +9 -0
- package/dist/lib/brushes/presets/round/index.js +44 -0
- package/dist/lib/brushes/presets/round/round.js +3 -0
- package/dist/lib/brushes/presets/round/soft-round.js +3 -0
- package/dist/lib/brushes/presets/sized/index.d.ts +20 -0
- package/dist/lib/brushes/presets/sized/index.js +30 -0
- package/dist/lib/brushes/presets/sketching/index.d.ts +14 -0
- package/dist/lib/brushes/presets/sketching/index.js +107 -0
- package/dist/lib/brushes/presets/square/index.d.ts +7 -0
- package/dist/lib/brushes/presets/square/index.js +22 -0
- package/dist/lib/brushes/presets/square/square.js +3 -0
- package/dist/lib/brushes/shapeImage/index.d.ts +40 -0
- package/dist/lib/brushes/shapeImage/index.js +61 -0
- package/dist/lib/brushes/types/index.d.ts +243 -0
- package/dist/lib/brushes/types/index.js +20 -0
- package/dist/lib/classes/BackgroundWork/index.d.ts +55 -0
- package/dist/lib/classes/BackgroundWork/index.js +97 -0
- package/dist/lib/classes/BrushSettings/index.d.ts +91 -0
- package/dist/lib/classes/BrushSettings/index.js +152 -0
- package/dist/lib/classes/CustomPresets/index.d.ts +53 -0
- package/dist/lib/classes/CustomPresets/index.js +71 -0
- package/dist/lib/classes/ExternalSheets/index.d.ts +64 -0
- package/dist/lib/classes/ExternalSheets/index.js +112 -0
- package/dist/lib/classes/GestureTracker/index.d.ts +62 -0
- package/dist/lib/classes/GestureTracker/index.js +122 -0
- package/dist/lib/classes/History/index.d.ts +83 -0
- package/dist/lib/classes/History/index.js +126 -0
- package/dist/lib/classes/HistoryGuard/index.d.ts +58 -0
- package/dist/lib/classes/HistoryGuard/index.js +97 -0
- package/dist/lib/classes/InputSettings/index.d.ts +74 -0
- package/dist/lib/classes/InputSettings/index.js +101 -0
- package/dist/lib/classes/KeyboardShortcuts/index.d.ts +48 -0
- package/dist/lib/classes/KeyboardShortcuts/index.js +137 -0
- package/dist/lib/classes/Layer/index.d.ts +86 -0
- package/dist/lib/classes/Layer/index.js +111 -0
- package/dist/lib/classes/LayerAutosave/index.d.ts +180 -0
- package/dist/lib/classes/LayerAutosave/index.js +346 -0
- package/dist/lib/classes/LayerStack/index.d.ts +177 -0
- package/dist/lib/classes/LayerStack/index.js +394 -0
- package/dist/lib/classes/PaintCanvas/index.d.ts +537 -0
- package/dist/lib/classes/PaintCanvas/index.js +965 -0
- package/dist/lib/classes/StrokeLog/index.d.ts +149 -0
- package/dist/lib/classes/StrokeLog/index.js +257 -0
- package/dist/lib/classes/Viewport/index.d.ts +108 -0
- package/dist/lib/classes/Viewport/index.js +194 -0
- package/dist/lib/document/index.d.ts +17 -0
- package/dist/lib/document/index.js +25 -0
- package/dist/lib/document/schema.d.ts +132 -0
- package/dist/lib/document/schema.js +110 -0
- package/dist/lib/i18n/index.d.ts +131 -0
- package/dist/lib/i18n/index.js +277 -0
- package/dist/lib/image/index.d.ts +90 -0
- package/dist/lib/image/index.js +147 -0
- package/dist/lib/messages/PatchSender/index.d.ts +75 -0
- package/dist/lib/messages/PatchSender/index.js +125 -0
- package/dist/lib/messages/index.d.ts +26 -0
- package/dist/lib/messages/index.js +59 -0
- package/dist/lib/messages/schema.d.ts +1775 -0
- package/dist/lib/messages/schema.js +192 -0
- package/dist/lib/presets/index.d.ts +32 -0
- package/dist/lib/presets/index.js +35 -0
- package/dist/lib/presets/schema.d.ts +753 -0
- package/dist/lib/presets/schema.js +181 -0
- package/dist/lib/rendering/BrushTip/index.d.ts +63 -0
- package/dist/lib/rendering/BrushTip/index.js +112 -0
- package/dist/lib/rendering/checkerboard/index.d.ts +22 -0
- package/dist/lib/rendering/checkerboard/index.js +31 -0
- package/dist/lib/rendering/compositing/index.d.ts +64 -0
- package/dist/lib/rendering/compositing/index.js +124 -0
- package/dist/lib/rendering/cursor/index.d.ts +14 -0
- package/dist/lib/rendering/cursor/index.js +39 -0
- package/dist/lib/rendering/cursors/index.d.ts +18 -0
- package/dist/lib/rendering/cursors/index.js +23 -0
- package/dist/lib/rendering/maskOutline/index.d.ts +22 -0
- package/dist/lib/rendering/maskOutline/index.js +78 -0
- package/dist/lib/rendering/outline/index.d.ts +36 -0
- package/dist/lib/rendering/outline/index.js +120 -0
- package/dist/lib/rendering/sample/index.d.ts +55 -0
- package/dist/lib/rendering/sample/index.js +83 -0
- package/dist/lib/rendering/screenshot/index.d.ts +67 -0
- package/dist/lib/rendering/screenshot/index.js +96 -0
- package/dist/lib/rendering/transformBox/index.d.ts +10 -0
- package/dist/lib/rendering/transformBox/index.js +57 -0
- package/dist/lib/selection/Lasso/index.d.ts +70 -0
- package/dist/lib/selection/Lasso/index.js +156 -0
- package/dist/lib/selection/Selection/index.d.ts +86 -0
- package/dist/lib/selection/Selection/index.js +228 -0
- package/dist/lib/selection/geometry/index.d.ts +37 -0
- package/dist/lib/selection/geometry/index.js +57 -0
- package/dist/lib/storage/index.d.ts +63 -0
- package/dist/lib/storage/index.js +114 -0
- package/dist/lib/transform/Transformer/index.d.ts +124 -0
- package/dist/lib/transform/Transformer/index.js +286 -0
- package/dist/lib/transform/geometry/index.d.ts +95 -0
- package/dist/lib/transform/geometry/index.js +258 -0
- package/dist/lib/utils/array/index.d.ts +10 -0
- package/dist/lib/utils/array/index.js +21 -0
- package/dist/lib/utils/gestures/index.d.ts +35 -0
- package/dist/lib/utils/gestures/index.js +81 -0
- package/dist/lib/utils/navigation/index.d.ts +51 -0
- package/dist/lib/utils/navigation/index.js +59 -0
- package/dist/lib/utils/random/index.d.ts +18 -0
- package/dist/lib/utils/random/index.js +27 -0
- package/dist/lib/utils/shortcuts/index.d.ts +74 -0
- package/dist/lib/utils/shortcuts/index.js +189 -0
- package/dist/lib/validation/validators.generated.d.ts +27 -0
- package/dist/lib/validation/validators.generated.js +19826 -0
- package/dist/locales/en-GB.js +181 -0
- package/dist/locales/en-US.js +181 -0
- package/dist/locales/fr.js +181 -0
- package/dist/locales/ja.js +181 -0
- package/dist/locales/ko.js +181 -0
- package/dist/locales/zh-Hans.js +181 -0
- package/dist/locales/zh-Hant.js +181 -0
- package/dist/package.js +3 -0
- package/dist/testing/app.d.ts +7 -0
- package/dist/testing/app.js +16 -0
- package/dist/testing/elements.d.ts +42 -0
- package/dist/testing/elements.js +58 -0
- package/dist/testing/fakeCanvas.d.ts +21 -0
- package/dist/testing/fakeCanvas.js +26 -0
- package/dist/testing/layerStore.d.ts +26 -0
- package/dist/testing/layerStore.js +43 -0
- package/dist/testing/locks.d.ts +15 -0
- package/dist/testing/locks.js +41 -0
- package/dist/testing/recordingCanvas.d.ts +14 -0
- package/dist/testing/recordingCanvas.js +28 -0
- package/dist/testing/recordingContext.d.ts +10 -0
- package/dist/testing/recordingContext.js +19 -0
- package/dist/ui/ChangeController/index.d.ts +40 -0
- package/dist/ui/ChangeController/index.js +62 -0
- package/dist/ui/base/index.d.ts +69 -0
- package/dist/ui/base/index.js +94 -0
- package/dist/ui/elements/PaintCanvas/index.d.ts +249 -0
- package/dist/ui/elements/PaintCanvas/index.js +584 -0
- package/dist/ui/elements/PaintCanvas/style.js +3 -0
- package/dist/ui/elements/PcBrushConfig/index.d.ts +130 -0
- package/dist/ui/elements/PcBrushConfig/index.js +465 -0
- package/dist/ui/elements/PcBrushConfig/style.js +3 -0
- package/dist/ui/elements/PcBrushMenu/index.d.ts +121 -0
- package/dist/ui/elements/PcBrushMenu/index.js +362 -0
- package/dist/ui/elements/PcBrushMenu/style.js +3 -0
- package/dist/ui/elements/PcBrushTest/index.d.ts +71 -0
- package/dist/ui/elements/PcBrushTest/index.js +136 -0
- package/dist/ui/elements/PcBrushTest/style.js +3 -0
- package/dist/ui/elements/PcButton/index.d.ts +57 -0
- package/dist/ui/elements/PcButton/index.js +124 -0
- package/dist/ui/elements/PcButtonGroup/index.d.ts +44 -0
- package/dist/ui/elements/PcButtonGroup/index.js +93 -0
- package/dist/ui/elements/PcCanvas/index.d.ts +356 -0
- package/dist/ui/elements/PcCanvas/index.js +1004 -0
- package/dist/ui/elements/PcCanvas/style.js +3 -0
- package/dist/ui/elements/PcCanvasMap/index.d.ts +96 -0
- package/dist/ui/elements/PcCanvasMap/index.js +187 -0
- package/dist/ui/elements/PcCanvasMap/style.js +3 -0
- package/dist/ui/elements/PcContextMenu/index.d.ts +72 -0
- package/dist/ui/elements/PcContextMenu/index.js +148 -0
- package/dist/ui/elements/PcDialog/index.d.ts +63 -0
- package/dist/ui/elements/PcDialog/index.js +114 -0
- package/dist/ui/elements/PcIcon/index.d.ts +36 -0
- package/dist/ui/elements/PcIcon/index.js +43 -0
- package/dist/ui/elements/PcIconToggle/index.d.ts +46 -0
- package/dist/ui/elements/PcIconToggle/index.js +87 -0
- package/dist/ui/elements/PcInfoTip/index.d.ts +40 -0
- package/dist/ui/elements/PcInfoTip/index.js +95 -0
- package/dist/ui/elements/PcInputSettings/index.d.ts +69 -0
- package/dist/ui/elements/PcInputSettings/index.js +154 -0
- package/dist/ui/elements/PcLayerItem/index.d.ts +221 -0
- package/dist/ui/elements/PcLayerItem/index.js +561 -0
- package/dist/ui/elements/PcLayerItem/style.js +3 -0
- package/dist/ui/elements/PcLayersMenu/index.d.ts +108 -0
- package/dist/ui/elements/PcLayersMenu/index.js +208 -0
- package/dist/ui/elements/PcLayersMenu/style.js +3 -0
- package/dist/ui/elements/PcMatch/index.d.ts +31 -0
- package/dist/ui/elements/PcMatch/index.js +58 -0
- package/dist/ui/elements/PcPanel/index.d.ts +43 -0
- package/dist/ui/elements/PcPanel/index.js +51 -0
- package/dist/ui/elements/PcPopover/index.d.ts +48 -0
- package/dist/ui/elements/PcPopover/index.js +86 -0
- package/dist/ui/elements/PcSlider/index.d.ts +57 -0
- package/dist/ui/elements/PcSlider/index.js +88 -0
- package/dist/ui/elements/PcStats/index.d.ts +54 -0
- package/dist/ui/elements/PcStats/index.js +144 -0
- package/dist/ui/elements/PcThemePicker/index.d.ts +56 -0
- package/dist/ui/elements/PcThemePicker/index.js +134 -0
- package/dist/ui/elements/PcToast/index.d.ts +58 -0
- package/dist/ui/elements/PcToast/index.js +110 -0
- package/dist/ui/elements/PcToggleCheckbox/index.d.ts +37 -0
- package/dist/ui/elements/PcToggleCheckbox/index.js +105 -0
- package/dist/ui/elements/PcToolSettings/index.d.ts +128 -0
- package/dist/ui/elements/PcToolSettings/index.js +549 -0
- package/dist/ui/elements/PcToolSettings/style.js +3 -0
- package/dist/ui/elements/PcToolbar/index.d.ts +71 -0
- package/dist/ui/elements/PcToolbar/index.js +190 -0
- package/dist/ui/elements/PcToolbar/style.js +3 -0
- package/dist/ui/icons/images/brush.js +3 -0
- package/dist/ui/icons/images/circle-mask.js +3 -0
- package/dist/ui/icons/images/clipboard.js +3 -0
- package/dist/ui/icons/images/cursor.js +3 -0
- package/dist/ui/icons/images/eraser.js +3 -0
- package/dist/ui/icons/images/eyedropper.js +3 -0
- package/dist/ui/icons/images/flip-horizontal.js +3 -0
- package/dist/ui/icons/images/flip-vertical.js +3 -0
- package/dist/ui/icons/images/focus-pen.js +3 -0
- package/dist/ui/icons/images/freehand-mask.js +3 -0
- package/dist/ui/icons/images/hidden.js +3 -0
- package/dist/ui/icons/images/layers.js +3 -0
- package/dist/ui/icons/images/linked.js +3 -0
- package/dist/ui/icons/images/locked.js +3 -0
- package/dist/ui/icons/images/moon.js +3 -0
- package/dist/ui/icons/images/pan.js +3 -0
- package/dist/ui/icons/images/polygon-mask.js +3 -0
- package/dist/ui/icons/images/pressure.js +3 -0
- package/dist/ui/icons/images/randomise.js +3 -0
- package/dist/ui/icons/images/rectangle-mask.js +3 -0
- package/dist/ui/icons/images/redo.js +3 -0
- package/dist/ui/icons/images/rotate-90-left.js +3 -0
- package/dist/ui/icons/images/rotate-90-right.js +3 -0
- package/dist/ui/icons/images/smoothing-bezier-cubic.js +3 -0
- package/dist/ui/icons/images/smoothing-bezier-quad.js +3 -0
- package/dist/ui/icons/images/smoothing-linear.js +3 -0
- package/dist/ui/icons/images/star.js +3 -0
- package/dist/ui/icons/images/sun.js +3 -0
- package/dist/ui/icons/images/tilt.js +3 -0
- package/dist/ui/icons/images/undo.js +3 -0
- package/dist/ui/icons/images/unlinked.js +3 -0
- package/dist/ui/icons/images/unlocked.js +3 -0
- package/dist/ui/icons/images/visible.js +3 -0
- package/dist/ui/icons/images/zoom-in.js +3 -0
- package/dist/ui/icons/images/zoom-out.js +3 -0
- package/dist/ui/icons/images/zoom.js +3 -0
- package/dist/ui/icons/index.d.ts +42 -0
- package/dist/ui/icons/index.js +130 -0
- package/dist/ui/index.d.ts +21 -0
- package/dist/ui/index.js +22 -0
- package/dist/ui/layout/index.d.ts +88 -0
- package/dist/ui/layout/index.js +96 -0
- package/dist/ui/styles/index.d.ts +13 -0
- package/dist/ui/styles/index.js +195 -0
- package/dist/ui/tokens/index.d.ts +29 -0
- package/dist/ui/tokens/index.js +114 -0
- package/locales/en-GB.json +179 -0
- package/locales/en-US.json +179 -0
- package/locales/fr.json +179 -0
- package/locales/ja.json +179 -0
- package/locales/ko.json +179 -0
- package/locales/zh-Hans.json +179 -0
- package/locales/zh-Hant.json +179 -0
- package/package.json +97 -3
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// src/lib/classes/GestureTracker/index.ts
|
|
2
|
+
/** A tap's fingers must all lift within this long of the first touching down */
|
|
3
|
+
var TAP_MAX_MS = 300;
|
|
4
|
+
/** How far a finger can move and still be tapping (screen pixels); two-finger pan and pinch wait until a finger moves further */
|
|
5
|
+
var TAP_SLOP_PX = 10;
|
|
6
|
+
/** How far three fingers must move together, or spread apart or close together, to swipe (screen pixels) */
|
|
7
|
+
var SWIPE_MIN_PX = 40;
|
|
8
|
+
/**
|
|
9
|
+
* The swipe three fingers made from where they were to where they are: in or out if their spread changed more than
|
|
10
|
+
* they moved together, otherwise left, right, up or down (whichever way they moved most). Undefined if they moved less
|
|
11
|
+
* than `SWIPE_MIN_PX`.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* swipeOf([{ x: 0, y: 0 }, { x: 10, y: 0 }, { x: 20, y: 0 }], [{ x: 50, y: 0 }, { x: 60, y: 0 }, { x: 70, y: 0 }]); // "swipeRight3"
|
|
15
|
+
*/
|
|
16
|
+
var swipeOf = (from, to) => {
|
|
17
|
+
const centre = (points) => ({
|
|
18
|
+
x: points.reduce((sum, p) => sum + p.x, 0) / points.length,
|
|
19
|
+
y: points.reduce((sum, p) => sum + p.y, 0) / points.length
|
|
20
|
+
});
|
|
21
|
+
const spread = (points) => {
|
|
22
|
+
const c = centre(points);
|
|
23
|
+
return points.reduce((sum, p) => sum + Math.hypot(p.x - c.x, p.y - c.y), 0) / points.length;
|
|
24
|
+
};
|
|
25
|
+
const a = centre(from);
|
|
26
|
+
const b = centre(to);
|
|
27
|
+
const dx = b.x - a.x;
|
|
28
|
+
const dy = b.y - a.y;
|
|
29
|
+
const moved = Math.hypot(dx, dy);
|
|
30
|
+
const spreadChange = spread(to) - spread(from);
|
|
31
|
+
if (Math.abs(spreadChange) > moved) {
|
|
32
|
+
if (Math.abs(spreadChange) < 40) return void 0;
|
|
33
|
+
return spreadChange > 0 ? "swipeOut3" : "swipeIn3";
|
|
34
|
+
}
|
|
35
|
+
if (moved < 40) return void 0;
|
|
36
|
+
if (Math.abs(dx) > Math.abs(dy)) return dx > 0 ? "swipeRight3" : "swipeLeft3";
|
|
37
|
+
return dy > 0 ? "swipeDown3" : "swipeUp3";
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Recognises touch gestures from the fingers on the canvas, from the first finger down to the last one up: a tap with
|
|
41
|
+
* two or three fingers (all lifted within `TAP_MAX_MS`, none moving more than `TAP_SLOP_PX`), or a swipe with three
|
|
42
|
+
* fingers (see `swipeOf`, measured from when the third finger touched down to when the first of them lifted). Four or
|
|
43
|
+
* more fingers, or a cancelled touch (e.g. taken by a system gesture), make no gesture.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* const tracker = new GestureTracker();
|
|
47
|
+
* tracker.down(1, { x: 0, y: 0 }, 0);
|
|
48
|
+
* tracker.down(2, { x: 50, y: 0 }, 10);
|
|
49
|
+
* tracker.up(1, 100);
|
|
50
|
+
* tracker.up(2, 120); // "tap2"
|
|
51
|
+
*/
|
|
52
|
+
var GestureTracker = class {
|
|
53
|
+
/** Where each finger on the screen touched down, and where it is now */
|
|
54
|
+
fingers = /* @__PURE__ */ new Map();
|
|
55
|
+
/** When the first finger touched down */
|
|
56
|
+
startTime = 0;
|
|
57
|
+
/** Most fingers down at once */
|
|
58
|
+
most = 0;
|
|
59
|
+
/** Whether any finger moved too far to tap */
|
|
60
|
+
moved = false;
|
|
61
|
+
/** Whether a touch was cancelled */
|
|
62
|
+
cancelled = false;
|
|
63
|
+
/** The three fingers' positions when the third touched down, and when the first of them lifted */
|
|
64
|
+
swipe;
|
|
65
|
+
/** How many fingers are on the screen */
|
|
66
|
+
get count() {
|
|
67
|
+
return this.fingers.size;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Whether any finger moved further than a tap allows since touching down.
|
|
71
|
+
*/
|
|
72
|
+
get dragging() {
|
|
73
|
+
return this.moved;
|
|
74
|
+
}
|
|
75
|
+
/** A finger touched down */
|
|
76
|
+
down(id, point, time) {
|
|
77
|
+
if (this.fingers.size === 0) this.reset(time);
|
|
78
|
+
this.fingers.set(id, {
|
|
79
|
+
start: point,
|
|
80
|
+
now: point
|
|
81
|
+
});
|
|
82
|
+
this.most = Math.max(this.most, this.fingers.size);
|
|
83
|
+
if (this.fingers.size === 3) this.swipe = { from: [...this.fingers.values()].map((f) => f.now) };
|
|
84
|
+
}
|
|
85
|
+
/** A finger moved */
|
|
86
|
+
move(id, point) {
|
|
87
|
+
const finger = this.fingers.get(id);
|
|
88
|
+
if (!finger) return;
|
|
89
|
+
finger.now = point;
|
|
90
|
+
if (Math.hypot(point.x - finger.start.x, point.y - finger.start.y) > 10) this.moved = true;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* A finger lifted. Returns the gesture made once the last finger lifts, if any.
|
|
94
|
+
*/
|
|
95
|
+
up(id, time) {
|
|
96
|
+
if (!this.fingers.has(id)) return void 0;
|
|
97
|
+
if (this.swipe && !this.swipe.to) this.swipe.to = [...this.fingers.values()].map((f) => f.now);
|
|
98
|
+
this.fingers.delete(id);
|
|
99
|
+
if (this.fingers.size > 0 || this.cancelled || this.most > 3) return void 0;
|
|
100
|
+
if (this.most === 3 && this.swipe?.to) {
|
|
101
|
+
const swipe = swipeOf(this.swipe.from, this.swipe.to);
|
|
102
|
+
if (swipe) return swipe;
|
|
103
|
+
}
|
|
104
|
+
if (this.moved || time - this.startTime > 300) return void 0;
|
|
105
|
+
if (this.most === 2) return "tap2";
|
|
106
|
+
if (this.most === 3) return "tap3";
|
|
107
|
+
}
|
|
108
|
+
/** A touch was cancelled: no gesture until all fingers have lifted */
|
|
109
|
+
cancel(id) {
|
|
110
|
+
if (!this.fingers.delete(id)) return;
|
|
111
|
+
this.cancelled = true;
|
|
112
|
+
}
|
|
113
|
+
/** Start tracking a new gesture */
|
|
114
|
+
reset(time) {
|
|
115
|
+
this.startTime = time;
|
|
116
|
+
this.most = 0;
|
|
117
|
+
this.moved = false;
|
|
118
|
+
this.cancelled = false;
|
|
119
|
+
this.swipe = void 0;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
export { SWIPE_MIN_PX, TAP_MAX_MS, TAP_SLOP_PX, GestureTracker as default, swipeOf };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/** Maximum number of actions that can be undone */
|
|
2
|
+
export declare const MAX_HISTORY_ENTRIES = 20;
|
|
3
|
+
/** Event dispatched after a step is undone (detail: `HistoryStepDetail`) */
|
|
4
|
+
export declare const UNDO_EVENT = "undo";
|
|
5
|
+
/** Event dispatched after a step is redone (detail: `HistoryStepDetail`) */
|
|
6
|
+
export declare const REDO_EVENT = "redo";
|
|
7
|
+
/** Details of an undo or redo event: the step undone or redone */
|
|
8
|
+
export interface HistoryStepDetail {
|
|
9
|
+
step: string;
|
|
10
|
+
}
|
|
11
|
+
/** An action that can be undone and redone */
|
|
12
|
+
export interface HistoryEntry {
|
|
13
|
+
/** Put things back as they were before the action */
|
|
14
|
+
undo(): void;
|
|
15
|
+
/** Apply the action again after it was undone */
|
|
16
|
+
redo(): void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A timeline of the user's actions. Undo moves back along it and redo moves forward again. Recording an action after
|
|
20
|
+
* undoing removes the undone actions. Only the latest `limit` actions are kept.
|
|
21
|
+
*
|
|
22
|
+
* Actions recorded while an entry is being undone or redone (or inside `untracked`) are ignored, so entries can use
|
|
23
|
+
* the same methods that record actions. Each recorded step gets an ID. Dispatches `change` when what can be undone or
|
|
24
|
+
* redone changes, and `undo` or `redo` (with the step's ID) after a step is undone or redone.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* const history = new History();
|
|
28
|
+
* history.push({ undo: () => (layer.opacity = 1), redo: () => (layer.opacity = 0.5) });
|
|
29
|
+
* history.undo(); // layer.opacity is 1
|
|
30
|
+
* history.redo(); // layer.opacity is 0.5
|
|
31
|
+
*/
|
|
32
|
+
export default class History extends EventTarget {
|
|
33
|
+
/** Maximum number of entries kept */
|
|
34
|
+
readonly limit: number;
|
|
35
|
+
private entries;
|
|
36
|
+
/** Number of entries applied (the rest can be redone) */
|
|
37
|
+
private applied;
|
|
38
|
+
/** Each recorded step's ID */
|
|
39
|
+
private readonly steps;
|
|
40
|
+
private paused;
|
|
41
|
+
constructor(limit?: number);
|
|
42
|
+
/** Number of recorded steps (done and undone), at most `limit` */
|
|
43
|
+
get size(): number;
|
|
44
|
+
/** Whether there is an action to undo */
|
|
45
|
+
get canUndo(): boolean;
|
|
46
|
+
/** Whether there is an undone action to redo */
|
|
47
|
+
get canRedo(): boolean;
|
|
48
|
+
/** Whether actions are being recorded (not while undoing, redoing or inside `untracked`) */
|
|
49
|
+
get recording(): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Record an action that has just been applied. Removes any undone actions, and the oldest action when over the limit.
|
|
52
|
+
*/
|
|
53
|
+
push(entry: HistoryEntry): void;
|
|
54
|
+
/**
|
|
55
|
+
* Undo the latest applied action. Returns whether there was one.
|
|
56
|
+
*/
|
|
57
|
+
undo(): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Redo the next undone action. Returns whether there was one.
|
|
60
|
+
*/
|
|
61
|
+
redo(): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Forget all actions (e.g. when the canvas is replaced).
|
|
64
|
+
*/
|
|
65
|
+
clear(): void;
|
|
66
|
+
/**
|
|
67
|
+
* Tell listeners that what can be undone or redone may have changed.
|
|
68
|
+
*/
|
|
69
|
+
/**
|
|
70
|
+
* The ID of the latest recorded step (undefined when there are none), e.g. to tell others which step a stroke made.
|
|
71
|
+
*/
|
|
72
|
+
get latestStep(): string | undefined;
|
|
73
|
+
/** Dispatch an undo or redo event for a step */
|
|
74
|
+
private announce;
|
|
75
|
+
private notify;
|
|
76
|
+
/**
|
|
77
|
+
* Run a function without recording the actions it applies. Returns the function's result.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* history.untracked(() => stack.create()); // not undoable
|
|
81
|
+
*/
|
|
82
|
+
untracked<T>(run: () => T): T;
|
|
83
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// src/lib/classes/History/index.ts
|
|
2
|
+
/** Maximum number of actions that can be undone */
|
|
3
|
+
var MAX_HISTORY_ENTRIES = 20;
|
|
4
|
+
/** Event dispatched after a step is undone (detail: `HistoryStepDetail`) */
|
|
5
|
+
var UNDO_EVENT = "undo";
|
|
6
|
+
/** Event dispatched after a step is redone (detail: `HistoryStepDetail`) */
|
|
7
|
+
var REDO_EVENT = "redo";
|
|
8
|
+
/**
|
|
9
|
+
* A timeline of the user's actions. Undo moves back along it and redo moves forward again. Recording an action after
|
|
10
|
+
* undoing removes the undone actions. Only the latest `limit` actions are kept.
|
|
11
|
+
*
|
|
12
|
+
* Actions recorded while an entry is being undone or redone (or inside `untracked`) are ignored, so entries can use
|
|
13
|
+
* the same methods that record actions. Each recorded step gets an ID. Dispatches `change` when what can be undone or
|
|
14
|
+
* redone changes, and `undo` or `redo` (with the step's ID) after a step is undone or redone.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* const history = new History();
|
|
18
|
+
* history.push({ undo: () => (layer.opacity = 1), redo: () => (layer.opacity = 0.5) });
|
|
19
|
+
* history.undo(); // layer.opacity is 1
|
|
20
|
+
* history.redo(); // layer.opacity is 0.5
|
|
21
|
+
*/
|
|
22
|
+
var History = class extends EventTarget {
|
|
23
|
+
/** Maximum number of entries kept */
|
|
24
|
+
limit;
|
|
25
|
+
entries = [];
|
|
26
|
+
/** Number of entries applied (the rest can be redone) */
|
|
27
|
+
applied = 0;
|
|
28
|
+
/** Each recorded step's ID */
|
|
29
|
+
steps = /* @__PURE__ */ new WeakMap();
|
|
30
|
+
paused = false;
|
|
31
|
+
constructor(limit = 20) {
|
|
32
|
+
super();
|
|
33
|
+
this.limit = limit;
|
|
34
|
+
}
|
|
35
|
+
/** Number of recorded steps (done and undone), at most `limit` */
|
|
36
|
+
get size() {
|
|
37
|
+
return this.entries.length;
|
|
38
|
+
}
|
|
39
|
+
/** Whether there is an action to undo */
|
|
40
|
+
get canUndo() {
|
|
41
|
+
return this.applied > 0;
|
|
42
|
+
}
|
|
43
|
+
/** Whether there is an undone action to redo */
|
|
44
|
+
get canRedo() {
|
|
45
|
+
return this.applied < this.entries.length;
|
|
46
|
+
}
|
|
47
|
+
/** Whether actions are being recorded (not while undoing, redoing or inside `untracked`) */
|
|
48
|
+
get recording() {
|
|
49
|
+
return !this.paused;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Record an action that has just been applied. Removes any undone actions, and the oldest action when over the limit.
|
|
53
|
+
*/
|
|
54
|
+
push(entry) {
|
|
55
|
+
if (this.paused) return;
|
|
56
|
+
this.steps.set(entry, crypto.randomUUID());
|
|
57
|
+
this.entries = [...this.entries.slice(0, this.applied), entry].slice(-this.limit);
|
|
58
|
+
this.applied = this.entries.length;
|
|
59
|
+
this.notify();
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Undo the latest applied action. Returns whether there was one.
|
|
63
|
+
*/
|
|
64
|
+
undo() {
|
|
65
|
+
if (!this.canUndo) return false;
|
|
66
|
+
this.applied -= 1;
|
|
67
|
+
const entry = this.entries[this.applied];
|
|
68
|
+
this.untracked(() => entry.undo());
|
|
69
|
+
this.notify();
|
|
70
|
+
this.announce(UNDO_EVENT, entry);
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Redo the next undone action. Returns whether there was one.
|
|
75
|
+
*/
|
|
76
|
+
redo() {
|
|
77
|
+
if (!this.canRedo) return false;
|
|
78
|
+
const entry = this.entries[this.applied];
|
|
79
|
+
this.applied += 1;
|
|
80
|
+
this.untracked(() => entry.redo());
|
|
81
|
+
this.notify();
|
|
82
|
+
this.announce(REDO_EVENT, entry);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Forget all actions (e.g. when the canvas is replaced).
|
|
87
|
+
*/
|
|
88
|
+
clear() {
|
|
89
|
+
this.entries = [];
|
|
90
|
+
this.applied = 0;
|
|
91
|
+
this.notify();
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Tell listeners that what can be undone or redone may have changed.
|
|
95
|
+
*/
|
|
96
|
+
/**
|
|
97
|
+
* The ID of the latest recorded step (undefined when there are none), e.g. to tell others which step a stroke made.
|
|
98
|
+
*/
|
|
99
|
+
get latestStep() {
|
|
100
|
+
const entry = this.entries.at(-1);
|
|
101
|
+
return entry && this.steps.get(entry);
|
|
102
|
+
}
|
|
103
|
+
/** Dispatch an undo or redo event for a step */
|
|
104
|
+
announce(type, entry) {
|
|
105
|
+
this.dispatchEvent(new CustomEvent(type, { detail: { step: this.steps.get(entry) } }));
|
|
106
|
+
}
|
|
107
|
+
notify() {
|
|
108
|
+
this.dispatchEvent(new Event("change"));
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Run a function without recording the actions it applies. Returns the function's result.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* history.untracked(() => stack.create()); // not undoable
|
|
115
|
+
*/
|
|
116
|
+
untracked(run) {
|
|
117
|
+
const paused = this.paused;
|
|
118
|
+
this.paused = true;
|
|
119
|
+
try {
|
|
120
|
+
return run();
|
|
121
|
+
} finally {
|
|
122
|
+
this.paused = paused;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
export { MAX_HISTORY_ENTRIES, REDO_EVENT, UNDO_EVENT, History as default };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** History state of the entry the guard adds */
|
|
2
|
+
export declare const GUARD_STATE: {
|
|
3
|
+
readonly paintcanvasGuard: true;
|
|
4
|
+
};
|
|
5
|
+
/** The parts of `window` the guard uses (replaceable for tests) */
|
|
6
|
+
export type GuardWindow = Pick<Window, "history" | "location" | "addEventListener" | "removeEventListener">;
|
|
7
|
+
/**
|
|
8
|
+
* Whether a history state is the guard's entry.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* isGuardState(GUARD_STATE); // true
|
|
12
|
+
* isGuardState(null); // false
|
|
13
|
+
*/
|
|
14
|
+
export declare const isGuardState: (state: unknown) => boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Keeps going back (e.g. swiping from the screen's edge) from leaving the page while it guards: it adds a history entry
|
|
17
|
+
* for the page, and when the browser goes back from it, puts it back and calls `onBack` (to ask before leaving).
|
|
18
|
+
* Releasing goes back to the page's own entry, keeping the address the page shows.
|
|
19
|
+
*
|
|
20
|
+
* Call `guard` whenever the page's address may have changed while guarding (it remembers the address to show), and
|
|
21
|
+
* `release` once leaving is safe.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* const guard = new HistoryGuard(() => askBeforeLeaving());
|
|
25
|
+
* guard.attach(window);
|
|
26
|
+
* guard.guard(); // a canvas is open
|
|
27
|
+
* guard.release(); // the canvas closed
|
|
28
|
+
*/
|
|
29
|
+
export default class HistoryGuard {
|
|
30
|
+
private readonly onBack;
|
|
31
|
+
private target?;
|
|
32
|
+
/** Whether the page wants guarding */
|
|
33
|
+
private wanted;
|
|
34
|
+
/** Whether the guard's entry was added (and not yet released) */
|
|
35
|
+
private guarded;
|
|
36
|
+
/** The address the guard's entry shows */
|
|
37
|
+
private url;
|
|
38
|
+
/** While releasing: the address to keep once back on the page's own entry */
|
|
39
|
+
private releasing?;
|
|
40
|
+
constructor(onBack: () => void);
|
|
41
|
+
/**
|
|
42
|
+
* Start listening for the browser going back.
|
|
43
|
+
*/
|
|
44
|
+
attach(target: GuardWindow): void;
|
|
45
|
+
/**
|
|
46
|
+
* Stop listening (the guard's entry, if any, stays).
|
|
47
|
+
*/
|
|
48
|
+
detach(): void;
|
|
49
|
+
/**
|
|
50
|
+
* Guard the page: add the guard's entry if it isn't there, and remember the address it shows.
|
|
51
|
+
*/
|
|
52
|
+
guard(): void;
|
|
53
|
+
/**
|
|
54
|
+
* Stop guarding: go back to the page's own entry, keeping the address the page shows now.
|
|
55
|
+
*/
|
|
56
|
+
release(): void;
|
|
57
|
+
private readonly onPopState;
|
|
58
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// src/lib/classes/HistoryGuard/index.ts
|
|
2
|
+
/** History state of the entry the guard adds */
|
|
3
|
+
var GUARD_STATE = { paintcanvasGuard: true };
|
|
4
|
+
/**
|
|
5
|
+
* Whether a history state is the guard's entry.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* isGuardState(GUARD_STATE); // true
|
|
9
|
+
* isGuardState(null); // false
|
|
10
|
+
*/
|
|
11
|
+
var isGuardState = (state) => typeof state === "object" && state !== null && state.paintcanvasGuard === true;
|
|
12
|
+
/**
|
|
13
|
+
* Keeps going back (e.g. swiping from the screen's edge) from leaving the page while it guards: it adds a history entry
|
|
14
|
+
* for the page, and when the browser goes back from it, puts it back and calls `onBack` (to ask before leaving).
|
|
15
|
+
* Releasing goes back to the page's own entry, keeping the address the page shows.
|
|
16
|
+
*
|
|
17
|
+
* Call `guard` whenever the page's address may have changed while guarding (it remembers the address to show), and
|
|
18
|
+
* `release` once leaving is safe.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* const guard = new HistoryGuard(() => askBeforeLeaving());
|
|
22
|
+
* guard.attach(window);
|
|
23
|
+
* guard.guard(); // a canvas is open
|
|
24
|
+
* guard.release(); // the canvas closed
|
|
25
|
+
*/
|
|
26
|
+
var HistoryGuard = class {
|
|
27
|
+
onBack;
|
|
28
|
+
target;
|
|
29
|
+
/** Whether the page wants guarding */
|
|
30
|
+
wanted = false;
|
|
31
|
+
/** Whether the guard's entry was added (and not yet released) */
|
|
32
|
+
guarded = false;
|
|
33
|
+
/** The address the guard's entry shows */
|
|
34
|
+
url = "";
|
|
35
|
+
/** While releasing: the address to keep once back on the page's own entry */
|
|
36
|
+
releasing;
|
|
37
|
+
constructor(onBack) {
|
|
38
|
+
this.onBack = onBack;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Start listening for the browser going back.
|
|
42
|
+
*/
|
|
43
|
+
attach(target) {
|
|
44
|
+
this.detach();
|
|
45
|
+
this.target = target;
|
|
46
|
+
target.addEventListener("popstate", this.onPopState);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Stop listening (the guard's entry, if any, stays).
|
|
50
|
+
*/
|
|
51
|
+
detach() {
|
|
52
|
+
this.target?.removeEventListener("popstate", this.onPopState);
|
|
53
|
+
this.target = void 0;
|
|
54
|
+
this.guarded = false;
|
|
55
|
+
this.wanted = false;
|
|
56
|
+
this.releasing = void 0;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Guard the page: add the guard's entry if it isn't there, and remember the address it shows.
|
|
60
|
+
*/
|
|
61
|
+
guard() {
|
|
62
|
+
const target = this.target;
|
|
63
|
+
if (!target) return;
|
|
64
|
+
this.wanted = true;
|
|
65
|
+
if (this.releasing !== void 0) return;
|
|
66
|
+
if (!this.guarded) {
|
|
67
|
+
this.guarded = true;
|
|
68
|
+
target.history.pushState(GUARD_STATE, "", target.location.href);
|
|
69
|
+
}
|
|
70
|
+
if (isGuardState(target.history.state)) this.url = target.location.href;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Stop guarding: go back to the page's own entry, keeping the address the page shows now.
|
|
74
|
+
*/
|
|
75
|
+
release() {
|
|
76
|
+
const target = this.target;
|
|
77
|
+
this.wanted = false;
|
|
78
|
+
if (!target || !this.guarded) return;
|
|
79
|
+
this.guarded = false;
|
|
80
|
+
if (!isGuardState(target.history.state)) return;
|
|
81
|
+
this.releasing = target.location.href;
|
|
82
|
+
target.history.back();
|
|
83
|
+
}
|
|
84
|
+
onPopState = (event) => {
|
|
85
|
+
const target = this.target;
|
|
86
|
+
if (this.releasing !== void 0) {
|
|
87
|
+
target.history.replaceState(null, "", this.releasing);
|
|
88
|
+
this.releasing = void 0;
|
|
89
|
+
if (this.wanted) this.guard();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (!this.guarded || isGuardState(event.state)) return;
|
|
93
|
+
target.history.pushState(GUARD_STATE, "", this.url);
|
|
94
|
+
this.onBack();
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
export { GUARD_STATE, HistoryGuard as default, isGuardState };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type GestureSettings } from "../../utils/gestures";
|
|
2
|
+
/** How the user's input devices control the canvas */
|
|
3
|
+
export interface InputSettingsData {
|
|
4
|
+
/** Two-finger scroll (or mouse wheel) pans the canvas */
|
|
5
|
+
scrollPan: boolean;
|
|
6
|
+
/** Pinch zooms the canvas */
|
|
7
|
+
pinchZoom: boolean;
|
|
8
|
+
/** Cmd/Ctrl + scroll zooms the canvas (desktop only) */
|
|
9
|
+
modifierScrollZoom: boolean;
|
|
10
|
+
/** Which touch gesture undoes, redoes, copies and pastes (touch devices only) */
|
|
11
|
+
gestures: GestureSettings;
|
|
12
|
+
/** Focus pen mode: fingers and palms never draw, and touches are ignored while a pen is down or just lifted */
|
|
13
|
+
focusPen: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** Names of all input settings, e.g. to pick them out of a larger settings object */
|
|
16
|
+
export declare const INPUT_SETTING_KEYS: readonly ["scrollPan", "pinchZoom", "modifierScrollZoom", "focusPen", "gestures"];
|
|
17
|
+
/**
|
|
18
|
+
* Default input settings: all navigation on, the default touch gestures, focus pen mode off.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* defaultInputSettings().focusPen; // false
|
|
22
|
+
*/
|
|
23
|
+
export declare const defaultInputSettings: () => InputSettingsData;
|
|
24
|
+
/**
|
|
25
|
+
* Keep only valid input settings from an untrusted value (e.g. stored or imported), using `defaults` for anything
|
|
26
|
+
* missing or invalid. Other properties are left out.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* sanitiseInputSettings({ focusPen: true, scrollPan: "yes" }, defaultInputSettings()); // focusPen on, scrollPan from defaults
|
|
30
|
+
*/
|
|
31
|
+
export declare const sanitiseInputSettings: (input: unknown, defaults: InputSettingsData) => InputSettingsData;
|
|
32
|
+
/**
|
|
33
|
+
* Whether the device has no fine pointer (mouse or trackpad), i.e. it is a phone or tablet. Without `matchMedia`, the
|
|
34
|
+
* device is treated as a desktop.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* isTouchDevice(); // false on a laptop
|
|
38
|
+
*/
|
|
39
|
+
export declare const isTouchDevice: (matchMedia?: ((query: string) => {
|
|
40
|
+
matches: boolean;
|
|
41
|
+
}) | undefined) => boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Name of the key held to zoom with the scroll wheel: "Cmd" on Apple devices, "Ctrl" elsewhere.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* zoomModifierName("MacIntel"); // "Cmd"
|
|
47
|
+
* zoomModifierName("Win32"); // "Ctrl"
|
|
48
|
+
*/
|
|
49
|
+
export declare const zoomModifierName: (platform?: string) => "Cmd" | "Ctrl";
|
|
50
|
+
/**
|
|
51
|
+
* How the user's input devices control the canvas: scrolling, pinching, touch gestures and focus pen mode. Kept in
|
|
52
|
+
* memory; the implementer saves and restores them (see `update` and the `change` event).
|
|
53
|
+
*
|
|
54
|
+
* Dispatches a `change` event when a setting changes.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* const input = new InputSettings();
|
|
58
|
+
* input.update({ focusPen: true });
|
|
59
|
+
* input.data.focusPen; // true
|
|
60
|
+
*/
|
|
61
|
+
export default class InputSettings extends EventTarget {
|
|
62
|
+
private values;
|
|
63
|
+
constructor(initial?: unknown);
|
|
64
|
+
/** Current settings (read-only copy) */
|
|
65
|
+
get data(): Readonly<InputSettingsData>;
|
|
66
|
+
/**
|
|
67
|
+
* Change some settings and dispatch `change` if anything changed. Invalid values and other properties are ignored, so
|
|
68
|
+
* a larger settings object can be passed.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* input.update({ pinchZoom: false });
|
|
72
|
+
*/
|
|
73
|
+
update(changes: Partial<InputSettingsData>): void;
|
|
74
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { DEFAULT_GESTURES, sanitiseGestures } from "../../utils/gestures/index.js";
|
|
2
|
+
// src/lib/classes/InputSettings/index.ts
|
|
3
|
+
/** The on/off input settings */
|
|
4
|
+
var INPUT_TOGGLES = [
|
|
5
|
+
"scrollPan",
|
|
6
|
+
"pinchZoom",
|
|
7
|
+
"modifierScrollZoom",
|
|
8
|
+
"focusPen"
|
|
9
|
+
];
|
|
10
|
+
/** Names of all input settings, e.g. to pick them out of a larger settings object */
|
|
11
|
+
var INPUT_SETTING_KEYS = [...INPUT_TOGGLES, "gestures"];
|
|
12
|
+
/**
|
|
13
|
+
* Default input settings: all navigation on, the default touch gestures, focus pen mode off.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* defaultInputSettings().focusPen; // false
|
|
17
|
+
*/
|
|
18
|
+
var defaultInputSettings = () => ({
|
|
19
|
+
scrollPan: true,
|
|
20
|
+
pinchZoom: true,
|
|
21
|
+
modifierScrollZoom: true,
|
|
22
|
+
gestures: { ...DEFAULT_GESTURES },
|
|
23
|
+
focusPen: false
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* Keep only valid input settings from an untrusted value (e.g. stored or imported), using `defaults` for anything
|
|
27
|
+
* missing or invalid. Other properties are left out.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* sanitiseInputSettings({ focusPen: true, scrollPan: "yes" }, defaultInputSettings()); // focusPen on, scrollPan from defaults
|
|
31
|
+
*/
|
|
32
|
+
var sanitiseInputSettings = (input, defaults) => {
|
|
33
|
+
const result = {
|
|
34
|
+
...defaults,
|
|
35
|
+
gestures: { ...defaults.gestures }
|
|
36
|
+
};
|
|
37
|
+
if (typeof input !== "object" || input === null) return result;
|
|
38
|
+
const value = input;
|
|
39
|
+
for (const key of INPUT_TOGGLES) if (typeof value[key] === "boolean") result[key] = value[key];
|
|
40
|
+
if ("gestures" in value) result.gestures = sanitiseGestures(value.gestures);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Whether two sets of input settings are the same.
|
|
45
|
+
*/
|
|
46
|
+
var sameInputSettings = (a, b) => INPUT_TOGGLES.every((key) => a[key] === b[key]) && JSON.stringify(a.gestures) === JSON.stringify(b.gestures);
|
|
47
|
+
/**
|
|
48
|
+
* Whether the device has no fine pointer (mouse or trackpad), i.e. it is a phone or tablet. Without `matchMedia`, the
|
|
49
|
+
* device is treated as a desktop.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* isTouchDevice(); // false on a laptop
|
|
53
|
+
*/
|
|
54
|
+
var isTouchDevice = (matchMedia = typeof window.matchMedia === "function" ? (query) => window.matchMedia(query) : void 0) => matchMedia ? !matchMedia("(any-pointer: fine)").matches : false;
|
|
55
|
+
/**
|
|
56
|
+
* Name of the key held to zoom with the scroll wheel: "Cmd" on Apple devices, "Ctrl" elsewhere.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* zoomModifierName("MacIntel"); // "Cmd"
|
|
60
|
+
* zoomModifierName("Win32"); // "Ctrl"
|
|
61
|
+
*/
|
|
62
|
+
var zoomModifierName = (platform = navigator.platform) => /Mac|iPhone|iPad|iPod/.test(platform) ? "Cmd" : "Ctrl";
|
|
63
|
+
/**
|
|
64
|
+
* How the user's input devices control the canvas: scrolling, pinching, touch gestures and focus pen mode. Kept in
|
|
65
|
+
* memory; the implementer saves and restores them (see `update` and the `change` event).
|
|
66
|
+
*
|
|
67
|
+
* Dispatches a `change` event when a setting changes.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* const input = new InputSettings();
|
|
71
|
+
* input.update({ focusPen: true });
|
|
72
|
+
* input.data.focusPen; // true
|
|
73
|
+
*/
|
|
74
|
+
var InputSettings = class extends EventTarget {
|
|
75
|
+
values;
|
|
76
|
+
constructor(initial) {
|
|
77
|
+
super();
|
|
78
|
+
this.values = sanitiseInputSettings(initial, defaultInputSettings());
|
|
79
|
+
}
|
|
80
|
+
/** Current settings (read-only copy) */
|
|
81
|
+
get data() {
|
|
82
|
+
return this.values;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Change some settings and dispatch `change` if anything changed. Invalid values and other properties are ignored, so
|
|
86
|
+
* a larger settings object can be passed.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* input.update({ pinchZoom: false });
|
|
90
|
+
*/
|
|
91
|
+
update(changes) {
|
|
92
|
+
const next = sanitiseInputSettings({
|
|
93
|
+
...this.values,
|
|
94
|
+
...changes
|
|
95
|
+
}, this.values);
|
|
96
|
+
if (sameInputSettings(next, this.values)) return;
|
|
97
|
+
this.values = next;
|
|
98
|
+
this.dispatchEvent(new Event("change"));
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
export { INPUT_SETTING_KEYS, InputSettings as default, defaultInputSettings, isTouchDevice, sanitiseInputSettings, zoomModifierName };
|