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