@cocreate/elements 1.45.0 → 1.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +1 -1
- package/README.md +467 -44
- package/package.json +11 -10
- package/src/crudHandler.js +150 -46
- package/src/dndHandler.js +11 -8
- package/src/formHandler.js +225 -170
- package/src/getDataHandler.js +111 -53
- package/src/index.js +4 -3
- package/src/initHandler.js +96 -46
- package/src/mapRegistry.js +10 -1
- package/src/setDataHandler.js +163 -84
- package/src/socketHandler.js +328 -0
- package/src/srcHandler.js +68 -32
- package/src/subscribeHandler.js +64 -64
- package/test/deferred-src.test.cjs +11 -0
- package/test/form-enter.test.cjs +48 -0
- package/test/get-data.test.cjs +100 -0
- package/test/init-read.test.cjs +145 -0
- package/test/load-more.test.cjs +19 -0
- package/test/save-array.test.cjs +31 -0
- package/test/save-completion.test.cjs +36 -0
- package/test/set-data-type.test.cjs +119 -0
- package/test/ssr-response.test.cjs +108 -0
- package/webpack.config.js +1 -0
package/src/getDataHandler.js
CHANGED
|
@@ -3,8 +3,39 @@ import {
|
|
|
3
3
|
checkValue
|
|
4
4
|
} from "@cocreate/utils";
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import {
|
|
7
|
+
elements,
|
|
8
|
+
forms,
|
|
9
|
+
keys,
|
|
10
|
+
elementSubscriptions,
|
|
11
|
+
subscriptions
|
|
12
|
+
} from "./mapRegistry.js";
|
|
13
|
+
import { getSubscription } from "./subscribeHandler.js";
|
|
14
|
+
import { read, save } from "./crudHandler.js";
|
|
15
|
+
|
|
16
|
+
const environment = localStorage.getItem("environment");
|
|
17
|
+
|
|
18
|
+
function registerFormElement(form, element, data) {
|
|
19
|
+
if (!form || !data?.type) return;
|
|
20
|
+
|
|
21
|
+
if (!form.save) form.save = () => save(form);
|
|
22
|
+
if (!form.getData) form.getData = () => getData(form);
|
|
23
|
+
|
|
24
|
+
let formObject = forms.get(form);
|
|
25
|
+
if (!formObject) {
|
|
26
|
+
formObject = {
|
|
27
|
+
elements: new Map(),
|
|
28
|
+
types: new Map()
|
|
29
|
+
};
|
|
30
|
+
forms.set(form, formObject);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
formObject.elements.set(element, data);
|
|
34
|
+
if (!formObject.types.has(data.type)) {
|
|
35
|
+
formObject.types.set(data.type, new Map());
|
|
36
|
+
}
|
|
37
|
+
formObject.types.get(data.type).set(element, data);
|
|
38
|
+
}
|
|
8
39
|
|
|
9
40
|
/**
|
|
10
41
|
* Parses and normalizes HTML attributes from an element into a canonical CRUD data object.
|
|
@@ -46,6 +77,13 @@ export function getDataObject(element) {
|
|
|
46
77
|
else if (data.storage || data.storage === "") data.type = "storage";
|
|
47
78
|
else if (data.data) data.type = "data";
|
|
48
79
|
|
|
80
|
+
if (
|
|
81
|
+
environment &&
|
|
82
|
+
!data.database &&
|
|
83
|
+
["array", "index", "object"].includes(data.type)
|
|
84
|
+
)
|
|
85
|
+
data.database = environment;
|
|
86
|
+
|
|
49
87
|
delete data.isRealtime;
|
|
50
88
|
return data;
|
|
51
89
|
}
|
|
@@ -59,7 +97,10 @@ export function getDataObject(element) {
|
|
|
59
97
|
* @returns {{ string: string, object: Object }}
|
|
60
98
|
*/
|
|
61
99
|
export function initDataKey(element, data) {
|
|
100
|
+
if (!data.database && environment) data.database = environment;
|
|
101
|
+
|
|
62
102
|
const dataKey = getDataKey(data);
|
|
103
|
+
const subscription = getSubscription(data);
|
|
63
104
|
|
|
64
105
|
if (keys.has(dataKey.string)) {
|
|
65
106
|
keys.get(dataKey.string).elements.set(element, "");
|
|
@@ -69,34 +110,27 @@ export function initDataKey(element, data) {
|
|
|
69
110
|
data,
|
|
70
111
|
dataKey
|
|
71
112
|
});
|
|
72
|
-
subscribe(dataKey, data);
|
|
73
113
|
}
|
|
74
114
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
formObject.elements.set(element, data);
|
|
86
|
-
if (formObject.types.has(data.type)) {
|
|
87
|
-
formObject.types.get(data.type).set(element, data);
|
|
88
|
-
} else {
|
|
89
|
-
formObject.types.set(data.type, new Map([[element, data]]));
|
|
90
|
-
}
|
|
91
|
-
} else {
|
|
92
|
-
forms.set(form, {
|
|
93
|
-
elements: new Map([[element, data]]),
|
|
94
|
-
types: new Map([[data.type, new Map([[element, data]])]])
|
|
95
|
-
});
|
|
96
|
-
}
|
|
115
|
+
if (subscription) {
|
|
116
|
+
let subscriptionEntry = subscriptions.get(subscription.string);
|
|
117
|
+
if (subscriptionEntry) {
|
|
118
|
+
subscriptionEntry.elements.add(element);
|
|
119
|
+
} else {
|
|
120
|
+
subscriptionEntry = {
|
|
121
|
+
elements: new Set([element]),
|
|
122
|
+
subscription
|
|
123
|
+
};
|
|
124
|
+
subscriptions.set(subscription.string, subscriptionEntry);
|
|
97
125
|
}
|
|
126
|
+
elementSubscriptions.set(element, subscription.string);
|
|
98
127
|
}
|
|
99
128
|
|
|
129
|
+
elements.set(element, dataKey.string);
|
|
130
|
+
|
|
131
|
+
const form = element.form || element.closest?.("form");
|
|
132
|
+
registerFormElement(form, element, data);
|
|
133
|
+
|
|
100
134
|
if (!element.read) element.read = () => read(element);
|
|
101
135
|
if (!element.save) element.save = () => save(element);
|
|
102
136
|
|
|
@@ -113,6 +147,8 @@ export function initDataKey(element, data) {
|
|
|
113
147
|
*/
|
|
114
148
|
export function getDataKey(data) {
|
|
115
149
|
const dataKey = {};
|
|
150
|
+
if (!data.database && environment) data.database = environment;
|
|
151
|
+
|
|
116
152
|
const attributes = [
|
|
117
153
|
"host",
|
|
118
154
|
"organization_id",
|
|
@@ -126,13 +162,13 @@ export function getDataKey(data) {
|
|
|
126
162
|
"object",
|
|
127
163
|
"key",
|
|
128
164
|
"updateKey",
|
|
129
|
-
"
|
|
165
|
+
"filter",
|
|
130
166
|
"upsert",
|
|
131
167
|
"namespace",
|
|
132
168
|
"room",
|
|
133
169
|
"broadcast",
|
|
134
170
|
"broadcastSender",
|
|
135
|
-
"
|
|
171
|
+
"broadcastClient"
|
|
136
172
|
];
|
|
137
173
|
|
|
138
174
|
for (const attribute of attributes) {
|
|
@@ -193,7 +229,7 @@ export function getDataKeys(data) {
|
|
|
193
229
|
"array",
|
|
194
230
|
"index",
|
|
195
231
|
"object",
|
|
196
|
-
"
|
|
232
|
+
"filter"
|
|
197
233
|
];
|
|
198
234
|
|
|
199
235
|
for (const [keyString, sortedKey] of keys.entries()) {
|
|
@@ -201,8 +237,8 @@ export function getDataKeys(data) {
|
|
|
201
237
|
|
|
202
238
|
for (const key of targetKeys) {
|
|
203
239
|
if (
|
|
204
|
-
!data
|
|
205
|
-
key === "
|
|
240
|
+
!data.filter &&
|
|
241
|
+
key === "filter" &&
|
|
206
242
|
sortedKey.data.hasOwnProperty(key)
|
|
207
243
|
) {
|
|
208
244
|
hasMatch = true;
|
|
@@ -211,25 +247,28 @@ export function getDataKeys(data) {
|
|
|
211
247
|
hasMatch = false;
|
|
212
248
|
break;
|
|
213
249
|
}
|
|
250
|
+
|
|
251
|
+
// Filtered object elements watch a collection query, not one
|
|
252
|
+
// specific object. Creates and updates contain the affected
|
|
253
|
+
// object(s), so their object shape will not match the empty
|
|
254
|
+
// object scope used when the filtered element was registered.
|
|
255
|
+
if (key === "object" && sortedKey.data.filter) continue;
|
|
256
|
+
|
|
214
257
|
if (
|
|
215
258
|
Array.isArray(sortedKey.data[key]) &&
|
|
216
259
|
Array.isArray(data[key])
|
|
217
260
|
) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return data[key].some((obj) => obj._id === value._id);
|
|
224
|
-
} else {
|
|
225
|
-
return data[key].includes(value);
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
if (!matches) {
|
|
230
|
-
hasMatch = false;
|
|
231
|
-
break;
|
|
261
|
+
const matches = sortedKey.data[key].some((value) => {
|
|
262
|
+
if (key === "object") {
|
|
263
|
+
return data[key].some((obj) => obj._id === value._id);
|
|
264
|
+
} else {
|
|
265
|
+
return data[key].includes(value);
|
|
232
266
|
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
if (!matches) {
|
|
270
|
+
hasMatch = false;
|
|
271
|
+
break;
|
|
233
272
|
}
|
|
234
273
|
} else if (sortedKey.data[key] !== data[key]) {
|
|
235
274
|
hasMatch = false;
|
|
@@ -264,7 +303,7 @@ function formatPayload(payload, value, element) {
|
|
|
264
303
|
} else payload.object = { ...payload.object, ...value };
|
|
265
304
|
} else payload.object[payload.key] = value;
|
|
266
305
|
}
|
|
267
|
-
if (payload.isUpsert && payload
|
|
306
|
+
if (payload.isUpsert && payload.filter)
|
|
268
307
|
payload.method = "object.update";
|
|
269
308
|
} else payload[payload.type] = value;
|
|
270
309
|
} else if (payload[payload.type] && payload.key) {
|
|
@@ -352,19 +391,27 @@ function formatPayload(payload, value, element) {
|
|
|
352
391
|
*/
|
|
353
392
|
export async function getData(form) {
|
|
354
393
|
const dataKeys = new Map();
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
394
|
+
const selector = "[storage], [database], [array], [object]";
|
|
395
|
+
|
|
396
|
+
// form.elements includes controls associated through form="id", even when
|
|
397
|
+
// the controller lives elsewhere in the document.
|
|
398
|
+
for (const element of Array.from(form.elements || [])) {
|
|
399
|
+
if (!element.matches(selector) || element.closest("[template]")) continue;
|
|
400
|
+
|
|
401
|
+
// A dataKey identifies the document scope and intentionally excludes
|
|
402
|
+
// the field-level `key`. Reusing the registry entry's data here would
|
|
403
|
+
// therefore give every control the key of the first registered field.
|
|
404
|
+
const data = getDataObject(element);
|
|
405
|
+
registerFormElement(form, element, data);
|
|
362
406
|
}
|
|
363
407
|
|
|
408
|
+
const formObject = forms.get(form);
|
|
409
|
+
if (!formObject) return [];
|
|
364
410
|
|
|
365
411
|
for (const type of formObject.types.values()) {
|
|
366
412
|
for (const [element, data] of type.entries()) {
|
|
367
413
|
if (
|
|
414
|
+
element.closest("[template]") ||
|
|
368
415
|
!element.hasAttribute("key") ||
|
|
369
416
|
element.getAttribute("save") === "false"
|
|
370
417
|
) {
|
|
@@ -384,6 +431,17 @@ export async function getData(form) {
|
|
|
384
431
|
const value = await element.getValue();
|
|
385
432
|
if (value === undefined) continue;
|
|
386
433
|
|
|
434
|
+
// A form can expose multiple file sources (for example, device and
|
|
435
|
+
// camera) while only one source has a pending selection. An unused
|
|
436
|
+
// file input must not produce an empty object.create operation.
|
|
437
|
+
if (
|
|
438
|
+
element.matches('input[type="file"]') &&
|
|
439
|
+
Array.isArray(value) &&
|
|
440
|
+
value.length === 0
|
|
441
|
+
) {
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
|
|
387
445
|
const payload = { ...data };
|
|
388
446
|
const dataKey = elements.get(element);
|
|
389
447
|
|
|
@@ -414,4 +472,4 @@ export default {
|
|
|
414
472
|
getDataKeys,
|
|
415
473
|
getData,
|
|
416
474
|
getDataObject
|
|
417
|
-
};
|
|
475
|
+
};
|
package/src/index.js
CHANGED
|
@@ -32,8 +32,9 @@ import { reset } from "./formHandler.js";
|
|
|
32
32
|
|
|
33
33
|
import "@cocreate/element-prototype";
|
|
34
34
|
import "./dndHandler.js";
|
|
35
|
-
import "./srcHandler.js";
|
|
36
|
-
import "./
|
|
35
|
+
import "./srcHandler.js";
|
|
36
|
+
import "./socketHandler.js";
|
|
37
|
+
import "./valueQuery.js";
|
|
37
38
|
|
|
38
39
|
import { elements, forms, keys, debounce } from "./mapRegistry.js"
|
|
39
40
|
|
|
@@ -50,4 +51,4 @@ export default {
|
|
|
50
51
|
debounce,
|
|
51
52
|
getAttributes,
|
|
52
53
|
setDataType
|
|
53
|
-
};
|
|
54
|
+
};
|
package/src/initHandler.js
CHANGED
|
@@ -6,8 +6,15 @@ import { getAttributes } from "@cocreate/utils";
|
|
|
6
6
|
import { initDataKey, getData, getDataObject } from "./getDataHandler.js";
|
|
7
7
|
import { read, save } from "./crudHandler.js";
|
|
8
8
|
import { setData } from "./setDataHandler.js";
|
|
9
|
-
import {
|
|
10
|
-
|
|
9
|
+
import {
|
|
10
|
+
elements,
|
|
11
|
+
forms,
|
|
12
|
+
keys,
|
|
13
|
+
debounce,
|
|
14
|
+
elementSubscriptions,
|
|
15
|
+
subscriptions
|
|
16
|
+
} from "./mapRegistry.js";
|
|
17
|
+
import { unsubscribe } from "./subscribeHandler.js";
|
|
11
18
|
|
|
12
19
|
const selector = "[storage], [database], [array], [render-json]";
|
|
13
20
|
|
|
@@ -36,36 +43,31 @@ export async function init(element) {
|
|
|
36
43
|
elements.has(element[i]) ||
|
|
37
44
|
element[i].tagName === "FORM" ||
|
|
38
45
|
element[i].getAttribute("crud") === "false"
|
|
39
|
-
)
|
|
46
|
+
) {
|
|
40
47
|
continue;
|
|
48
|
+
}
|
|
41
49
|
|
|
42
50
|
promises.push(
|
|
43
51
|
(async () => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
const el = element[i];
|
|
53
|
+
try {
|
|
54
|
+
const data = await initElement(el);
|
|
55
|
+
if (data) {
|
|
56
|
+
const dataKey = initDataKey(el, data);
|
|
57
|
+
// read() already batches elements by dataKey. Schedule each
|
|
58
|
+
// ready element without waiting for unrelated async fields.
|
|
59
|
+
await read(el, data, dataKey);
|
|
60
|
+
}
|
|
61
|
+
} catch (error) {
|
|
62
|
+
// Allow initialization to be retried if filter setup failed.
|
|
63
|
+
if (elements.get(el) === "") elements.delete(el);
|
|
64
|
+
console.error("[CoCreateElements] Failed to initialize element:", el, error);
|
|
48
65
|
}
|
|
49
66
|
})()
|
|
50
67
|
);
|
|
51
68
|
}
|
|
52
69
|
|
|
53
|
-
|
|
54
|
-
let dataObjects = new Map();
|
|
55
|
-
|
|
56
|
-
for (let res of results) {
|
|
57
|
-
if (res) dataObjects.set(res.string, res.data);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (dataObjects && dataObjects.size > 0) {
|
|
61
|
-
for (let key of dataObjects.keys()) {
|
|
62
|
-
let keyEntry = keys.get(key);
|
|
63
|
-
if (keyEntry) {
|
|
64
|
-
let { data, elements: mappedElements } = keyEntry;
|
|
65
|
-
read(Array.from(mappedElements.keys()), data, { string: key });
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
70
|
+
await Promise.all(promises);
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
/**
|
|
@@ -76,14 +78,17 @@ export async function init(element) {
|
|
|
76
78
|
*/
|
|
77
79
|
export async function initElement(el) {
|
|
78
80
|
let data = getDataObject(el);
|
|
79
|
-
if (!data || !data.type)
|
|
81
|
+
if (!data || !data.type) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
80
84
|
|
|
81
85
|
if (
|
|
82
86
|
data.object &&
|
|
83
87
|
data.object !== "pending" &&
|
|
84
88
|
!/^[0-9a-fA-F]{24}$/.test(data.object)
|
|
85
|
-
)
|
|
89
|
+
) {
|
|
86
90
|
return;
|
|
91
|
+
}
|
|
87
92
|
|
|
88
93
|
if (!elements.has(el)) {
|
|
89
94
|
elements.set(el, "");
|
|
@@ -124,10 +129,10 @@ export async function initElement(el) {
|
|
|
124
129
|
await filter.init(el);
|
|
125
130
|
|
|
126
131
|
if (el.getFilter) {
|
|
127
|
-
data
|
|
132
|
+
data.filter = await el.getFilter();
|
|
128
133
|
|
|
129
134
|
el.setFilter = (filter) => {
|
|
130
|
-
data
|
|
135
|
+
data.filter = filter;
|
|
131
136
|
el.hasRead = false;
|
|
132
137
|
read(el, data);
|
|
133
138
|
};
|
|
@@ -160,16 +165,13 @@ export async function remove(element) {
|
|
|
160
165
|
|
|
161
166
|
form = form.elements;
|
|
162
167
|
for (let formEl of form.keys()) {
|
|
168
|
+
removeSubscription(formEl);
|
|
163
169
|
let key = elements.get(formEl);
|
|
164
170
|
if (key) {
|
|
165
171
|
let keyEntry = keys.get(key);
|
|
166
172
|
if (keyEntry) {
|
|
167
173
|
keyEntry.elements.delete(formEl);
|
|
168
|
-
if (!keyEntry.elements.size)
|
|
169
|
-
// Last element watching this key was removed -> trigger socket unsubscribe
|
|
170
|
-
unsubscribe(keyEntry.dataKey, keyEntry.data);
|
|
171
|
-
keys.delete(key);
|
|
172
|
-
}
|
|
174
|
+
if (!keyEntry.elements.size) keys.delete(key);
|
|
173
175
|
}
|
|
174
176
|
elements.delete(formEl);
|
|
175
177
|
}
|
|
@@ -180,15 +182,13 @@ export async function remove(element) {
|
|
|
180
182
|
let keyEntry = keys.get(key);
|
|
181
183
|
if (keyEntry) {
|
|
182
184
|
keyEntry.elements.delete(el);
|
|
183
|
-
if (!keyEntry.elements.size)
|
|
184
|
-
unsubscribe(keyEntry.dataKey, keyEntry.data);
|
|
185
|
-
keys.delete(key);
|
|
186
|
-
}
|
|
185
|
+
if (!keyEntry.elements.size) keys.delete(key);
|
|
187
186
|
}
|
|
188
187
|
}
|
|
189
188
|
elements.delete(el);
|
|
190
189
|
forms.delete(el);
|
|
191
190
|
} else {
|
|
191
|
+
removeSubscription(el);
|
|
192
192
|
let key = elements.get(el);
|
|
193
193
|
if (key) {
|
|
194
194
|
if (el.getFilter) filter.filters.delete(el);
|
|
@@ -198,11 +198,7 @@ export async function remove(element) {
|
|
|
198
198
|
keyEntry.elements.delete(el);
|
|
199
199
|
elements.delete(el);
|
|
200
200
|
|
|
201
|
-
if (!keyEntry.elements.size)
|
|
202
|
-
// Last element watching this key was removed -> trigger socket unsubscribe
|
|
203
|
-
unsubscribe(keyEntry.dataKey, keyEntry.data);
|
|
204
|
-
keys.delete(key);
|
|
205
|
-
}
|
|
201
|
+
if (!keyEntry.elements.size) keys.delete(key);
|
|
206
202
|
} else {
|
|
207
203
|
elements.delete(el);
|
|
208
204
|
}
|
|
@@ -220,11 +216,27 @@ export async function remove(element) {
|
|
|
220
216
|
}
|
|
221
217
|
}
|
|
222
218
|
|
|
219
|
+
function removeSubscription(element) {
|
|
220
|
+
const subscription = elementSubscriptions.get(element);
|
|
221
|
+
if (!subscription) return;
|
|
222
|
+
|
|
223
|
+
const subscriptionEntry = subscriptions.get(subscription);
|
|
224
|
+
if (subscriptionEntry) {
|
|
225
|
+
subscriptionEntry.elements.delete(element);
|
|
226
|
+
if (!subscriptionEntry.elements.size) {
|
|
227
|
+
unsubscribe(subscriptionEntry.subscription);
|
|
228
|
+
subscriptions.delete(subscription);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
elementSubscriptions.delete(element);
|
|
233
|
+
}
|
|
234
|
+
|
|
223
235
|
/**
|
|
224
236
|
* Initializes global CRUD socket response listeners.
|
|
225
237
|
*/
|
|
226
238
|
export function initSocket() {
|
|
227
|
-
const type = ["storage", "database", "array", "index", "object"
|
|
239
|
+
const type = ["storage", "database", "array", "index", "object"];
|
|
228
240
|
const method = ["create", "update", "delete"];
|
|
229
241
|
|
|
230
242
|
for (let i = 0; i < type.length; i++) {
|
|
@@ -233,9 +245,11 @@ export function initSocket() {
|
|
|
233
245
|
|
|
234
246
|
CRUD.listen(action, function (data) {
|
|
235
247
|
if (
|
|
236
|
-
data.
|
|
248
|
+
data.key &&
|
|
249
|
+
!data.updateDB &&
|
|
237
250
|
data.status === "received" &&
|
|
238
|
-
|
|
251
|
+
!data.method.includes(".delete") &&
|
|
252
|
+
CRUD.socket.frameId === data.frameId
|
|
239
253
|
)
|
|
240
254
|
return;
|
|
241
255
|
if (data.rendered) return;
|
|
@@ -253,6 +267,7 @@ Observer.init({
|
|
|
253
267
|
types: ["addedNodes"],
|
|
254
268
|
selector: selector,
|
|
255
269
|
callback: function (mutation) {
|
|
270
|
+
if (mutation.move) return;
|
|
256
271
|
init([mutation.target]);
|
|
257
272
|
}
|
|
258
273
|
});
|
|
@@ -262,7 +277,7 @@ Observer.init({
|
|
|
262
277
|
types: ["removedNodes"],
|
|
263
278
|
selector: selector,
|
|
264
279
|
callback: function (mutation) {
|
|
265
|
-
if (mutation.
|
|
280
|
+
if (mutation.move) return;
|
|
266
281
|
remove(mutation.target);
|
|
267
282
|
}
|
|
268
283
|
});
|
|
@@ -273,6 +288,8 @@ Observer.init({
|
|
|
273
288
|
attributeFilter: [
|
|
274
289
|
"organization_id",
|
|
275
290
|
"host",
|
|
291
|
+
"namespace",
|
|
292
|
+
"room",
|
|
276
293
|
"storage",
|
|
277
294
|
"database",
|
|
278
295
|
"array",
|
|
@@ -290,6 +307,39 @@ Observer.init({
|
|
|
290
307
|
}
|
|
291
308
|
});
|
|
292
309
|
|
|
310
|
+
Observer.init({
|
|
311
|
+
name: "CoCreateElementsReadAttribute",
|
|
312
|
+
types: ["attributes"],
|
|
313
|
+
attributeFilter: ["read"],
|
|
314
|
+
selector,
|
|
315
|
+
callback: async function (mutation) {
|
|
316
|
+
const el = mutation.target;
|
|
317
|
+
const currentValue = el.getAttribute("read");
|
|
318
|
+
if (currentValue === mutation.oldValue) return;
|
|
319
|
+
|
|
320
|
+
if (currentValue === "false") {
|
|
321
|
+
el.hasRead = false;
|
|
322
|
+
for (const [key, pending] of debounce) {
|
|
323
|
+
pending.elements?.delete(el);
|
|
324
|
+
if (pending.elements && !pending.elements.size) {
|
|
325
|
+
clearTimeout(pending.timer);
|
|
326
|
+
debounce.delete(key);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (!elements.has(el)) {
|
|
333
|
+
await init([el]);
|
|
334
|
+
} else if (el.getFilter && el.setFilter) {
|
|
335
|
+
el.setFilter(await el.getFilter());
|
|
336
|
+
} else {
|
|
337
|
+
el.hasRead = false;
|
|
338
|
+
await read(el);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
293
343
|
init();
|
|
294
344
|
|
|
295
|
-
export default { init, initElement, initSocket, remove };
|
|
345
|
+
export default { init, initElement, initSocket, remove };
|
package/src/mapRegistry.js
CHANGED
|
@@ -2,5 +2,14 @@ export const elements = new WeakMap();
|
|
|
2
2
|
export const forms = new WeakMap();
|
|
3
3
|
export const keys = new Map();
|
|
4
4
|
export const debounce = new Map();
|
|
5
|
+
export const elementSubscriptions = new WeakMap();
|
|
6
|
+
export const subscriptions = new Map();
|
|
5
7
|
|
|
6
|
-
export default {
|
|
8
|
+
export default {
|
|
9
|
+
elements,
|
|
10
|
+
forms,
|
|
11
|
+
keys,
|
|
12
|
+
debounce,
|
|
13
|
+
elementSubscriptions,
|
|
14
|
+
subscriptions
|
|
15
|
+
}
|