@reffy/infinite-canvas 0.0.10 → 0.0.12
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/assets/index-KcoKIUJZ.js +281 -0
- package/dist/index.html +54 -0
- package/esm/Canvas.d.ts.map +1 -1
- package/esm/Canvas.js +104 -113
- package/esm/Canvas.js.map +1 -1
- package/esm/Component.d.ts.map +1 -1
- package/esm/Component.js +274 -304
- package/esm/Component.js.map +1 -1
- package/esm/manager/ContextMenu.js +4 -17
- package/esm/manager/ContextMenu.js.map +1 -1
- package/esm/manager/Selection.js +87 -100
- package/esm/manager/Selection.js.map +1 -1
- package/esm/serializer/serializer.d.ts.map +1 -1
- package/esm/serializer/serializer.js +11 -5
- package/esm/serializer/serializer.js.map +1 -1
- package/esm/shapes/Shape.js +1 -1
- package/esm/shapes/Shape.js.map +1 -1
- package/lib/Canvas.d.ts.map +1 -1
- package/lib/Canvas.js +104 -113
- package/lib/Canvas.js.map +1 -1
- package/lib/Component.d.ts.map +1 -1
- package/lib/Component.js +274 -304
- package/lib/Component.js.map +1 -1
- package/lib/manager/ContextMenu.js +4 -17
- package/lib/manager/ContextMenu.js.map +1 -1
- package/lib/manager/Selection.js +87 -100
- package/lib/manager/Selection.js.map +1 -1
- package/lib/serializer/serializer.d.ts.map +1 -1
- package/lib/serializer/serializer.js +11 -5
- package/lib/serializer/serializer.js.map +1 -1
- package/lib/shapes/Shape.js +1 -1
- package/lib/shapes/Shape.js.map +1 -1
- package/package.json +3 -2
- package/dist/index.js +0 -4110
- package/dist/index.js.map +0 -1
- package/dist/index.umd.js +0 -276
- package/dist/index.umd.js.map +0 -1
package/lib/Component.js
CHANGED
|
@@ -5,21 +5,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
6
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
7
|
};
|
|
8
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
-
};
|
|
13
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
14
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
15
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
16
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
17
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
18
|
-
};
|
|
19
8
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
20
9
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
21
10
|
};
|
|
22
|
-
var _InfiniteCanvasElement_canvas, _InfiniteCanvasElement_eventHub, _InfiniteCanvasElement_resizeObserver, _InfiniteCanvasElement_history, _InfiniteCanvasElement_fileStorage, _InfiniteCanvasElement_canvasStorage, _InfiniteCanvasElement_saveFrequency, _InfiniteCanvasElement_timeoutId, _InfiniteCanvasElement_intervalId, _InfiniteCanvasElement_rootDiv, _InfiniteCanvasElement_onChange, _InfiniteCanvasElement_singleImageMenuOptions, _InfiniteCanvasElement_multiImageMenuOptions, _InfiniteCanvasElement_canvasImageMenuOptions;
|
|
23
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
12
|
exports.InfiniteCanvasElement = void 0;
|
|
25
13
|
const history_1 = require("./history");
|
|
@@ -34,7 +22,6 @@ const contextMenu_1 = require("./contextMenu");
|
|
|
34
22
|
const storage_1 = require("./storage");
|
|
35
23
|
const eventemitter3_1 = __importDefault(require("eventemitter3"));
|
|
36
24
|
const loader_1 = require("./loader");
|
|
37
|
-
const stats_js_1 = __importDefault(require("stats.js"));
|
|
38
25
|
let InfiniteCanvasElement = class InfiniteCanvasElement extends lit_1.LitElement {
|
|
39
26
|
constructor() {
|
|
40
27
|
super(...arguments);
|
|
@@ -42,20 +29,7 @@ let InfiniteCanvasElement = class InfiniteCanvasElement extends lit_1.LitElement
|
|
|
42
29
|
this.name = "Reffy";
|
|
43
30
|
/** There are two acceptable options: fullscreen (default) and windowed. Fullscreen mode will ignore the width and height you inputed. */
|
|
44
31
|
this.displayMode = "fullscreen";
|
|
45
|
-
|
|
46
|
-
_InfiniteCanvasElement_eventHub.set(this, void 0);
|
|
47
|
-
_InfiniteCanvasElement_resizeObserver.set(this, void 0);
|
|
48
|
-
_InfiniteCanvasElement_history.set(this, void 0);
|
|
49
|
-
_InfiniteCanvasElement_fileStorage.set(this, void 0);
|
|
50
|
-
_InfiniteCanvasElement_canvasStorage.set(this, void 0);
|
|
51
|
-
_InfiniteCanvasElement_saveFrequency.set(this, 300000);
|
|
52
|
-
_InfiniteCanvasElement_timeoutId.set(this, void 0);
|
|
53
|
-
_InfiniteCanvasElement_intervalId.set(this, void 0);
|
|
54
|
-
_InfiniteCanvasElement_rootDiv.set(this, void 0);
|
|
55
|
-
_InfiniteCanvasElement_onChange.set(this, void 0);
|
|
56
|
-
_InfiniteCanvasElement_singleImageMenuOptions.set(this, void 0);
|
|
57
|
-
_InfiniteCanvasElement_multiImageMenuOptions.set(this, void 0);
|
|
58
|
-
_InfiniteCanvasElement_canvasImageMenuOptions.set(this, void 0);
|
|
32
|
+
this.#saveFrequency = 300000;
|
|
59
33
|
// Global helper
|
|
60
34
|
this.handleGlobalPointerDown = (e) => {
|
|
61
35
|
if (!this.contains(e.target) &&
|
|
@@ -64,29 +38,179 @@ let InfiniteCanvasElement = class InfiniteCanvasElement extends lit_1.LitElement
|
|
|
64
38
|
}
|
|
65
39
|
};
|
|
66
40
|
}
|
|
41
|
+
static { this.properties = {
|
|
42
|
+
name: { type: String, reflect: true },
|
|
43
|
+
width: { type: String, reflect: true },
|
|
44
|
+
height: { type: String, reflect: true },
|
|
45
|
+
displayMode: { type: String, reflect: true },
|
|
46
|
+
}; }
|
|
47
|
+
static { this.styles = (0, lit_1.css) `
|
|
48
|
+
:host {
|
|
49
|
+
position: relative;
|
|
50
|
+
overflow: hidden;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.context-menu {
|
|
54
|
+
position: absolute;
|
|
55
|
+
background: white;
|
|
56
|
+
min-width: 180px;
|
|
57
|
+
background: var(--menu-bg, #fff);
|
|
58
|
+
border-radius: 6px;
|
|
59
|
+
border: 1px solid var(--menu-border, #9f9f9fff);
|
|
60
|
+
box-sizing: border-box;
|
|
61
|
+
padding: 6px 0;
|
|
62
|
+
display: flex;
|
|
63
|
+
gap: 2px;
|
|
64
|
+
flex-direction: column;
|
|
65
|
+
font-family: system-ui, sans-serif;
|
|
66
|
+
animation: fadeInMenu 0.13s cubic-bezier(0.4, 0, 0.2, 1);
|
|
67
|
+
overflow: scroll;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@keyframes fadeInMenu {
|
|
71
|
+
from {
|
|
72
|
+
opacity: 0;
|
|
73
|
+
transform: translateY(8px);
|
|
74
|
+
}
|
|
75
|
+
to {
|
|
76
|
+
opacity: 1;
|
|
77
|
+
transform: none;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.context-menu button {
|
|
82
|
+
all: unset;
|
|
83
|
+
display: flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
box-sizing: border-box;
|
|
86
|
+
width: 100%;
|
|
87
|
+
padding: 8px 18px;
|
|
88
|
+
font-size: 15px;
|
|
89
|
+
color: var(--menu-fg, #222);
|
|
90
|
+
background: none;
|
|
91
|
+
cursor: pointer;
|
|
92
|
+
transition:
|
|
93
|
+
background 0.1s,
|
|
94
|
+
color 0.1s;
|
|
95
|
+
user-select: none;
|
|
96
|
+
outline: none;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.context-menu button:hover,
|
|
100
|
+
.context-menu button:focus-visible {
|
|
101
|
+
background: var(--menu-hover, #c7d5eaff);
|
|
102
|
+
color: var(--menu-accent, #155290ff);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.context-menu button:active {
|
|
106
|
+
background: var(--menu-active, #e3eaf3);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.context-menu button[disabled] {
|
|
110
|
+
color: #aaa;
|
|
111
|
+
cursor: not-allowed;
|
|
112
|
+
background: none;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.context-menu-divider {
|
|
116
|
+
height: 1px;
|
|
117
|
+
background: var(--menu-divider, #c7d5eaff);
|
|
118
|
+
margin: 6px 12px;
|
|
119
|
+
border: none;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
canvas {
|
|
123
|
+
width: 100%;
|
|
124
|
+
height: 100%;
|
|
125
|
+
outline: none;
|
|
126
|
+
padding: 0;
|
|
127
|
+
margin: 0;
|
|
128
|
+
touch-action: none;
|
|
129
|
+
display: block;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.canvas-loader {
|
|
133
|
+
position: absolute;
|
|
134
|
+
top: 0;
|
|
135
|
+
left: 0;
|
|
136
|
+
display: flex;
|
|
137
|
+
flex-direction: column;
|
|
138
|
+
align-items: center;
|
|
139
|
+
justify-content: center;
|
|
140
|
+
background: rgba(255, 255, 255, 0.7);
|
|
141
|
+
z-index: 1000;
|
|
142
|
+
pointer-events: all;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.canvas-loader-spinner {
|
|
146
|
+
width: 48px;
|
|
147
|
+
height: 48px;
|
|
148
|
+
border: 6px solid #e0e0e0;
|
|
149
|
+
border-top: 6px solid #1976d2;
|
|
150
|
+
border-radius: 50%;
|
|
151
|
+
animation: canvas-loader-spin 1s linear infinite;
|
|
152
|
+
margin-bottom: 16px;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
@keyframes canvas-loader-spin {
|
|
156
|
+
0% {
|
|
157
|
+
transform: rotate(0deg);
|
|
158
|
+
}
|
|
159
|
+
100% {
|
|
160
|
+
transform: rotate(360deg);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.canvas-loader-message {
|
|
165
|
+
font-size: 1.1rem;
|
|
166
|
+
color: #333;
|
|
167
|
+
background: rgba(255, 255, 255, 0.9);
|
|
168
|
+
padding: 8px 16px;
|
|
169
|
+
border-radius: 4px;
|
|
170
|
+
margin-top: 8px;
|
|
171
|
+
text-align: center;
|
|
172
|
+
max-width: 80%;
|
|
173
|
+
word-break: break-word;
|
|
174
|
+
}
|
|
175
|
+
`; }
|
|
176
|
+
#canvas;
|
|
177
|
+
#eventHub;
|
|
178
|
+
#resizeObserver;
|
|
179
|
+
#history;
|
|
180
|
+
#fileStorage;
|
|
181
|
+
#canvasStorage;
|
|
182
|
+
#saveFrequency;
|
|
183
|
+
#timeoutId;
|
|
184
|
+
#intervalId;
|
|
185
|
+
#onViewportResize;
|
|
186
|
+
#rootDiv;
|
|
187
|
+
#onChange;
|
|
188
|
+
#singleImageMenuOptions;
|
|
189
|
+
#multiImageMenuOptions;
|
|
190
|
+
#canvasImageMenuOptions;
|
|
67
191
|
get singleImageMenuOptions() {
|
|
68
|
-
return
|
|
192
|
+
return this.#singleImageMenuOptions;
|
|
69
193
|
}
|
|
70
194
|
get multiImageMenuOptions() {
|
|
71
|
-
return
|
|
195
|
+
return this.#multiImageMenuOptions;
|
|
72
196
|
}
|
|
73
197
|
get canvasImageMenuOptions() {
|
|
74
|
-
return
|
|
198
|
+
return this.#canvasImageMenuOptions;
|
|
75
199
|
}
|
|
76
200
|
get canvas() {
|
|
77
|
-
return
|
|
201
|
+
return this.#canvas;
|
|
78
202
|
}
|
|
79
203
|
get onCanvasChange() {
|
|
80
|
-
return
|
|
204
|
+
return this.#onChange;
|
|
81
205
|
}
|
|
82
206
|
set onCanvasChange(fn) {
|
|
83
|
-
|
|
207
|
+
this.#onChange = fn;
|
|
84
208
|
}
|
|
85
209
|
get eventHub() {
|
|
86
|
-
return
|
|
210
|
+
return this.#eventHub;
|
|
87
211
|
}
|
|
88
212
|
get rootDiv() {
|
|
89
|
-
return
|
|
213
|
+
return this.#rootDiv;
|
|
90
214
|
}
|
|
91
215
|
// Lifecycle
|
|
92
216
|
connectedCallback() {
|
|
@@ -96,9 +220,10 @@ let InfiniteCanvasElement = class InfiniteCanvasElement extends lit_1.LitElement
|
|
|
96
220
|
}
|
|
97
221
|
disconnectedCallback() {
|
|
98
222
|
document.removeEventListener("pointerdown", this.handleGlobalPointerDown, true);
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
223
|
+
window.removeEventListener("resize", this.#onViewportResize);
|
|
224
|
+
this.#resizeObserver?.disconnect();
|
|
225
|
+
this.#resizeObserver = undefined;
|
|
226
|
+
this.#canvas.destroy();
|
|
102
227
|
super.disconnectedCallback();
|
|
103
228
|
}
|
|
104
229
|
firstUpdated(_changedProperties) {
|
|
@@ -116,7 +241,7 @@ let InfiniteCanvasElement = class InfiniteCanvasElement extends lit_1.LitElement
|
|
|
116
241
|
changed.has("displayMode");
|
|
117
242
|
if (!sizeChanged)
|
|
118
243
|
return;
|
|
119
|
-
const container =
|
|
244
|
+
const container = this.#rootDiv;
|
|
120
245
|
const canvasEl = this.renderRoot.querySelector("canvas");
|
|
121
246
|
if (container && canvasEl) {
|
|
122
247
|
this.resizeCanvas(container, canvasEl);
|
|
@@ -124,12 +249,12 @@ let InfiniteCanvasElement = class InfiniteCanvasElement extends lit_1.LitElement
|
|
|
124
249
|
}
|
|
125
250
|
async initCanvas() {
|
|
126
251
|
await this.warmUpStorage();
|
|
127
|
-
|
|
128
|
-
|
|
252
|
+
this.#history = new history_1.CanvasHistory();
|
|
253
|
+
this.#eventHub = new eventemitter3_1.default();
|
|
129
254
|
const div = document.createElement("div");
|
|
130
255
|
div.style.overflow = "hidden";
|
|
131
256
|
this.renderRoot.appendChild(div);
|
|
132
|
-
|
|
257
|
+
this.#rootDiv = div;
|
|
133
258
|
const canvas = document.createElement("canvas");
|
|
134
259
|
// persistent state set up
|
|
135
260
|
this.assignFileStorage = this.assignFileStorage.bind(this);
|
|
@@ -154,7 +279,7 @@ let InfiniteCanvasElement = class InfiniteCanvasElement extends lit_1.LitElement
|
|
|
154
279
|
div.appendChild(canvas);
|
|
155
280
|
}
|
|
156
281
|
this.registerSignal();
|
|
157
|
-
|
|
282
|
+
this.#canvas = new Canvas_1.Canvas(canvas, this.#history, this.#eventHub, this.debounceSaveToCanvasStorage, this.saveImageFileMetadata, this.getContainerSize);
|
|
158
283
|
try {
|
|
159
284
|
await this.restoreStateFromCanvasStorage();
|
|
160
285
|
}
|
|
@@ -163,21 +288,15 @@ let InfiniteCanvasElement = class InfiniteCanvasElement extends lit_1.LitElement
|
|
|
163
288
|
}
|
|
164
289
|
// resize canvas to start
|
|
165
290
|
this.resizeCanvas(div, canvas);
|
|
291
|
+
this.#onViewportResize = () => this.resizeCanvas(div, canvas);
|
|
292
|
+
window.addEventListener("resize", this.#onViewportResize);
|
|
166
293
|
const basicImageMenuOptions = contextMenu_1.createBasicImageMenuOptions.bind(this)();
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
294
|
+
this.#singleImageMenuOptions = contextMenu_1.createSingleImageMenuOptions.bind(this)(basicImageMenuOptions.options);
|
|
295
|
+
this.#canvasImageMenuOptions = contextMenu_1.createCanvasImageMenuOptions.bind(this)();
|
|
296
|
+
this.#multiImageMenuOptions = contextMenu_1.createMultiImageMenuOptions.bind(this)(basicImageMenuOptions.options);
|
|
170
297
|
this.dispatchEvent(new Event("load"));
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
if (!this.renderRoot.contains(stats.dom)) {
|
|
174
|
-
this.renderRoot.appendChild(stats.dom);
|
|
175
|
-
}
|
|
176
|
-
const animate = () => {
|
|
177
|
-
if (stats) {
|
|
178
|
-
stats.update();
|
|
179
|
-
}
|
|
180
|
-
__classPrivateFieldGet(this, _InfiniteCanvasElement_canvas, "f").render();
|
|
298
|
+
const animate = async () => {
|
|
299
|
+
this.#canvas.render();
|
|
181
300
|
requestAnimationFrame(animate);
|
|
182
301
|
};
|
|
183
302
|
animate();
|
|
@@ -206,6 +325,7 @@ let InfiniteCanvasElement = class InfiniteCanvasElement extends lit_1.LitElement
|
|
|
206
325
|
// set the canvas to be as big as possible so there will never be a flickering resize problem
|
|
207
326
|
let w = window.screen.width;
|
|
208
327
|
let h = window.screen.height;
|
|
328
|
+
const rect = container.getBoundingClientRect();
|
|
209
329
|
const targetWidthPx = Math.round(w * dpr);
|
|
210
330
|
const targetHeightPx = Math.round(h * dpr);
|
|
211
331
|
if (canvas.width !== targetWidthPx || canvas.height !== targetHeightPx) {
|
|
@@ -215,7 +335,6 @@ let InfiniteCanvasElement = class InfiniteCanvasElement extends lit_1.LitElement
|
|
|
215
335
|
canvas.style.width = `${w}px`;
|
|
216
336
|
canvas.style.height = `${h}px`;
|
|
217
337
|
// sets up camera dimensions
|
|
218
|
-
const rect = container.getBoundingClientRect();
|
|
219
338
|
this.canvas.camera.viewportX = rect.x;
|
|
220
339
|
this.canvas.camera.viewportY = rect.y;
|
|
221
340
|
this.canvas.camera.state.setHeight(rect.height);
|
|
@@ -223,25 +342,25 @@ let InfiniteCanvasElement = class InfiniteCanvasElement extends lit_1.LitElement
|
|
|
223
342
|
}
|
|
224
343
|
// Register signal
|
|
225
344
|
registerSignal() {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if (
|
|
232
|
-
|
|
345
|
+
this.#eventHub.on(util_1.LoaderEvent.start, loader_1.showLoader.bind(this), "spinner");
|
|
346
|
+
this.#eventHub.on(util_1.LoaderEvent.done, loader_1.hideLoader.bind(this));
|
|
347
|
+
this.#eventHub.on(util_1.ContextMenuEvent.Open, this.addContextMenu);
|
|
348
|
+
this.#eventHub.on(util_1.ContextMenuEvent.Close, this.clearContextMenu);
|
|
349
|
+
this.#eventHub.on(util_1.CanvasEvent.Change, () => {
|
|
350
|
+
if (this.#onChange)
|
|
351
|
+
this.#onChange();
|
|
233
352
|
});
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
353
|
+
this.#eventHub.on(util_1.SaveEvent.Save, this.saveToCanvasStorage);
|
|
354
|
+
this.#eventHub.on(util_1.SaveEvent.SaveCompleted, () => { });
|
|
355
|
+
this.#eventHub.on(util_1.SaveEvent.SaveFailed, () => console.error("Failed to Save!"));
|
|
237
356
|
}
|
|
238
357
|
// need to check if this is good practice
|
|
239
358
|
async warmUpStorage() {
|
|
240
|
-
if (!
|
|
241
|
-
|
|
359
|
+
if (!this.#fileStorage) {
|
|
360
|
+
this.#fileStorage = new storage_1.DefaultIndexedDbStorage();
|
|
242
361
|
}
|
|
243
362
|
try {
|
|
244
|
-
await
|
|
363
|
+
await this.#fileStorage.readAll();
|
|
245
364
|
}
|
|
246
365
|
catch (err) {
|
|
247
366
|
console.error("Storage warm-up failed", err);
|
|
@@ -251,9 +370,9 @@ let InfiniteCanvasElement = class InfiniteCanvasElement extends lit_1.LitElement
|
|
|
251
370
|
* @returns An array containing the width and height of the container
|
|
252
371
|
*/
|
|
253
372
|
getContainerSize() {
|
|
254
|
-
if (!
|
|
373
|
+
if (!this.#rootDiv)
|
|
255
374
|
return;
|
|
256
|
-
const rect =
|
|
375
|
+
const rect = this.#rootDiv.getBoundingClientRect();
|
|
257
376
|
return [rect.width, rect.height];
|
|
258
377
|
}
|
|
259
378
|
// Storage & Persistence
|
|
@@ -261,17 +380,17 @@ let InfiniteCanvasElement = class InfiniteCanvasElement extends lit_1.LitElement
|
|
|
261
380
|
* @param storage Canvas storage stores the positions of all the renderables
|
|
262
381
|
* @param saveFrequency How often should auto save execute
|
|
263
382
|
*/
|
|
264
|
-
assignCanvasStorage(storage, saveFrequency =
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
383
|
+
assignCanvasStorage(storage, saveFrequency = this.#saveFrequency) {
|
|
384
|
+
this.#canvasStorage = storage;
|
|
385
|
+
this.#saveFrequency = saveFrequency;
|
|
386
|
+
this.#intervalId && clearInterval(this.#intervalId);
|
|
387
|
+
this.#intervalId = setInterval(this.saveToCanvasStorage, this.#saveFrequency);
|
|
269
388
|
}
|
|
270
389
|
/**
|
|
271
390
|
* @param storage File storage captures the information about the image data that has previously been added. Made more efficient by using SHA of the image data for storage.
|
|
272
391
|
*/
|
|
273
392
|
assignFileStorage(storage) {
|
|
274
|
-
|
|
393
|
+
this.#fileStorage = storage;
|
|
275
394
|
}
|
|
276
395
|
/**
|
|
277
396
|
* Duplicate images will not be written to the database
|
|
@@ -279,13 +398,13 @@ let InfiniteCanvasElement = class InfiniteCanvasElement extends lit_1.LitElement
|
|
|
279
398
|
* @returns The unique ID that the image has been logged with. This is a hashed version of the image data URL
|
|
280
399
|
*/
|
|
281
400
|
async saveImageFileMetadata(dataURL) {
|
|
282
|
-
if (!
|
|
283
|
-
|
|
401
|
+
if (!this.#fileStorage) {
|
|
402
|
+
this.#fileStorage = new storage_1.DefaultIndexedDbStorage();
|
|
284
403
|
}
|
|
285
404
|
try {
|
|
286
405
|
const id = await (0, files_1.hashStringToId)(dataURL);
|
|
287
|
-
if (!(await
|
|
288
|
-
return await
|
|
406
|
+
if (!(await this.#fileStorage.checkIfImageStored(id))) {
|
|
407
|
+
return await this.#fileStorage.write(dataURL);
|
|
289
408
|
}
|
|
290
409
|
else {
|
|
291
410
|
return id;
|
|
@@ -300,11 +419,11 @@ let InfiniteCanvasElement = class InfiniteCanvasElement extends lit_1.LitElement
|
|
|
300
419
|
* @returns
|
|
301
420
|
*/
|
|
302
421
|
async getImageFileMetadata(fileId) {
|
|
303
|
-
if (!
|
|
304
|
-
|
|
422
|
+
if (!this.#fileStorage) {
|
|
423
|
+
this.#fileStorage = new storage_1.DefaultIndexedDbStorage();
|
|
305
424
|
}
|
|
306
425
|
try {
|
|
307
|
-
return await
|
|
426
|
+
return await this.#fileStorage.read(fileId);
|
|
308
427
|
}
|
|
309
428
|
catch (err) {
|
|
310
429
|
console.error(err);
|
|
@@ -314,11 +433,11 @@ let InfiniteCanvasElement = class InfiniteCanvasElement extends lit_1.LitElement
|
|
|
314
433
|
* @returns All file metadata saved in connected storage
|
|
315
434
|
*/
|
|
316
435
|
async getAllImageFileMetdata() {
|
|
317
|
-
if (!
|
|
318
|
-
|
|
436
|
+
if (!this.#fileStorage) {
|
|
437
|
+
this.#fileStorage = new storage_1.DefaultIndexedDbStorage();
|
|
319
438
|
}
|
|
320
439
|
try {
|
|
321
|
-
return await
|
|
440
|
+
return await this.#fileStorage.readAll();
|
|
322
441
|
}
|
|
323
442
|
catch (err) {
|
|
324
443
|
console.error(err);
|
|
@@ -328,134 +447,134 @@ let InfiniteCanvasElement = class InfiniteCanvasElement extends lit_1.LitElement
|
|
|
328
447
|
* Schedule the auto save to the canvas storage based on timer
|
|
329
448
|
*/
|
|
330
449
|
debounceSaveToCanvasStorage(timeout = 1000) {
|
|
331
|
-
if (!
|
|
332
|
-
|
|
450
|
+
if (!this.#canvasStorage) {
|
|
451
|
+
this.#canvasStorage = new storage_1.DefaultLocalStorage(this.name);
|
|
333
452
|
}
|
|
334
|
-
clearTimeout(
|
|
335
|
-
|
|
453
|
+
clearTimeout(this.#timeoutId);
|
|
454
|
+
this.#timeoutId = setTimeout(this.saveToCanvasStorage, timeout);
|
|
336
455
|
}
|
|
337
456
|
async saveToCanvasStorage() {
|
|
338
|
-
if (!
|
|
339
|
-
|
|
457
|
+
if (!this.#canvasStorage) {
|
|
458
|
+
this.#canvasStorage = new storage_1.DefaultLocalStorage(this.name);
|
|
340
459
|
}
|
|
341
|
-
|
|
342
|
-
.write((0, serializer_1.serializeCanvas)(
|
|
343
|
-
.then(() =>
|
|
344
|
-
.catch(() =>
|
|
460
|
+
this.#canvasStorage
|
|
461
|
+
.write((0, serializer_1.serializeCanvas)(this.#canvas))
|
|
462
|
+
.then(() => this.#eventHub.emit(util_1.SaveEvent.SaveCompleted))
|
|
463
|
+
.catch(() => this.#eventHub.emit(util_1.SaveEvent.SaveFailed));
|
|
345
464
|
}
|
|
346
465
|
async restoreStateFromCanvasStorage() {
|
|
347
|
-
if (!
|
|
348
|
-
|
|
466
|
+
if (!this.#canvasStorage) {
|
|
467
|
+
this.#canvasStorage = new storage_1.DefaultLocalStorage(this.name);
|
|
349
468
|
}
|
|
350
|
-
const dataAsString = await
|
|
469
|
+
const dataAsString = await this.#canvasStorage.read();
|
|
351
470
|
const data = JSON.parse(dataAsString);
|
|
352
471
|
if (data) {
|
|
353
|
-
await (0, serializer_1.deserializeCanvas)(data,
|
|
472
|
+
await (0, serializer_1.deserializeCanvas)(data, this.#canvas, this.getImageFileMetadata);
|
|
354
473
|
}
|
|
355
474
|
}
|
|
356
475
|
// Canvas API
|
|
357
476
|
togglePointerMode() {
|
|
358
|
-
if (!
|
|
477
|
+
if (!this.#canvas)
|
|
359
478
|
return;
|
|
360
|
-
|
|
479
|
+
this.#canvas.changeMode();
|
|
361
480
|
}
|
|
362
481
|
toggleGrid() {
|
|
363
|
-
if (!
|
|
482
|
+
if (!this.#canvas)
|
|
364
483
|
return;
|
|
365
|
-
|
|
484
|
+
this.#canvas.toggleGrid();
|
|
366
485
|
}
|
|
367
486
|
zoomIn() {
|
|
368
|
-
if (!
|
|
487
|
+
if (!this.#canvas)
|
|
369
488
|
return;
|
|
370
|
-
|
|
489
|
+
this.#canvas.updateZoomByFixedAmount(-1);
|
|
371
490
|
}
|
|
372
491
|
zoomOut() {
|
|
373
|
-
if (!
|
|
492
|
+
if (!this.#canvas)
|
|
374
493
|
return;
|
|
375
|
-
|
|
494
|
+
this.#canvas.updateZoomByFixedAmount();
|
|
376
495
|
}
|
|
377
496
|
async addImages(fileList) {
|
|
378
|
-
if (!
|
|
497
|
+
if (!this.#canvas)
|
|
379
498
|
return;
|
|
380
|
-
const rect =
|
|
499
|
+
const rect = this.#rootDiv.getBoundingClientRect();
|
|
381
500
|
const clientX = rect.left + rect.width / 2;
|
|
382
501
|
const clientY = rect.top + rect.height / 2;
|
|
383
|
-
const [wx, wy] = (0, util_1.getWorldCoords)(clientX, clientY,
|
|
384
|
-
const newImages = await (0, util_1.addImages)(fileList, (src) =>
|
|
385
|
-
|
|
502
|
+
const [wx, wy] = (0, util_1.getWorldCoords)(clientX, clientY, this.#canvas);
|
|
503
|
+
const newImages = await (0, util_1.addImages)(fileList, (src) => this.#canvas.addImageToCanvas(src, wx, wy, 1, 1, true));
|
|
504
|
+
this.#history.push((0, manager_1.makeMultiAddChildCommand)(this.#canvas, newImages));
|
|
386
505
|
}
|
|
387
506
|
async removeImage(id) {
|
|
388
|
-
if (!
|
|
507
|
+
if (!this.#canvas)
|
|
389
508
|
return;
|
|
390
|
-
const child =
|
|
391
|
-
|
|
392
|
-
|
|
509
|
+
const child = this.#canvas.getChild(id);
|
|
510
|
+
this.#canvas.removeChild(child);
|
|
511
|
+
this.#history.push((0, manager_1.makeRemoveChildCommand)(this.#canvas, child));
|
|
393
512
|
}
|
|
394
513
|
/**
|
|
395
514
|
* @param url Currently only accept base64 url and the image is automatically added to screen center
|
|
396
515
|
*/
|
|
397
516
|
async addImageFromUrl(url) {
|
|
398
|
-
if (!
|
|
517
|
+
if (!this.#canvas)
|
|
399
518
|
return;
|
|
400
|
-
const rect =
|
|
519
|
+
const rect = this.#rootDiv.getBoundingClientRect();
|
|
401
520
|
const clientX = rect.left + rect.width / 2;
|
|
402
521
|
const clientY = rect.top + rect.height / 2;
|
|
403
|
-
const [wx, wy] = (0, util_1.getWorldCoords)(clientX, clientY,
|
|
404
|
-
const img = await
|
|
405
|
-
|
|
522
|
+
const [wx, wy] = (0, util_1.getWorldCoords)(clientX, clientY, this.#canvas);
|
|
523
|
+
const img = await this.#canvas.addImageToCanvas(url, wx, wy, 1, 1, true);
|
|
524
|
+
this.#history.push((0, manager_1.makeMultiAddChildCommand)(this.#canvas, [img]));
|
|
406
525
|
}
|
|
407
526
|
async copyImage() {
|
|
408
|
-
if (!
|
|
527
|
+
if (!this.#canvas)
|
|
409
528
|
return;
|
|
410
|
-
await (0, util_1.copy)(
|
|
529
|
+
await (0, util_1.copy)(this.#canvas.getSelected());
|
|
411
530
|
}
|
|
412
531
|
async pasteImage(e) {
|
|
413
|
-
if (!
|
|
532
|
+
if (!this.#canvas)
|
|
414
533
|
return;
|
|
415
|
-
await (0, util_1.paste)(e.clientX, e.clientY,
|
|
534
|
+
await (0, util_1.paste)(e.clientX, e.clientY, this.#canvas, this.#history, false);
|
|
416
535
|
}
|
|
417
536
|
flipVertical() {
|
|
418
|
-
if (!
|
|
537
|
+
if (!this.#canvas)
|
|
419
538
|
return;
|
|
420
|
-
|
|
539
|
+
this.#canvas.selectionManager.flip("vertical");
|
|
421
540
|
}
|
|
422
541
|
flipHorizontal() {
|
|
423
|
-
if (!
|
|
542
|
+
if (!this.#canvas)
|
|
424
543
|
return;
|
|
425
|
-
|
|
544
|
+
this.#canvas.selectionManager.flip("horizontal");
|
|
426
545
|
}
|
|
427
546
|
align(direction) {
|
|
428
|
-
if (!
|
|
547
|
+
if (!this.#canvas)
|
|
429
548
|
return;
|
|
430
|
-
|
|
549
|
+
this.#canvas.selectionManager.alignSelection(direction);
|
|
431
550
|
}
|
|
432
551
|
normalizeSelection(type, mode) {
|
|
433
|
-
if (!
|
|
552
|
+
if (!this.#canvas)
|
|
434
553
|
return;
|
|
435
|
-
|
|
554
|
+
this.#canvas.selectionManager.normalize(type, mode);
|
|
436
555
|
}
|
|
437
556
|
sendShapeToNewZOrder(toFront) {
|
|
438
|
-
if (!
|
|
557
|
+
if (!this.#canvas)
|
|
439
558
|
return;
|
|
440
|
-
|
|
559
|
+
this.#canvas.setShapeZOrder(toFront);
|
|
441
560
|
}
|
|
442
561
|
deleteSelectedImages() {
|
|
443
|
-
if (!
|
|
562
|
+
if (!this.#canvas)
|
|
444
563
|
return;
|
|
445
|
-
|
|
564
|
+
this.#canvas.selectionManager.deleteSelected(this.#canvas);
|
|
446
565
|
}
|
|
447
566
|
async exportCanvas(filename = "infinite-canvas.json") {
|
|
448
|
-
if (!
|
|
567
|
+
if (!this.#canvas)
|
|
449
568
|
return;
|
|
450
|
-
|
|
569
|
+
this.#eventHub.emit(util_1.LoaderEvent.start, "spinner");
|
|
451
570
|
const files = await this.getAllImageFileMetdata();
|
|
452
|
-
const data = (0, serializer_1.serializeCanvas)(
|
|
571
|
+
const data = (0, serializer_1.serializeCanvas)(this.#canvas, files);
|
|
453
572
|
(0, files_1.downloadJSON)(filename, data);
|
|
454
|
-
|
|
573
|
+
this.#eventHub.emit(util_1.LoaderEvent.done);
|
|
455
574
|
}
|
|
456
575
|
async importCanvas(fileList) {
|
|
457
|
-
|
|
458
|
-
if (!
|
|
576
|
+
this.#eventHub.emit(util_1.LoaderEvent.start, "spinner");
|
|
577
|
+
if (!this.#canvas)
|
|
459
578
|
return;
|
|
460
579
|
if (!fileList || fileList.length !== 1)
|
|
461
580
|
return;
|
|
@@ -465,182 +584,33 @@ let InfiniteCanvasElement = class InfiniteCanvasElement extends lit_1.LitElement
|
|
|
465
584
|
!file.name.toLowerCase().endsWith(".json")))
|
|
466
585
|
return;
|
|
467
586
|
const data = await (0, files_1.readJSONFile)(file);
|
|
468
|
-
await (0, serializer_1.deserializeCanvas)(data,
|
|
469
|
-
|
|
470
|
-
|
|
587
|
+
await (0, serializer_1.deserializeCanvas)(data, this.#canvas, this.getImageFileMetadata, this.saveImageFileMetadata);
|
|
588
|
+
this.#eventHub.emit(util_1.SaveEvent.Save);
|
|
589
|
+
this.#eventHub.emit(util_1.LoaderEvent.done);
|
|
471
590
|
}
|
|
472
591
|
clearCanvas() {
|
|
473
|
-
if (!
|
|
592
|
+
if (!this.#canvas)
|
|
474
593
|
return;
|
|
475
|
-
|
|
594
|
+
this.#canvas.clearChildren();
|
|
476
595
|
}
|
|
477
596
|
/**
|
|
478
597
|
* A non-reactive method that captures the number of images added to the canvas
|
|
479
598
|
*/
|
|
480
599
|
getTotalNumberOfChildren() {
|
|
481
|
-
if (!
|
|
600
|
+
if (!this.#canvas)
|
|
482
601
|
return;
|
|
483
|
-
return
|
|
602
|
+
return this.#canvas.totalNumberOfChildren;
|
|
484
603
|
}
|
|
485
604
|
/**
|
|
486
605
|
* A non-reactive method that captures the number of images that are in camera
|
|
487
606
|
*/
|
|
488
607
|
getNumberOfChildrenRendered() {
|
|
489
|
-
if (!
|
|
608
|
+
if (!this.#canvas)
|
|
490
609
|
return;
|
|
491
|
-
return
|
|
610
|
+
return this.#canvas.numberOfChildrenRendered;
|
|
492
611
|
}
|
|
493
612
|
};
|
|
494
613
|
exports.InfiniteCanvasElement = InfiniteCanvasElement;
|
|
495
|
-
_InfiniteCanvasElement_canvas = new WeakMap();
|
|
496
|
-
_InfiniteCanvasElement_eventHub = new WeakMap();
|
|
497
|
-
_InfiniteCanvasElement_resizeObserver = new WeakMap();
|
|
498
|
-
_InfiniteCanvasElement_history = new WeakMap();
|
|
499
|
-
_InfiniteCanvasElement_fileStorage = new WeakMap();
|
|
500
|
-
_InfiniteCanvasElement_canvasStorage = new WeakMap();
|
|
501
|
-
_InfiniteCanvasElement_saveFrequency = new WeakMap();
|
|
502
|
-
_InfiniteCanvasElement_timeoutId = new WeakMap();
|
|
503
|
-
_InfiniteCanvasElement_intervalId = new WeakMap();
|
|
504
|
-
_InfiniteCanvasElement_rootDiv = new WeakMap();
|
|
505
|
-
_InfiniteCanvasElement_onChange = new WeakMap();
|
|
506
|
-
_InfiniteCanvasElement_singleImageMenuOptions = new WeakMap();
|
|
507
|
-
_InfiniteCanvasElement_multiImageMenuOptions = new WeakMap();
|
|
508
|
-
_InfiniteCanvasElement_canvasImageMenuOptions = new WeakMap();
|
|
509
|
-
InfiniteCanvasElement.properties = {
|
|
510
|
-
name: { type: String, reflect: true },
|
|
511
|
-
width: { type: String, reflect: true },
|
|
512
|
-
height: { type: String, reflect: true },
|
|
513
|
-
displayMode: { type: String, reflect: true },
|
|
514
|
-
};
|
|
515
|
-
InfiniteCanvasElement.styles = (0, lit_1.css) `
|
|
516
|
-
:host {
|
|
517
|
-
position: relative;
|
|
518
|
-
overflow: hidden;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
.context-menu {
|
|
522
|
-
position: absolute;
|
|
523
|
-
background: white;
|
|
524
|
-
min-width: 180px;
|
|
525
|
-
background: var(--menu-bg, #fff);
|
|
526
|
-
border-radius: 6px;
|
|
527
|
-
border: 1px solid var(--menu-border, #9f9f9fff);
|
|
528
|
-
box-sizing: border-box;
|
|
529
|
-
padding: 6px 0;
|
|
530
|
-
display: flex;
|
|
531
|
-
gap: 2px;
|
|
532
|
-
flex-direction: column;
|
|
533
|
-
font-family: system-ui, sans-serif;
|
|
534
|
-
animation: fadeInMenu 0.13s cubic-bezier(0.4, 0, 0.2, 1);
|
|
535
|
-
overflow: scroll;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
@keyframes fadeInMenu {
|
|
539
|
-
from {
|
|
540
|
-
opacity: 0;
|
|
541
|
-
transform: translateY(8px);
|
|
542
|
-
}
|
|
543
|
-
to {
|
|
544
|
-
opacity: 1;
|
|
545
|
-
transform: none;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
.context-menu button {
|
|
550
|
-
all: unset;
|
|
551
|
-
display: flex;
|
|
552
|
-
align-items: center;
|
|
553
|
-
box-sizing: border-box;
|
|
554
|
-
width: 100%;
|
|
555
|
-
padding: 8px 18px;
|
|
556
|
-
font-size: 15px;
|
|
557
|
-
color: var(--menu-fg, #222);
|
|
558
|
-
background: none;
|
|
559
|
-
cursor: pointer;
|
|
560
|
-
transition:
|
|
561
|
-
background 0.1s,
|
|
562
|
-
color 0.1s;
|
|
563
|
-
user-select: none;
|
|
564
|
-
outline: none;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
.context-menu button:hover,
|
|
568
|
-
.context-menu button:focus-visible {
|
|
569
|
-
background: var(--menu-hover, #c7d5eaff);
|
|
570
|
-
color: var(--menu-accent, #155290ff);
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
.context-menu button:active {
|
|
574
|
-
background: var(--menu-active, #e3eaf3);
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
.context-menu button[disabled] {
|
|
578
|
-
color: #aaa;
|
|
579
|
-
cursor: not-allowed;
|
|
580
|
-
background: none;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
.context-menu-divider {
|
|
584
|
-
height: 1px;
|
|
585
|
-
background: var(--menu-divider, #c7d5eaff);
|
|
586
|
-
margin: 6px 12px;
|
|
587
|
-
border: none;
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
canvas {
|
|
591
|
-
width: 100%;
|
|
592
|
-
height: 100%;
|
|
593
|
-
outline: none;
|
|
594
|
-
padding: 0;
|
|
595
|
-
margin: 0;
|
|
596
|
-
touch-action: none;
|
|
597
|
-
display: block;
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
.canvas-loader {
|
|
601
|
-
position: absolute;
|
|
602
|
-
top: 0;
|
|
603
|
-
left: 0;
|
|
604
|
-
display: flex;
|
|
605
|
-
flex-direction: column;
|
|
606
|
-
align-items: center;
|
|
607
|
-
justify-content: center;
|
|
608
|
-
background: rgba(255, 255, 255, 0.7);
|
|
609
|
-
z-index: 1000;
|
|
610
|
-
pointer-events: all;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
.canvas-loader-spinner {
|
|
614
|
-
width: 48px;
|
|
615
|
-
height: 48px;
|
|
616
|
-
border: 6px solid #e0e0e0;
|
|
617
|
-
border-top: 6px solid #1976d2;
|
|
618
|
-
border-radius: 50%;
|
|
619
|
-
animation: canvas-loader-spin 1s linear infinite;
|
|
620
|
-
margin-bottom: 16px;
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
@keyframes canvas-loader-spin {
|
|
624
|
-
0% {
|
|
625
|
-
transform: rotate(0deg);
|
|
626
|
-
}
|
|
627
|
-
100% {
|
|
628
|
-
transform: rotate(360deg);
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
.canvas-loader-message {
|
|
633
|
-
font-size: 1.1rem;
|
|
634
|
-
color: #333;
|
|
635
|
-
background: rgba(255, 255, 255, 0.9);
|
|
636
|
-
padding: 8px 16px;
|
|
637
|
-
border-radius: 4px;
|
|
638
|
-
margin-top: 8px;
|
|
639
|
-
text-align: center;
|
|
640
|
-
max-width: 80%;
|
|
641
|
-
word-break: break-word;
|
|
642
|
-
}
|
|
643
|
-
`;
|
|
644
614
|
exports.InfiniteCanvasElement = InfiniteCanvasElement = __decorate([
|
|
645
615
|
(0, decorators_js_1.customElement)("infinite-canvas")
|
|
646
616
|
], InfiniteCanvasElement);
|