@twobirds/microcomponents 0.1.20 → 0.1.21
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 +12 -16
- package/dist/MC.d.ts +2 -10
- package/dist/MC.d.ts.map +1 -1
- package/dist/MC.js +11 -61
- package/dist/MC.min.js +63 -60
- package/dist/MC.ts +268 -112
- package/dist/elements.d.ts.map +1 -1
- package/dist/elements.js +1 -2
- package/dist/elements.min.js +1 -1
- package/dist/elements.ts +26 -6
- package/dist/events.d.ts +11 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +55 -0
- package/dist/events.ts +158 -0
- 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 +168 -10
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +3 -1
- package/dist/helpers.ts +82 -1
- package/dist/main.mjs +1 -0
- package/dist/microcomponents.d.ts.map +1 -1
- package/dist/microcomponents.min.js +1 -1
- package/dist/observables.d.ts +1 -1
- package/dist/observables.d.ts.map +1 -1
- package/dist/observables.js +14 -14
- package/dist/observables.min.js +1 -1
- package/dist/observables.ts +132 -21
- package/package.json +7 -7
package/dist/observables.ts
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
|
-
|
|
1
|
+
import { LooseObject, flattenObject, debounce, deepEqual, copyGettersSetters } from './helpers.js';
|
|
2
|
+
import { HTMLMcElement, DC } from './MC.js';
|
|
3
|
+
import { trigger } from './events.js';
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { DC, HTMLMcElement, trigger } from './MC.js';
|
|
5
|
+
/**
|
|
6
|
+
Determines if a value is an observable object.
|
|
6
7
|
|
|
8
|
+
@param value - The value to check.
|
|
9
|
+
@returns `true` if the value is a non-null object, otherwise `false`.
|
|
10
|
+
*/
|
|
7
11
|
function isObservableObject(value: any): boolean {
|
|
8
12
|
return value != null && typeof value === 'object';
|
|
9
13
|
}
|
|
10
14
|
|
|
11
15
|
type Callback = (value: any) => void;
|
|
12
16
|
|
|
17
|
+
/**
|
|
18
|
+
Extracts placeholder names from an HTML element's attributes and text content.
|
|
19
|
+
|
|
20
|
+
Placeholders are strings enclosed in curly braces `{}`. This function scans
|
|
21
|
+
all attributes and direct child text nodes of the target element for placeholders
|
|
22
|
+
and returns them as an array of unique names (without braces).
|
|
23
|
+
|
|
24
|
+
@param target - The HTMLElement to scan for placeholders.
|
|
25
|
+
@returns An array of placeholder names found within the element's attributes and text content.
|
|
26
|
+
*/
|
|
13
27
|
function getPlaceholders(target: HTMLElement): string[] {
|
|
14
28
|
let placeholders: Set<string> = new Set(),
|
|
15
29
|
regEx = /\{[^\{\}]*\}/g;
|
|
@@ -18,8 +32,10 @@ function getPlaceholders(target: HTMLElement): string[] {
|
|
|
18
32
|
(target.getAttributeNames ? target.getAttributeNames() : [])
|
|
19
33
|
.filter((attr) => target.getAttribute(attr)?.match(regEx)) // extract placeholder(s)
|
|
20
34
|
.forEach((attr) => {
|
|
21
|
-
let
|
|
22
|
-
|
|
35
|
+
let attrPlaceholders: Set<string> = new Set(target.getAttribute(attr)?.match(regEx));
|
|
36
|
+
attrPlaceholders.forEach((attrPlaceholder: string) =>
|
|
37
|
+
placeholders.add(attrPlaceholder.replace(/[\{\}]/g, ''))
|
|
38
|
+
);
|
|
23
39
|
});
|
|
24
40
|
|
|
25
41
|
// scan children text nodes
|
|
@@ -31,21 +47,38 @@ function getPlaceholders(target: HTMLElement): string[] {
|
|
|
31
47
|
);
|
|
32
48
|
})
|
|
33
49
|
.forEach((childNode) => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
50
|
+
let textContent: string = childNode?.nodeValue || '';
|
|
51
|
+
let contentPlaceholders = textContent.match(regEx) as string[];
|
|
52
|
+
contentPlaceholders.forEach((textNodePlaceholder) =>
|
|
53
|
+
placeholders.add(textNodePlaceholder.replace(/[\{\}]/g, ''))
|
|
54
|
+
);
|
|
37
55
|
});
|
|
38
56
|
|
|
39
|
-
|
|
40
|
-
return [...placeholders].map((placeholder) => placeholder.replace(/[\{\}]/g, ''));
|
|
57
|
+
return [...placeholders];
|
|
41
58
|
}
|
|
42
59
|
|
|
43
60
|
const inputs = [HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement];
|
|
44
61
|
|
|
62
|
+
/**
|
|
63
|
+
A class that manages placeholders within an HTML element and updates them dynamically.
|
|
64
|
+
|
|
65
|
+
This class extends `DC` (Data Controller) and processes the target element to identify
|
|
66
|
+
placeholder patterns in attributes and text nodes. It then observes changes on its internal
|
|
67
|
+
data object and updates the corresponding DOM locations when notified of changes.
|
|
68
|
+
*/
|
|
45
69
|
class Placeholders extends DC {
|
|
46
70
|
data: LooseObject = {};
|
|
47
71
|
oldDisplay?: string = '';
|
|
48
72
|
|
|
73
|
+
/**
|
|
74
|
+
Creates a new `Placeholders` instance for the given HTML element.
|
|
75
|
+
|
|
76
|
+
Initializes the internal data with empty strings for each placeholder
|
|
77
|
+
found in the target and starts observing the data object. Attaches
|
|
78
|
+
callbacks to update placeholders as needed.
|
|
79
|
+
|
|
80
|
+
@param target - The HTMLElement to process for placeholders.
|
|
81
|
+
*/
|
|
49
82
|
constructor(target: HTMLElement) {
|
|
50
83
|
super(target);
|
|
51
84
|
getPlaceholders(target).forEach((property) => (this.data[property] = ''));
|
|
@@ -63,7 +96,7 @@ class Placeholders extends DC {
|
|
|
63
96
|
.forEach((attr: string) => {
|
|
64
97
|
let placeholders: Set<string> = new Set(target.getAttribute(attr)?.match(/\{[^\{\}]*\}/g));
|
|
65
98
|
// attach callback
|
|
66
|
-
this.data.
|
|
99
|
+
this.data.subscribe(
|
|
67
100
|
((template: string) => (data: LooseObject) => {
|
|
68
101
|
let result: string = template;
|
|
69
102
|
placeholders.forEach((placeholder) => {
|
|
@@ -91,7 +124,7 @@ class Placeholders extends DC {
|
|
|
91
124
|
let placeholders: Set<string> = new Set(childNode.nodeValue.match(/\{[^\{\}]*\}/g));
|
|
92
125
|
|
|
93
126
|
// attach callback
|
|
94
|
-
this.data.
|
|
127
|
+
this.data.subscribe(
|
|
95
128
|
((template: string) => (data: LooseObject) => {
|
|
96
129
|
let result: string = template;
|
|
97
130
|
placeholders.forEach((placeholder) => {
|
|
@@ -126,6 +159,14 @@ class Placeholders extends DC {
|
|
|
126
159
|
});
|
|
127
160
|
}
|
|
128
161
|
|
|
162
|
+
/**
|
|
163
|
+
Handles a `data` custom event to update the internal placeholder data.
|
|
164
|
+
|
|
165
|
+
Extracts matching keys from the event's detail data and updates only those
|
|
166
|
+
properties in the internal `data` object that were previously defined as placeholders.
|
|
167
|
+
|
|
168
|
+
@param ev - The CustomEvent containing new data to merge into the placeholders.
|
|
169
|
+
*/
|
|
129
170
|
onData(ev: CustomEvent) {
|
|
130
171
|
let data: LooseObject = {};
|
|
131
172
|
Object.keys(ev.detail.data).forEach((key) => {
|
|
@@ -135,6 +176,12 @@ class Placeholders extends DC {
|
|
|
135
176
|
}
|
|
136
177
|
}
|
|
137
178
|
|
|
179
|
+
/**
|
|
180
|
+
Finds all elements within a given scope that contain placeholders.
|
|
181
|
+
|
|
182
|
+
@param target - The HTMLElement, DocumentFragment, or ShadowRoot to search.
|
|
183
|
+
@returns An array of HTMLElements containing placeholder patterns.
|
|
184
|
+
*/
|
|
138
185
|
function getPlaceholderElements(
|
|
139
186
|
target: HTMLElement | DocumentFragment | ShadowRoot
|
|
140
187
|
): HTMLElement[] {
|
|
@@ -146,6 +193,14 @@ function getPlaceholderElements(
|
|
|
146
193
|
return elements as HTMLElement[];
|
|
147
194
|
}
|
|
148
195
|
|
|
196
|
+
/**
|
|
197
|
+
Applies placeholder support to an element and its descendants.
|
|
198
|
+
|
|
199
|
+
This function scans the provided target for elements containing placeholders
|
|
200
|
+
and attaches a `Placeholders` controller instance to each one if not already present.
|
|
201
|
+
|
|
202
|
+
@param element - The HTMLElement, DocumentFragment, or ShadowRoot to process.
|
|
203
|
+
*/
|
|
149
204
|
function makePlaceholders(element: HTMLElement | DocumentFragment | ShadowRoot): void {
|
|
150
205
|
getPlaceholderElements(element).forEach((e: HTMLElement) => {
|
|
151
206
|
if ((e as HTMLMcElement)?._mc?._placeholders) return;
|
|
@@ -153,11 +208,17 @@ function makePlaceholders(element: HTMLElement | DocumentFragment | ShadowRoot):
|
|
|
153
208
|
});
|
|
154
209
|
}
|
|
155
210
|
|
|
211
|
+
/**
|
|
212
|
+
Interface representing an observable object that supports subscription, notification, and DOM binding.
|
|
213
|
+
|
|
214
|
+
Observable objects track registered callbacks and invoke them when their internal state changes.
|
|
215
|
+
They can also be bound to a DOM target to automatically update placeholder values in the view layer.
|
|
216
|
+
*/
|
|
156
217
|
export type Observable = {
|
|
157
|
-
|
|
158
|
-
notify: () => void;
|
|
159
|
-
bind: (target: HTMLElement | DocumentFragment | ShadowRoot) => void;
|
|
160
|
-
callbacks: Array<Function>;
|
|
218
|
+
subscribe: (cb: Callback) => void; // Registers a callback function to be invoked when the observable is notified.
|
|
219
|
+
notify: () => void; // Notifies all registered callbacks with the current observable data.
|
|
220
|
+
bind: (target: HTMLElement | DocumentFragment | ShadowRoot) => void; // Binds the observable to a DOM target, setting up placeholder updates.
|
|
221
|
+
callbacks: Array<Function>; // Array of registered callback functions.
|
|
161
222
|
};
|
|
162
223
|
|
|
163
224
|
type Class<T> = new (...args: any[]) => T;
|
|
@@ -165,7 +226,15 @@ type Class<T> = new (...args: any[]) => T;
|
|
|
165
226
|
// repository for observable class wrappers
|
|
166
227
|
const classRepo: LooseObject = {};
|
|
167
228
|
|
|
168
|
-
|
|
229
|
+
/**
|
|
230
|
+
Creates a new observable wrapper class for the given value's constructor.
|
|
231
|
+
|
|
232
|
+
Returns a subclass of the original constructor that implements the Observable interface,
|
|
233
|
+
allowing observation and notification of changes. The new class is cached in `classRepo`.
|
|
234
|
+
|
|
235
|
+
@param val - The value whose constructor should be wrapped with observable behavior.
|
|
236
|
+
@returns A new Observable class that wraps the original constructor.
|
|
237
|
+
*/
|
|
169
238
|
function makeObservable(val: any): Class<typeof val> {
|
|
170
239
|
const Constructor = val.constructor,
|
|
171
240
|
ConstructorName: string = Constructor.name;
|
|
@@ -193,11 +262,27 @@ function makeObservable(val: any): Class<typeof val> {
|
|
|
193
262
|
this.#callbacks = callbacks;
|
|
194
263
|
}
|
|
195
264
|
|
|
196
|
-
|
|
265
|
+
/**
|
|
266
|
+
Registers a callback function to be invoked when the observable object is notified.
|
|
267
|
+
|
|
268
|
+
@param callback - The function to invoke on notification.
|
|
269
|
+
@returns This instance for chaining.
|
|
270
|
+
*/
|
|
271
|
+
subscribe(callback: Function) {
|
|
197
272
|
this.#callbacks.push(callback);
|
|
198
273
|
return this;
|
|
199
274
|
}
|
|
200
275
|
|
|
276
|
+
/**
|
|
277
|
+
Binds the observable to a DOM target, setting up placeholder updates and
|
|
278
|
+
triggering initial callbacks.
|
|
279
|
+
|
|
280
|
+
Scans the target and its descendants for placeholders and registers handlers
|
|
281
|
+
to update them with flattened data on notification.
|
|
282
|
+
|
|
283
|
+
@param target - The HTMLElement, DocumentFragment, or ShadowRoot to bind.
|
|
284
|
+
@returns This instance for chaining.
|
|
285
|
+
*/
|
|
201
286
|
bind(target: HTMLElement | DocumentFragment | ShadowRoot) {
|
|
202
287
|
const iso = isObservableObject(this.valueOf());
|
|
203
288
|
|
|
@@ -229,6 +314,15 @@ function makeObservable(val: any): Class<typeof val> {
|
|
|
229
314
|
return this;
|
|
230
315
|
}
|
|
231
316
|
|
|
317
|
+
/**
|
|
318
|
+
Notifies registered callbacks with the current observable data.
|
|
319
|
+
|
|
320
|
+
Clones the internal value (if it's an object and not an array) before passing
|
|
321
|
+
to callbacks. Respects the internal `#notify` flag that can suppress notifications.
|
|
322
|
+
|
|
323
|
+
@param notify - Optional boolean override for the notification state.
|
|
324
|
+
@returns This instance for chaining.
|
|
325
|
+
*/
|
|
232
326
|
notify(notify?: boolean) {
|
|
233
327
|
let data =
|
|
234
328
|
isObservableObject(this.valueOf()) && this.valueOf() instanceof Array === false
|
|
@@ -255,11 +349,28 @@ function makeObservable(val: any): Class<typeof val> {
|
|
|
255
349
|
return ObservableClass;
|
|
256
350
|
}
|
|
257
351
|
|
|
352
|
+
/**
|
|
353
|
+
Retrieves or creates an observable wrapper class for the given value.
|
|
354
|
+
|
|
355
|
+
@param val - The value whose constructor should be wrapped with observable behavior.
|
|
356
|
+
@returns A class that wraps the original constructor and implements observable functionality.
|
|
357
|
+
*/
|
|
258
358
|
function getConstructor(val: any): Class<typeof val> {
|
|
259
359
|
let key: string = val.constructor.name;
|
|
260
360
|
return classRepo[key] || makeObservable(val);
|
|
261
361
|
}
|
|
262
362
|
|
|
363
|
+
/**
|
|
364
|
+
Makes a target object's properties observable by replacing them with wrapped values.
|
|
365
|
+
|
|
366
|
+
Wraps each property of `target` that is not already observable (and meets criteria)
|
|
367
|
+
in an observable wrapper. Also sets up deep change detection for objects and
|
|
368
|
+
debounced notifications when the data changes.
|
|
369
|
+
|
|
370
|
+
@param target - The LooseObject whose properties should be made observable.
|
|
371
|
+
@param propertyName - Optional specific property name to make one property observable instead of all properties.
|
|
372
|
+
@returns The original target object, now with observable properties.
|
|
373
|
+
*/
|
|
263
374
|
export const observe = (target: LooseObject, propertyName?: string): LooseObject => {
|
|
264
375
|
let keys = !propertyName ? [...Object.getOwnPropertyNames(target)] : [propertyName];
|
|
265
376
|
|
|
@@ -267,7 +378,7 @@ export const observe = (target: LooseObject, propertyName?: string): LooseObject
|
|
|
267
378
|
if (
|
|
268
379
|
target.hasOwnProperty(key) &&
|
|
269
380
|
typeof target[key] !== 'function' &&
|
|
270
|
-
!target?.[key]?.constructor?.prototype?.
|
|
381
|
+
!target?.[key]?.constructor?.prototype?.subscribe
|
|
271
382
|
) {
|
|
272
383
|
if (key[0] === '_' || target?.[key] === undefined) return target;
|
|
273
384
|
let val: any = target[key];
|
|
@@ -297,7 +408,7 @@ export const observe = (target: LooseObject, propertyName?: string): LooseObject
|
|
|
297
408
|
let iso = isObservableObject(o.valueOf()) && o.valueOf() instanceof Array === false;
|
|
298
409
|
if (iso) {
|
|
299
410
|
// is observable object
|
|
300
|
-
if (o?.constructor?.prototype?.
|
|
411
|
+
if (o?.constructor?.prototype?.subscribe === undefined) {
|
|
301
412
|
// not observed yet
|
|
302
413
|
o = new (getConstructor(val) as Class<typeof val>)(
|
|
303
414
|
structuredClone(Object.assign({}, val))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twobirds/microcomponents",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"description": "Micro Components Organization Class",
|
|
5
5
|
"main": "./dist/microcomponents.ts",
|
|
6
6
|
"types": "./dist/microcomponents.d.ts",
|
|
@@ -16,14 +16,14 @@
|
|
|
16
16
|
"sideEffects": false,
|
|
17
17
|
"scripts": {
|
|
18
18
|
"format": "prettier --write \"src/**/*.{ts,js}\"",
|
|
19
|
-
"lint": "eslint \"src/**/*.
|
|
20
|
-
"typedoc": "typedoc",
|
|
19
|
+
"lint": "eslint \"src/**/*.ts\" --fix > eslint.log",
|
|
20
|
+
"typedoc": "typedoc || true",
|
|
21
21
|
"webpack": "webpack",
|
|
22
22
|
"clean": "rm -rf build/*; rm -rf dist/*; rm -rf public/*;",
|
|
23
|
-
"prep": "cp -
|
|
24
|
-
"copyToDist": "cp build/*.js dist; cp
|
|
25
|
-
"copyToPublic1": "cp
|
|
26
|
-
"copyToPublic2": "cp
|
|
23
|
+
"prep": " mkdir build || true; cp -r src/* build/ || true; mkdir public || true; npm run format; npm run lint;",
|
|
24
|
+
"copyToDist": "cp build/*.js dist; cp -r build/*.ts* dist; cp *.svg dist;",
|
|
25
|
+
"copyToPublic1": "cp -r build/test public; cp build/* public/test/; cp build/* public; cp -r assets/* public || true; cp -r build/src/test/* public/test || true;",
|
|
26
|
+
"copyToPublic2": "cp dist/MicroComponents.svg public; cp public/MicroComponents.svg public/test;",
|
|
27
27
|
"build": "npm run clean; npm run prep; tsc; npm run webpack; npm run copyToDist; npm run copyToPublic1; npm run copyToPublic2;",
|
|
28
28
|
"dev": "cd public; php -S 0.0.0.0:10000; cd ..",
|
|
29
29
|
"doc": "cd public/typedoc; php -S 0.0.0.0:11000; cd ../..",
|