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