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