@twobirds/microcomponents 0.1.6 → 0.1.10
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 +4 -0
- package/dist/MC.d.ts.map +1 -1
- package/dist/MC.js +22 -21
- package/dist/MC.min.js +22 -21
- package/dist/MC.ts +373 -359
- package/dist/elements.d.ts.map +1 -1
- package/dist/elements.js +3 -3
- package/dist/elements.ts +91 -100
- package/dist/formValues.d.ts.map +1 -1
- package/dist/formValues.js +4 -6
- package/dist/formValues.min.js +1 -1
- package/dist/formValues.ts +173 -179
- package/dist/helpers.d.ts +1 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +2 -4
- package/dist/helpers.ts +101 -113
- package/dist/microcomponents.d.ts.map +1 -1
- package/dist/microcomponents.js +1 -1
- package/dist/microcomponents.min.js +1 -1
- package/dist/microcomponents.ts +1 -3
- package/dist/observables.d.ts.map +1 -1
- package/dist/observables.js +17 -23
- package/dist/observables.min.js +1 -1
- package/dist/observables.ts +344 -378
- package/package.json +15 -4
package/dist/observables.ts
CHANGED
|
@@ -1,191 +1,173 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
LooseObject,
|
|
5
|
-
debounce,
|
|
6
|
-
deepEqual,
|
|
7
|
-
flattenObject,
|
|
8
|
-
copyGettersSetters,
|
|
9
|
-
} from './helpers.js';
|
|
3
|
+
import { LooseObject, debounce, deepEqual, flattenObject, copyGettersSetters } from './helpers.js';
|
|
10
4
|
|
|
11
5
|
import { DC, HTMLMcElement, trigger } from './MC.js';
|
|
12
6
|
|
|
13
7
|
function isObservableObject(value: any): boolean {
|
|
14
|
-
|
|
8
|
+
return value != null && typeof value === 'object';
|
|
15
9
|
}
|
|
16
10
|
|
|
17
11
|
type Callback = (value: any) => void;
|
|
18
12
|
|
|
19
13
|
function getPlaceholders(target: HTMLElement): string[] {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// extract {} and return result
|
|
51
|
-
return [...placeholders].map((placeholder) =>
|
|
52
|
-
placeholder.replace(/[\{\}]/g, '')
|
|
53
|
-
);
|
|
14
|
+
let placeholders: Set<string> = new Set(),
|
|
15
|
+
regEx = /\{[^\{\}]*\}/g;
|
|
16
|
+
|
|
17
|
+
// scan attributes
|
|
18
|
+
(target.getAttributeNames ? target.getAttributeNames() : [])
|
|
19
|
+
.filter((attr) => target.getAttribute(attr)?.match(regEx)) // extract placeholder(s)
|
|
20
|
+
.forEach((attr) => {
|
|
21
|
+
let placeholders: Set<string> = new Set(target.getAttribute(attr)?.match(regEx));
|
|
22
|
+
placeholders.forEach((placeholder) =>
|
|
23
|
+
placeholders.add(placeholder.replace(/[\{\}]/g, ''))
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// scan children text nodes
|
|
28
|
+
[...target.childNodes]
|
|
29
|
+
.filter((childNode) => {
|
|
30
|
+
return (
|
|
31
|
+
// is text node and has placeholders
|
|
32
|
+
childNode.nodeType === 3 && ((childNode as any).nodeValue || '').match(regEx)
|
|
33
|
+
);
|
|
34
|
+
})
|
|
35
|
+
.forEach((childNode) => {
|
|
36
|
+
((childNode as any).nodeValue || '').match(regEx).forEach((placeholder: string) => {
|
|
37
|
+
placeholders.add(placeholder.replace(/[\{\}]/g, ''));
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// extract {} and return result
|
|
42
|
+
return [...placeholders].map((placeholder) => placeholder.replace(/[\{\}]/g, ''));
|
|
54
43
|
}
|
|
55
44
|
|
|
56
45
|
const inputs = [HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement];
|
|
57
46
|
|
|
58
47
|
class Placeholders extends DC {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
});
|
|
156
|
-
//console.log( 'Placeholder onData', ev, data );
|
|
157
|
-
if (JSON.stringify(data) !== JSON.stringify(this.data))
|
|
158
|
-
Object.assign(this.data, data); // will trigger callbacks
|
|
159
|
-
}
|
|
48
|
+
data: LooseObject = {};
|
|
49
|
+
oldDisplay?: string = '';
|
|
50
|
+
|
|
51
|
+
constructor(target: HTMLElement) {
|
|
52
|
+
super(target);
|
|
53
|
+
getPlaceholders(target).forEach((property) => (this.data[property] = ''));
|
|
54
|
+
observe(this);
|
|
55
|
+
this.#attachCallbacks();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
#attachCallbacks() {
|
|
59
|
+
let target = this.target,
|
|
60
|
+
hide: boolean = false;
|
|
61
|
+
|
|
62
|
+
//console.log('attachCallbacks', this.target);
|
|
63
|
+
|
|
64
|
+
// scan attributes
|
|
65
|
+
(target.getAttributeNames ? target.getAttributeNames() : [])
|
|
66
|
+
.filter((attr: string) => target.getAttribute(attr)?.match(/\{[^\{\}]*\}/g)) // extract placeholder(s)
|
|
67
|
+
.forEach((attr: string) => {
|
|
68
|
+
let placeholders: Set<string> = new Set(
|
|
69
|
+
target.getAttribute(attr)?.match(/\{[^\{\}]*\}/g)
|
|
70
|
+
);
|
|
71
|
+
// attach callback
|
|
72
|
+
this.data.observe(
|
|
73
|
+
((template: string) => (data: LooseObject) => {
|
|
74
|
+
let result: string = template;
|
|
75
|
+
placeholders.forEach((placeholder) => {
|
|
76
|
+
result = result.replace(
|
|
77
|
+
placeholder,
|
|
78
|
+
data[placeholder.replace(/[\{\}]/g, '')]
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
target.setAttribute(attr, result);
|
|
82
|
+
//console.log('cb setAttribute()', data, template, attr, result);
|
|
83
|
+
})(target.getAttribute(attr))
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// scan children text nodes
|
|
88
|
+
[...target.childNodes]
|
|
89
|
+
.filter((childNode) => {
|
|
90
|
+
return (
|
|
91
|
+
// is text node and has placeholders
|
|
92
|
+
childNode.nodeType === 3 &&
|
|
93
|
+
((childNode as Node).nodeValue || '').match(/\{[^\{\}]*\}/g)
|
|
94
|
+
);
|
|
95
|
+
})
|
|
96
|
+
.forEach((childNode) => {
|
|
97
|
+
hide = true;
|
|
98
|
+
let placeholders: Set<string> = new Set(childNode.nodeValue.match(/\{[^\{\}]*\}/g));
|
|
99
|
+
|
|
100
|
+
// attach callback
|
|
101
|
+
this.data.observe(
|
|
102
|
+
((template: string) => (data: LooseObject) => {
|
|
103
|
+
let result: string = template;
|
|
104
|
+
//console.log('text node "', template, '" callback data:', data);
|
|
105
|
+
placeholders.forEach((placeholder) => {
|
|
106
|
+
result = result.replace(
|
|
107
|
+
placeholder,
|
|
108
|
+
data[placeholder.replace(/[\{\}]/g, '')]
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
if (this.oldDisplay) {
|
|
113
|
+
target.style = this.oldDisplay;
|
|
114
|
+
} else {
|
|
115
|
+
target.removeAttribute('style');
|
|
116
|
+
}
|
|
117
|
+
delete this.oldDisplay;
|
|
118
|
+
|
|
119
|
+
childNode.nodeValue = result;
|
|
120
|
+
})(childNode.nodeValue)
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
// hide for now
|
|
124
|
+
this.oldDisplay = target.getAttribute('style') || ''; // TBD allow null to indicate there was no old style attribute
|
|
125
|
+
|
|
126
|
+
if (
|
|
127
|
+
hide &&
|
|
128
|
+
!inputs.includes((target as any).constructor) &&
|
|
129
|
+
target.tagName !== 'FIELDSET'
|
|
130
|
+
) {
|
|
131
|
+
target.style.display = 'none';
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
onData(ev: CustomEvent) {
|
|
137
|
+
let data: LooseObject = {};
|
|
138
|
+
Object.keys(ev.detail.data).forEach((key) => {
|
|
139
|
+
if (this.data.hasOwnProperty(key)) data[key] = ev.detail.data[key];
|
|
140
|
+
});
|
|
141
|
+
//console.log( 'Placeholder onData', ev, data );
|
|
142
|
+
if (JSON.stringify(data) !== JSON.stringify(this.data)) Object.assign(this.data, data); // will trigger callbacks
|
|
143
|
+
}
|
|
160
144
|
}
|
|
161
145
|
|
|
162
146
|
function getPlaceholderElements(
|
|
163
|
-
|
|
147
|
+
target: HTMLElement | DocumentFragment | ShadowRoot
|
|
164
148
|
): HTMLElement[] {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
149
|
+
let elements: Array<Node> = [target]
|
|
150
|
+
.concat([...(target.querySelectorAll('*') as unknown as HTMLElement[])])
|
|
151
|
+
.filter((e) => !!getPlaceholders(e as HTMLElement).length)
|
|
152
|
+
.filter((e) => (e as HTMLElement)?.tagName !== 'STYLE')
|
|
153
|
+
.reverse(); // to start with inner elements
|
|
154
|
+
//console.log('getPlaceholderElements', elements);
|
|
155
|
+
return elements as HTMLElement[];
|
|
172
156
|
}
|
|
173
157
|
|
|
174
|
-
function makePlaceholders(
|
|
175
|
-
|
|
176
|
-
):
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
DC.add(e, '_placeholders', new Placeholders(e));
|
|
181
|
-
});
|
|
158
|
+
function makePlaceholders(element: HTMLElement | DocumentFragment | ShadowRoot): void {
|
|
159
|
+
//console.log('makePlaceholders', element);
|
|
160
|
+
getPlaceholderElements(element).forEach((e: HTMLElement) => {
|
|
161
|
+
if ((e as HTMLMcElement)?._mc?._placeholders) return;
|
|
162
|
+
DC.add(e, '_placeholders', new Placeholders(e));
|
|
163
|
+
});
|
|
182
164
|
}
|
|
183
165
|
|
|
184
166
|
export type Observable = {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
167
|
+
observe: (cb: Callback) => void;
|
|
168
|
+
notify: () => void;
|
|
169
|
+
bind: (target: HTMLElement | DocumentFragment | ShadowRoot) => void;
|
|
170
|
+
callbacks: Array<Function>;
|
|
189
171
|
};
|
|
190
172
|
|
|
191
173
|
type Class<T> = new (...args: any[]) => T;
|
|
@@ -194,223 +176,207 @@ type Class<T> = new (...args: any[]) => T;
|
|
|
194
176
|
const classRepo: LooseObject = {};
|
|
195
177
|
|
|
196
178
|
// creates a new observable class wrapper
|
|
197
|
-
function makeObservable(val: any): Class<typeof val>{
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
return this;
|
|
295
|
-
}
|
|
296
|
-
};
|
|
297
|
-
|
|
298
|
-
(classRepo as any)[ConstructorName] = ObservableClass;
|
|
299
|
-
|
|
300
|
-
// console.log( 'classRepo:', classRepo );
|
|
301
|
-
|
|
302
|
-
return ObservableClass;
|
|
179
|
+
function makeObservable(val: any): Class<typeof val> {
|
|
180
|
+
const Constructor = val.constructor,
|
|
181
|
+
ConstructorName: string = Constructor.name;
|
|
182
|
+
|
|
183
|
+
let ObservableClass = class extends Constructor {
|
|
184
|
+
#notify: boolean = true;
|
|
185
|
+
#callbacks: Array<Function> = [];
|
|
186
|
+
|
|
187
|
+
constructor(val: any) {
|
|
188
|
+
super(val);
|
|
189
|
+
(this.constructor as LooseObject).callbacks = [];
|
|
190
|
+
if (isObservableObject(val)) {
|
|
191
|
+
// you cannot bind native values to the dom
|
|
192
|
+
Object.getOwnPropertyNames(val).forEach((key) => {
|
|
193
|
+
this[key] = val[key];
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
get callbacks(): Array<Function> {
|
|
199
|
+
return this.#callbacks;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
set callbacks(callbacks: Array<Function>) {
|
|
203
|
+
this.#callbacks = callbacks;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
observe(callback: Function) {
|
|
207
|
+
//console.log('observe');
|
|
208
|
+
this.#callbacks.push(callback);
|
|
209
|
+
return this;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
bind(target: HTMLElement | DocumentFragment | ShadowRoot) {
|
|
213
|
+
const iso = isObservableObject(this.valueOf());
|
|
214
|
+
//console.log('bind', iso, target);
|
|
215
|
+
|
|
216
|
+
if (iso) {
|
|
217
|
+
makePlaceholders(target);
|
|
218
|
+
const placeholders: unknown = [
|
|
219
|
+
target,
|
|
220
|
+
...target.querySelectorAll('[data-mc]'),
|
|
221
|
+
].filter((e) => !!(e as LooseObject)?._mc?._placeholders);
|
|
222
|
+
|
|
223
|
+
//console.log('placeholders:', target, placeholders);
|
|
224
|
+
|
|
225
|
+
const func = function callback(data: LooseObject) {
|
|
226
|
+
// avoid bubbling for speed
|
|
227
|
+
let flattenedData = flattenObject(data);
|
|
228
|
+
//console.log('inside placeholder callback', target, flattenedData);
|
|
229
|
+
(placeholders as Node[]).forEach((node) => {
|
|
230
|
+
// console.log('trigger callback', e);
|
|
231
|
+
if (!node) return;
|
|
232
|
+
trigger(node as HTMLMcElement, 'data', flattenedData);
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
(this as any).#callbacks.push(func);
|
|
236
|
+
this.notify();
|
|
237
|
+
} else {
|
|
238
|
+
console.warn(
|
|
239
|
+
'IGNORED - cannot bind a non-Object to the dom, IS:',
|
|
240
|
+
typeof this.valueOf(),
|
|
241
|
+
this
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return this;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
notify(notify?: boolean) {
|
|
249
|
+
let data =
|
|
250
|
+
isObservableObject(this.valueOf()) && this.valueOf() instanceof Array === false
|
|
251
|
+
? structuredClone(Object.assign({}, this))
|
|
252
|
+
: this.valueOf();
|
|
253
|
+
|
|
254
|
+
if (notify !== undefined) {
|
|
255
|
+
this.#notify = notify;
|
|
256
|
+
// return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (this.#notify) {
|
|
260
|
+
(this as any).#callbacks.forEach((f: Function) => {
|
|
261
|
+
f(data);
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return this;
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
(classRepo as any)[ConstructorName] = ObservableClass;
|
|
270
|
+
|
|
271
|
+
// console.log( 'classRepo:', classRepo );
|
|
272
|
+
|
|
273
|
+
return ObservableClass;
|
|
303
274
|
}
|
|
304
275
|
|
|
305
|
-
function getConstructor(
|
|
306
|
-
|
|
307
|
-
|
|
276
|
+
function getConstructor(val: any): Class<typeof val> {
|
|
277
|
+
let key: string = val.constructor.name;
|
|
278
|
+
return classRepo[key] || makeObservable(val);
|
|
308
279
|
}
|
|
309
280
|
|
|
310
|
-
export const observe = (
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
)
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
};
|
|
412
|
-
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
return target;
|
|
281
|
+
export const observe = (target: LooseObject, propertyName?: string): LooseObject => {
|
|
282
|
+
let keys = !propertyName ? [...Object.getOwnPropertyNames(target)] : [propertyName];
|
|
283
|
+
|
|
284
|
+
keys.forEach((key) => {
|
|
285
|
+
if (
|
|
286
|
+
target.hasOwnProperty(key) &&
|
|
287
|
+
typeof target[key] !== 'function' &&
|
|
288
|
+
!target?.[key]?.constructor?.prototype?.observe
|
|
289
|
+
) {
|
|
290
|
+
// console.log( 'co propname', key, typeof target[key], target[key] );
|
|
291
|
+
if (key[0] === '_' || target?.[key] === undefined) return target;
|
|
292
|
+
let val: any = target[key];
|
|
293
|
+
|
|
294
|
+
let o = new (getConstructor(val) as Class<typeof val>)(val);
|
|
295
|
+
|
|
296
|
+
Object.defineProperty(target, key, {
|
|
297
|
+
enumerable: true,
|
|
298
|
+
get() {
|
|
299
|
+
if (isObservableObject(o.valueOf())) {
|
|
300
|
+
let check = structuredClone(Object.assign({}, o));
|
|
301
|
+
setTimeout(
|
|
302
|
+
debounce(function checkChanges() {
|
|
303
|
+
let state = structuredClone(Object.assign({}, o));
|
|
304
|
+
//console.log( 'check changes', state, check );
|
|
305
|
+
if (!deepEqual(state, check)) {
|
|
306
|
+
//console.log( '-> not equal!' );
|
|
307
|
+
o.notify();
|
|
308
|
+
}
|
|
309
|
+
}, 0),
|
|
310
|
+
0
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
return o;
|
|
314
|
+
},
|
|
315
|
+
set(val: any) {
|
|
316
|
+
//console.log( 'SET:', isObservableObject( val.valueOf() ), 'set:', val, 'to', isObservableObject( o.valueOf() ), o, );
|
|
317
|
+
if (typeof val === typeof o.valueOf()) {
|
|
318
|
+
let iso =
|
|
319
|
+
isObservableObject(o.valueOf()) &&
|
|
320
|
+
o.valueOf() instanceof Array === false;
|
|
321
|
+
//console.log('target iso:', iso);
|
|
322
|
+
if (iso) {
|
|
323
|
+
let old = structuredClone(Object.assign({}, o));
|
|
324
|
+
//val = structuredClone(Object.assign({}, val));
|
|
325
|
+
|
|
326
|
+
//let callbacks = (o.constructor as LooseObject).callbacks;
|
|
327
|
+
if (o?.constructor?.prototype?.observe === undefined) {
|
|
328
|
+
o = new (getConstructor(val) as Class<typeof val>)(
|
|
329
|
+
structuredClone(Object.assign({}, val))
|
|
330
|
+
);
|
|
331
|
+
copyGettersSetters(val, o);
|
|
332
|
+
o.seal();
|
|
333
|
+
}
|
|
334
|
+
Object.assign(o, val);
|
|
335
|
+
|
|
336
|
+
// console.log('after set o=', o, 'check', old);
|
|
337
|
+
// (o.constructor as LooseObject).callbacks = callbacks;
|
|
338
|
+
|
|
339
|
+
setTimeout(
|
|
340
|
+
// check for changes
|
|
341
|
+
debounce(function checkChanges() {
|
|
342
|
+
let state = structuredClone(Object.assign({}, o));
|
|
343
|
+
//console.log( 'check changes' );
|
|
344
|
+
if (!deepEqual(state, old)) {
|
|
345
|
+
//console.log('-> not equal:', state, old);
|
|
346
|
+
o.notify();
|
|
347
|
+
}
|
|
348
|
+
}, 0),
|
|
349
|
+
0
|
|
350
|
+
);
|
|
351
|
+
} else {
|
|
352
|
+
// simple native var
|
|
353
|
+
let old = o.valueOf(),
|
|
354
|
+
callbacks = (o as Observable).callbacks;
|
|
355
|
+
|
|
356
|
+
// console.log( 'sO', o, o.valueOf(), 'val', val );
|
|
357
|
+
if (val !== old) {
|
|
358
|
+
// console.log( '-> sO new val', val );
|
|
359
|
+
o = new (getConstructor(val) as Class<typeof val>)(val);
|
|
360
|
+
(o as Observable).callbacks = callbacks;
|
|
361
|
+
o.notify();
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
} else {
|
|
365
|
+
console.warn(
|
|
366
|
+
'IGNORED - cannot change observable types, IS:',
|
|
367
|
+
typeof o.valueOf(),
|
|
368
|
+
o,
|
|
369
|
+
'- CANNOT BECOME:',
|
|
370
|
+
typeof val
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// replace old property with proxy (observable)
|
|
377
|
+
target[key] = val;
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
return target;
|
|
416
382
|
};
|