@twobirds/microcomponents 0.0.80 → 0.0.81

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/dist/MC.js ADDED
@@ -0,0 +1,379 @@
1
+ 'use strict';
2
+ import { McEvent } from './McEvent.js';
3
+ var isBrowser = new Function('try {return this===window;}catch(e){ return false;}');
4
+ let nativeEventNames = new Set();
5
+ if (isBrowser()) {
6
+ Object.keys(HTMLElement.prototype)
7
+ .filter((key) => /^on/.test(key))
8
+ .forEach((eventName) => nativeEventNames.add(eventName));
9
+ }
10
+ function getMicroComponents(elements, search) {
11
+ let mcValues = [];
12
+ elements.forEach((e) => {
13
+ if (e) {
14
+ if (e instanceof HTMLElement) {
15
+ const _mc = e?._mc;
16
+ if (!search && _mc) {
17
+ mcValues = mcValues.concat(Object.values(e._mc));
18
+ }
19
+ else if (search && _mc && _mc[search]) {
20
+ mcValues.push(_mc[search]);
21
+ }
22
+ }
23
+ }
24
+ });
25
+ return mcValues;
26
+ }
27
+ function traverseNodes(node, search, firstonly = true) {
28
+ let mcValues = [];
29
+ while (node) {
30
+ if (node._mc) {
31
+ if (!search) {
32
+ mcValues = mcValues.concat(Object.values(node._mc));
33
+ }
34
+ else if (search && node._mc[search]) {
35
+ mcValues.push(node._mc[search]);
36
+ }
37
+ if (firstonly)
38
+ return mcValues;
39
+ }
40
+ node = node.parentNode;
41
+ }
42
+ return mcValues;
43
+ }
44
+ function isDOM(element) {
45
+ return element && element instanceof HTMLElement;
46
+ }
47
+ function addListener(domNode, eventName, handler, capture = false, once = false) {
48
+ let options = { once: false };
49
+ if (capture) {
50
+ options.capture = capture;
51
+ }
52
+ if (once) {
53
+ options.once = once;
54
+ }
55
+ domNode.addEventListener(eventName, handler, options);
56
+ }
57
+ function removeListener(domNode, eventName, handler) {
58
+ domNode.removeEventListener(eventName, handler);
59
+ }
60
+ function add(target, key, element = {}) {
61
+ let node = target;
62
+ if (!node._mc) {
63
+ let debug = DC?.debug;
64
+ DC.debug = false;
65
+ node._mc = new DC(target);
66
+ DC.debug = debug;
67
+ delete node._mc._mc;
68
+ }
69
+ const _mc = node._mc;
70
+ if (key.length && !_mc[key]) {
71
+ _mc[key] = element;
72
+ if (target instanceof HTMLElement && !target.getAttribute('_mc')) {
73
+ target.setAttribute('_mc', '');
74
+ }
75
+ }
76
+ return element;
77
+ }
78
+ function remove(target, key) {
79
+ if (!key)
80
+ return;
81
+ const _mc = target?._mc || {};
82
+ if (_mc && _mc[key]) {
83
+ delete _mc[key];
84
+ }
85
+ if (!Object.keys(_mc).length &&
86
+ target instanceof HTMLElement &&
87
+ target.hasAttribute('_mc')) {
88
+ target.removeAttribute('_mc');
89
+ }
90
+ return;
91
+ }
92
+ function trigger(target, ev, data = {}, bubble = 'l') {
93
+ let _mc = target?._mc;
94
+ if (!_mc)
95
+ return;
96
+ let event = ev instanceof McEvent
97
+ ? new McEvent(ev.type, ev.data, 'l')
98
+ : new McEvent(ev, data, 'l'), mcEvent = typeof ev !== 'string' ? ev : new McEvent(ev, data, bubble);
99
+ [...Object.values(_mc)].forEach((mc) => {
100
+ if (mc?.trigger && typeof mc?.trigger === 'function') {
101
+ mc?.trigger(event);
102
+ }
103
+ });
104
+ if (/[ud]/.test(mcEvent.bubble)) {
105
+ bubbleEvent(target, mcEvent);
106
+ }
107
+ return;
108
+ }
109
+ function init(element) {
110
+ if (element.constructor === DC)
111
+ return;
112
+ setTimeout(() => {
113
+ element.trigger('Init');
114
+ }, 0);
115
+ }
116
+ function autoAttachListeners(element) {
117
+ if (!isBrowser() || element.constructor === DC)
118
+ return;
119
+ const target = element.target;
120
+ if (target instanceof HTMLElement) {
121
+ let that = element;
122
+ Object.getOwnPropertyNames(that.constructor.prototype)
123
+ .filter((key) => /^on[A-Z]|one[A-Z]/.test(key) &&
124
+ typeof that.constructor.prototype[key] === 'function')
125
+ .forEach((key) => {
126
+ let once = /^one/.test(key), withoutOnOne = key.replace(/^on[e]{0,1}/, ''), nativeEventName = withoutOnOne.toLowerCase(), listener = (ev) => {
127
+ that.trigger(withoutOnOne, ev);
128
+ };
129
+ if (once) {
130
+ Object.defineProperty(element, 'on' + withoutOnOne, {
131
+ configurable: true,
132
+ enumerable: false,
133
+ writable: true,
134
+ value: (ev) => {
135
+ that.constructor.prototype[key].bind(that)(ev);
136
+ },
137
+ });
138
+ }
139
+ if (nativeEventNames.has('on' + nativeEventName)) {
140
+ addListener(target, nativeEventName, listener, false, once);
141
+ }
142
+ });
143
+ }
144
+ }
145
+ function bubbleEvent(target, ev) {
146
+ if (/[ud]/.test(ev.bubble)) {
147
+ setTimeout(() => {
148
+ if (ev.bubble.indexOf('l') === -1) {
149
+ ev.bubble += 'l';
150
+ }
151
+ if (ev.bubble.indexOf('u') > -1) {
152
+ let parent = traverseNodes(target, '', true)?.[0].target;
153
+ if (parent)
154
+ DC.trigger(parent, ev);
155
+ }
156
+ if (ev.bubble.indexOf('d') > -1) {
157
+ let targets = new Set(target._mc
158
+ .children()
159
+ .map((c) => c?.target));
160
+ targets.forEach((target) => {
161
+ if (target)
162
+ DC.trigger(target, ev);
163
+ });
164
+ }
165
+ }, 0);
166
+ }
167
+ }
168
+ class McBase {
169
+ #target;
170
+ constructor(target, debug = false) {
171
+ let that = this, element = that;
172
+ this.#target = new WeakRef(target || {});
173
+ init(element);
174
+ autoAttachListeners(element);
175
+ }
176
+ get target() {
177
+ return this.#target.deref();
178
+ }
179
+ }
180
+ class MC extends McBase {
181
+ _mc = {};
182
+ constructor(target) {
183
+ super(target);
184
+ }
185
+ static trigger = trigger;
186
+ trigger(ev, data = {}, bubble = 'l') {
187
+ let that = this;
188
+ if (that.constructor === MC || that.constructor === DC) {
189
+ let event = ev instanceof McEvent
190
+ ? new McEvent(ev.type, ev.data, 'l')
191
+ : new McEvent(ev, data, 'l'), mcEvent = typeof ev !== 'string'
192
+ ? ev
193
+ : new McEvent(ev, data, bubble);
194
+ [...Object.getOwnPropertyNames(that)].forEach((mc) => {
195
+ if (that[mc]?.trigger &&
196
+ typeof that[mc]?.trigger === 'function') {
197
+ that[mc]?.trigger(event);
198
+ }
199
+ });
200
+ if (/[ud]/.test(mcEvent.bubble)) {
201
+ bubbleEvent(that.target, mcEvent);
202
+ }
203
+ return;
204
+ }
205
+ if (that instanceof MC || that instanceof DC) {
206
+ const that = this, mcEvent = typeof ev !== 'string'
207
+ ? ev
208
+ : new McEvent(ev, data, bubble), handlerName = 'on' + mcEvent.type[0].toUpperCase() + mcEvent.type.slice(1);
209
+ if (mcEvent.bubble.indexOf('l') > -1) {
210
+ if (!mcEvent.immediateStopped &&
211
+ typeof that[handlerName] === 'function') {
212
+ that[handlerName](mcEvent);
213
+ if (that.hasOwnProperty(handlerName)) {
214
+ delete that[handlerName];
215
+ }
216
+ }
217
+ }
218
+ if (mcEvent.stopped || mcEvent.bubble === 'l') {
219
+ return that;
220
+ }
221
+ if (/[ud]/.test(mcEvent.bubble)) {
222
+ bubbleEvent(that.target, mcEvent);
223
+ }
224
+ }
225
+ }
226
+ }
227
+ class DC extends McBase {
228
+ _mc = {};
229
+ constructor(target, debug = false) {
230
+ super(target);
231
+ }
232
+ static add = add;
233
+ static remove = remove;
234
+ static trigger = trigger;
235
+ trigger(ev, data = {}, bubble) {
236
+ MC.prototype.trigger.call(this, ev, data, bubble);
237
+ }
238
+ parent(search = '') {
239
+ let target = this.target;
240
+ if (!isDOM(target))
241
+ return [];
242
+ return traverseNodes(target.parentNode, search, true);
243
+ }
244
+ ancestors(search = '') {
245
+ let target = this?.target;
246
+ if (!isDOM(target))
247
+ return [];
248
+ return traverseNodes(this.target.parentNode, search, false);
249
+ }
250
+ children(search = '') {
251
+ let target = this?.target;
252
+ if (!isDOM(target))
253
+ return [];
254
+ const id = Math.random().toString().replace('.', '');
255
+ const selector = '[_mc]:not([temp_id="' + id + '"] [_mc] [_mc])';
256
+ target.setAttribute('temp_id', id);
257
+ const myDomElements = target.querySelectorAll(selector);
258
+ const mcValues = getMicroComponents([...myDomElements], search);
259
+ target.removeAttribute('temp_id');
260
+ return mcValues;
261
+ }
262
+ descendants(search = '') {
263
+ let target = this?.target;
264
+ if (!isDOM(target))
265
+ return [];
266
+ const myDomElements = target.querySelectorAll('*[_mc]');
267
+ return getMicroComponents([...myDomElements], search);
268
+ }
269
+ }
270
+ const CEs = new Map();
271
+ const MCs = new Map();
272
+ function makeLoadScript(element) {
273
+ const fileName = './' + element.split('-').join('/') + '.js';
274
+ const se = document.createElement('script');
275
+ se.setAttribute('src', fileName);
276
+ se.setAttribute('blocking', 'render');
277
+ se.async = true;
278
+ se.setAttribute('type', 'module');
279
+ se.setAttribute('name', element);
280
+ se.setAttribute('loading', '');
281
+ addListener(se, 'load', (ev) => {
282
+ se.removeAttribute('loading');
283
+ se.setAttribute('loaded', '');
284
+ });
285
+ addListener(se, 'error', (ev) => {
286
+ console.error('could not load Custom Element code from', fileName);
287
+ });
288
+ return se;
289
+ }
290
+ function onLoadCallback(script) {
291
+ setTimeout(function () {
292
+ const importFileName = script.getAttribute('src');
293
+ const elementName = script.getAttribute('name');
294
+ const se = document.createElement('script');
295
+ se.async = true;
296
+ se.setAttribute('type', 'module');
297
+ const code = `
298
+
299
+ import _ from "${importFileName}";
300
+ import { DC } from "./microcomponents.js";
301
+
302
+ const elements = Array.from(document.querySelectorAll('[_mc*="${elementName}"]')).filter( element => element instanceof HTMLElement );
303
+
304
+ elements.forEach((element) => {
305
+ // add class to MCs repository
306
+ autoload.state.MCs.set( '${elementName}', _ );
307
+ // add instance to HTMLElement
308
+ DC.add(element, _.name, new _(element));
309
+ // remove instance name from elements "_mc" attribute
310
+ element.setAttribute('_mc', element.getAttribute('_mc').replace('${elementName}', '').replace( /\w\w/g, ' ').trim());
311
+ });
312
+
313
+ setTimeout( () => {
314
+ document.body.querySelector('script[name="${elementName}]"')?.remove();
315
+ },0);
316
+
317
+ `;
318
+ se.innerHTML = code;
319
+ document.body.append(se);
320
+ }, 0);
321
+ }
322
+ function loadUndefinedMicroComponents() {
323
+ [...document.querySelectorAll(':not(:defined)')]
324
+ .filter((element) => !!element
325
+ && !customElements.get(element.tagName.toLowerCase())
326
+ && !CEs.has(element.tagName.toLowerCase()))
327
+ .forEach((el) => {
328
+ const elementName = el.tagName.toLowerCase(), fileName = './' + elementName.split('-').join('/') + '.js';
329
+ const se = makeLoadScript(elementName);
330
+ addListener(se, 'load', () => {
331
+ CEs.set(elementName, customElements.get(el.tagName.toLowerCase()));
332
+ });
333
+ CEs.set(elementName, 'loading');
334
+ document.head.append(se);
335
+ });
336
+ [...document.querySelectorAll('[_mc]:not([_mc=""]')]
337
+ .filter((element) => !!element)
338
+ .forEach((el) => {
339
+ const elements = el.getAttribute('_mc')?.split(' ').filter(m => !!m) || [];
340
+ const elementsToBeLoaded = elements.filter(m => !MCs.has(m));
341
+ elementsToBeLoaded?.forEach(elementName => {
342
+ const se = makeLoadScript(elementName);
343
+ addListener(se, 'load', () => onLoadCallback(document.head.querySelector(`script[name="${elementName}"]`)));
344
+ MCs.set(elementName, 'loading');
345
+ document.head.append(se);
346
+ });
347
+ const elementsAlreadyLoaded = elements.filter(m => MCs.has(m) && !MCs.get(m).length);
348
+ elementsAlreadyLoaded?.forEach(elementName => {
349
+ setTimeout(() => {
350
+ DC.add(el, elementName, new (autoload.state.MCs.get(elementName))(el));
351
+ const newAttribute = el.getAttribute('_mc')
352
+ .replace('${elementName}', '')
353
+ .replace(/\w\w/g, ' ')
354
+ .trim() || '_mc';
355
+ el.setAttribute('_mc');
356
+ }, 5);
357
+ });
358
+ });
359
+ }
360
+ let autoloadEnabled = false;
361
+ let observer = null;
362
+ function autoload(enable = true) {
363
+ if (!enable) {
364
+ observer?.disconnect();
365
+ observer = null;
366
+ return autoloadEnabled;
367
+ }
368
+ autoloadEnabled = enable;
369
+ observer = new MutationObserver(loadUndefinedMicroComponents);
370
+ setTimeout(() => {
371
+ observer.observe(document.body, { childList: true, subtree: true });
372
+ loadUndefinedMicroComponents();
373
+ }, 5);
374
+ window.autoload = window.autoload || autoload;
375
+ return autoloadEnabled;
376
+ }
377
+ autoload.state = { CEs, MCs };
378
+ export { addListener, removeListener, McBase, MC, DC, McEvent, autoload };
379
+ //# sourceMappingURL=MC.js.map
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ var isBrowser = new Function("try {return this===window;}catch(e){ return false;}");
3
+ let nativeEventNames = new Set();
4
+ if (isBrowser()) {
5
+ Object.keys(HTMLElement.prototype).filter(key => /^on/.test(key)).forEach(eventName => nativeEventNames.add(eventName));
6
+ }
7
+ function isDOM(element) {
8
+ return element && element instanceof HTMLElement;
9
+ }
10
+ class McEvent {
11
+ #type;
12
+ #stopped = false;
13
+ #immediateStopped = false;
14
+ constructor(type, data = {}, bubble = 'l') {
15
+ this.#type = type;
16
+ this.data = data;
17
+ this.bubble = bubble;
18
+ }
19
+ get type() {
20
+ return this.#type;
21
+ }
22
+ get stopped() {
23
+ return this.#stopped;
24
+ }
25
+ get immediateStopped() {
26
+ return this.#immediateStopped;
27
+ }
28
+ stopPropagation() {
29
+ this.#stopped = true;
30
+ }
31
+ stopImmediatePropagation() {
32
+ this.stopPropagation();
33
+ this.#immediateStopped = true;
34
+ }
35
+ }
36
+ function getMicroComponents(elements, search) {
37
+ let mcValues = [];
38
+ elements.forEach(e => {
39
+ if (e) {
40
+ if (e instanceof HTMLElement) {
41
+ const _mc = e?._mc;
42
+ if (!search && _mc) {
43
+ mcValues = mcValues.concat(Object.values(e._mc));
44
+ }
45
+ else if (search && _mc && _mc[search]) {
46
+ mcValues.push(_mc[search]);
47
+ }
48
+ }
49
+ }
50
+ });
51
+ return mcValues;
52
+ }
53
+ function traverseNodes(node, search, firstonly = true) {
54
+ let mcValues = [];
55
+ while (node) {
56
+ if (node._mc) {
57
+ if (!search) {
58
+ mcValues = mcValues.concat(Object.values(node._mc));
59
+ }
60
+ else if (search && node._mc[search]) {
61
+ mcValues.push(node._mc[search]);
62
+ }
63
+ if (firstonly)
64
+ return mcValues;
65
+ }
66
+ node = node.parentNode;
67
+ }
68
+ return mcValues;
69
+ }
70
+ export { McEvent };
71
+ //# sourceMappingURL=McEvent.js.map
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+ import { DC } from './MC.js';
3
+ import { kebabToPascal } from './helpers.js';
4
+ class CeBaseClass extends HTMLElement {
5
+ constructor() {
6
+ super();
7
+ }
8
+ connectedCallback() {
9
+ DC.trigger(this, 'Connected');
10
+ }
11
+ disconnectedCallback() {
12
+ DC.trigger(this, 'Disconnected');
13
+ }
14
+ adoptedCallback() {
15
+ DC.trigger(this, 'Adopted');
16
+ }
17
+ attributeChangedCallback(name, oldValue, newValue) {
18
+ DC.trigger(this, 'AttributeChanged', { name, oldValue, newValue });
19
+ }
20
+ }
21
+ function createNamedClass(name, ClassBody) {
22
+ return Object.defineProperty(ClassBody, 'name', { value: name });
23
+ }
24
+ export function defineCE(tagName, DefinitionClass, observedAttributes = []) {
25
+ if (customElements.get(tagName)) {
26
+ console.warn(`IGNORED: Custom element "${tagName}" is already defined!`);
27
+ return;
28
+ }
29
+ if (!tagName.includes('-')) {
30
+ throw new Error('createCustomElement: tagName must contain at least one hyphen ("-").');
31
+ }
32
+ let name = kebabToPascal(tagName);
33
+ customElements.define(tagName, createNamedClass(name, class extends CeBaseClass {
34
+ instance;
35
+ constructor(data) {
36
+ super();
37
+ let instance = new DefinitionClass(this, data);
38
+ DC.add(this, name, instance);
39
+ }
40
+ static get observedAttributes() {
41
+ return observedAttributes;
42
+ }
43
+ }));
44
+ }
45
+ export function getCE(tagName) {
46
+ return customElements.get(tagName);
47
+ }
48
+ //# sourceMappingURL=elements.js.map
@@ -0,0 +1,162 @@
1
+ 'use strict';
2
+ import { copyGettersSetters } from './helpers.js';
3
+ import { DC, addListener } from './MC.js';
4
+ import { observe } from './observables.js';
5
+ const getInputName = (e) => {
6
+ return e.tagName.toLowerCase();
7
+ };
8
+ class InputBase {
9
+ constructor(e) { }
10
+ }
11
+ class RadioHandler extends InputBase {
12
+ #e;
13
+ constructor(e) {
14
+ super(e);
15
+ this.#e = e;
16
+ }
17
+ get val() {
18
+ let ret = new FormData(this.#e.form).get(this.#e.name);
19
+ return ret;
20
+ }
21
+ set val(value) {
22
+ let arr = [
23
+ ...this.#e.form.querySelectorAll(`input[type="radio"][name="${this.#e.name}"]`),
24
+ ];
25
+ arr
26
+ .filter((e) => e.getAttribute('value') === value)
27
+ .shift().checked = true;
28
+ }
29
+ }
30
+ class CheckboxHandler extends InputBase {
31
+ #e;
32
+ constructor(e) {
33
+ super(e);
34
+ this.#e = e;
35
+ }
36
+ get val() {
37
+ return this.#e.checked;
38
+ }
39
+ set val(value) {
40
+ this.#e.checked = value;
41
+ }
42
+ }
43
+ class InputHandler extends InputBase {
44
+ #e;
45
+ constructor(e) {
46
+ super(e);
47
+ this.#e = e;
48
+ }
49
+ get val() {
50
+ return this.#e.value;
51
+ }
52
+ set val(value) {
53
+ this.#e.value = value;
54
+ }
55
+ }
56
+ class SelectHandler extends InputBase {
57
+ #e;
58
+ #multi = false;
59
+ constructor(e) {
60
+ super(e);
61
+ this.#e = e;
62
+ this.#multi = e.multiple;
63
+ }
64
+ get val() {
65
+ const that = this, ret = [];
66
+ [...that.#e.querySelectorAll('option')]
67
+ .filter((o) => o.selected)
68
+ .forEach((o) => {
69
+ ret.push(o.value);
70
+ });
71
+ if (this.#multi)
72
+ return ret;
73
+ return ret.length ? ret[0] : '';
74
+ }
75
+ set val(value) {
76
+ const that = this;
77
+ value = typeof value === 'string' ? [value] : value;
78
+ [...that.#e.querySelectorAll('option')].forEach((o) => {
79
+ if (value.indexOf(o.value) > -1) {
80
+ o.selected = true;
81
+ }
82
+ else {
83
+ o.selected = false;
84
+ }
85
+ });
86
+ }
87
+ }
88
+ class TextAreaHandler extends InputBase {
89
+ #e;
90
+ constructor(e) {
91
+ super(e);
92
+ this.#e = e;
93
+ }
94
+ get val() {
95
+ return this.#e.value || '';
96
+ }
97
+ set val(value) {
98
+ this.#e.value = value;
99
+ }
100
+ }
101
+ const getInputHandlers = (e) => {
102
+ let inputList = [
103
+ ...(e || document).querySelectorAll('input,select,textarea'),
104
+ ], handlers = {};
105
+ inputList.forEach((i) => {
106
+ const name = i.getAttribute('name');
107
+ if (name) {
108
+ let handler = false;
109
+ switch (getInputName(i)) {
110
+ case 'input':
111
+ switch (i.type) {
112
+ case 'radio':
113
+ handler = new RadioHandler(i);
114
+ break;
115
+ case 'checkbox':
116
+ handler = new CheckboxHandler(i);
117
+ break;
118
+ default:
119
+ handler = new InputHandler(i);
120
+ }
121
+ break;
122
+ case 'select':
123
+ handler = new SelectHandler(i);
124
+ break;
125
+ case 'textarea':
126
+ handler = new TextAreaHandler(i);
127
+ break;
128
+ default:
129
+ console.warn('handler missing:', name, 'for', i.tagName.toLowerCase(), i);
130
+ break;
131
+ }
132
+ if (handler) {
133
+ DC.add(i, name, handler);
134
+ Object.defineProperty(handlers, name, {
135
+ enumerable: true,
136
+ configurable: true,
137
+ get: () => {
138
+ return handler.val;
139
+ },
140
+ set: (value) => {
141
+ if (handler.val !== value) {
142
+ handler.val = value;
143
+ }
144
+ },
145
+ });
146
+ }
147
+ }
148
+ });
149
+ return handlers;
150
+ };
151
+ export function formValues(targetObject, propertyName, dom) {
152
+ let handlers = getInputHandlers(dom);
153
+ targetObject[propertyName] = Object.assign({}, handlers);
154
+ observe(targetObject, propertyName);
155
+ copyGettersSetters(handlers, targetObject[propertyName]);
156
+ [...dom.querySelectorAll('input, textarea, select')].forEach((input) => {
157
+ addListener(input, 'input', (ev) => {
158
+ targetObject[propertyName].notify();
159
+ });
160
+ });
161
+ }
162
+ //# sourceMappingURL=formValues.js.map