@twobirds/microcomponents 0.1.21 → 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.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +91 -92
  3. package/dist/MC.min.js +1 -345
  4. package/dist/autoload.min.js +1 -0
  5. package/dist/core/MC.d.ts +401 -0
  6. package/dist/core/MC.d.ts.map +1 -0
  7. package/dist/core/MC.js +471 -0
  8. package/dist/core/autoload.d.ts +54 -0
  9. package/dist/core/autoload.d.ts.map +1 -0
  10. package/dist/core/autoload.js +158 -0
  11. package/dist/core/elements.d.ts +232 -0
  12. package/dist/core/elements.d.ts.map +1 -0
  13. package/dist/core/elements.js +123 -0
  14. package/dist/core/events.d.ts +231 -0
  15. package/dist/core/events.d.ts.map +1 -0
  16. package/dist/core/events.js +228 -0
  17. package/dist/core/mcGuards.d.ts +49 -0
  18. package/dist/core/mcGuards.d.ts.map +1 -0
  19. package/dist/core/mcGuards.js +19 -0
  20. package/dist/elements.min.js +1 -1
  21. package/dist/events.min.js +1 -0
  22. package/dist/formValues.min.js +1 -1
  23. package/dist/mcGuards.min.js +1 -0
  24. package/dist/microcomponents.d.ts +8 -5
  25. package/dist/microcomponents.d.ts.map +1 -1
  26. package/dist/microcomponents.js +8 -5
  27. package/dist/microcomponents.min.js +1 -1
  28. package/dist/observables.min.js +1 -1
  29. package/dist/state/formValues.d.ts +70 -0
  30. package/dist/state/formValues.d.ts.map +1 -0
  31. package/dist/state/formValues.js +334 -0
  32. package/dist/state/observables.d.ts +276 -0
  33. package/dist/state/observables.d.ts.map +1 -0
  34. package/dist/state/observables.js +382 -0
  35. package/dist/state/placeholders.d.ts +62 -0
  36. package/dist/state/placeholders.d.ts.map +1 -0
  37. package/dist/state/placeholders.js +181 -0
  38. package/dist/types/mcTypes.d.ts +201 -0
  39. package/dist/types/mcTypes.d.ts.map +1 -0
  40. package/dist/types/mcTypes.js +9 -0
  41. package/dist/utils/helpers.d.ts +195 -0
  42. package/dist/utils/helpers.d.ts.map +1 -0
  43. package/dist/utils/helpers.js +340 -0
  44. package/package.json +96 -62
  45. package/dist/MC.d.ts +0 -31
  46. package/dist/MC.d.ts.map +0 -1
  47. package/dist/MC.js +0 -249
  48. package/dist/MC.ts +0 -613
  49. package/dist/MicroComponents.svg +0 -15
  50. package/dist/elements.d.ts +0 -5
  51. package/dist/elements.d.ts.map +0 -1
  52. package/dist/elements.js +0 -49
  53. package/dist/elements.ts +0 -114
  54. package/dist/events.d.ts +0 -11
  55. package/dist/events.d.ts.map +0 -1
  56. package/dist/events.js +0 -55
  57. package/dist/events.ts +0 -158
  58. package/dist/formValues.d.ts +0 -3
  59. package/dist/formValues.d.ts.map +0 -1
  60. package/dist/formValues.js +0 -156
  61. package/dist/formValues.ts +0 -371
  62. package/dist/helpers.d.ts +0 -15
  63. package/dist/helpers.d.ts.map +0 -1
  64. package/dist/helpers.js +0 -113
  65. package/dist/helpers.ts +0 -219
  66. package/dist/main.mjs +0 -1
  67. package/dist/microcomponents.ts +0 -6
  68. package/dist/observables.d.ts +0 -11
  69. package/dist/observables.d.ts.map +0 -1
  70. package/dist/observables.js +0 -228
  71. package/dist/observables.ts +0 -449
package/dist/helpers.ts DELETED
@@ -1,219 +0,0 @@
1
- /**
2
- * Determines whether the current runtime environment is a web browser.
3
- *
4
- * This function works by attempting to access `this` within an anonymous function
5
- * and checking if it refers to the global `window` object. It returns `true`
6
- * when executed in a browser context, and `false` otherwise (e.g., Node.js).
7
- *
8
- * Note: While modern environments typically provide more reliable detection mechanisms,
9
- * this fallback remains compatible across older runtimes where other detection techniques
10
- * may be unreliable.
11
- *
12
- * @returns {boolean} `true` if the code is running in a browser environment, `false` otherwise.
13
- */
14
- export function isBrowser() {
15
- return window ? true : false;
16
- }
17
-
18
- /**
19
- * Interface for objects that can have string or symbol keys with any type of values.
20
- */
21
- export interface LooseObject {
22
- [key: string | symbol]: any;
23
- }
24
-
25
- /**
26
- * Checks if a value is a native JavaScript object (created via {} or new Object()).
27
- * @param value - The value to check.
28
- * @returns True if the value is a plain object, false otherwise.
29
- */
30
- export function isNativeObject(value: any): boolean {
31
- return value && value['constructor'] && value.constructor === Object;
32
- }
33
-
34
- /**
35
- * Recursively sorts all keys in an object alphabetically, preserving nested structure.
36
- * @param o - The object to sort.
37
- * @returns A new object with sorted keys at all levels.
38
- */
39
- export function sortObject(o: LooseObject): LooseObject {
40
- let result: LooseObject = {},
41
- keys = [...Object.keys(o)].sort();
42
-
43
- keys.forEach((key) => {
44
- if (isNativeObject(o[key])) {
45
- result[key] = sortObject(o[key]);
46
- } else {
47
- result[key] = o[key];
48
- }
49
- });
50
-
51
- return result;
52
- }
53
-
54
- /**
55
- * Flattens a nested object into a single-level object with dot-separated keys.
56
- * Optionally sorts the resulting flat keys alphabetically (default: true).
57
- * @param o - The object to flatten.
58
- * @param doSort - Whether to sort the resulting keys (default: true).
59
- * @returns A flattened object with dot-notation keys.
60
- */
61
- export function flattenObject(o: LooseObject, doSort: boolean = true): LooseObject {
62
- let result: LooseObject = {};
63
-
64
- [...Object.getOwnPropertyNames(o)].forEach((propName) => {
65
- if (o[propName] instanceof Object) {
66
- const temp = flattenObject(o[propName], false);
67
- result[propName] = {};
68
- for (const j in temp) {
69
- result[propName + '.' + j] = temp[j];
70
- }
71
- } else {
72
- result[propName] = o[propName];
73
- }
74
- });
75
-
76
- return doSort ? sortObject(result) : result;
77
- }
78
-
79
- /**
80
- * Compares two objects for deep equality by flattening them and comparing their JSON representations.
81
- * @param o1 - The first object to compare.
82
- * @param o2 - The second object to compare.
83
- * @returns True if the objects are deeply equal, false otherwise.
84
- */
85
- export function deepEqual(o1: LooseObject, o2: LooseObject): boolean {
86
- return JSON.stringify(flattenObject(o1)) === JSON.stringify(flattenObject(o2));
87
- }
88
-
89
- /**
90
- * Retrieves a nested value from an object using a namespace path (dot-separated).
91
- * @param ns - The namespace string or array of keys representing the path.
92
- * @param obj - The object to traverse.
93
- * @returns The value at the specified namespace path, or undefined if not found.
94
- */
95
- export function nameSpace(ns: string | string[], obj: LooseObject = {}): any {
96
- let nsa = ns.constructor === String ? ns.split('.') : ns;
97
-
98
- if (!obj || !nsa[0] || !obj[nsa[0]]) {
99
- console.warn('ns', nsa[0], 'not in', obj);
100
- return;
101
- }
102
- if (obj[nsa[0]].constructor === Object && nsa.length > 1) {
103
- return nameSpace((nsa as string[]).splice(1), obj[nsa[0]]);
104
- }
105
- return obj[nsa[0]];
106
- }
107
-
108
- /**
109
- * Parses a template string or object, replacing placeholders in the form of {key.path} with
110
- * corresponding values from the provided data object using namespace resolution.
111
- * Also recursively processes objects and arrays within the input.
112
- * @param what - The string or object to parse (can contain {{placeholder}} expressions).
113
- * @param parseThis - The object containing values for placeholder substitution.
114
- * @returns The parsed result, with placeholders replaced by actual values.
115
- */
116
- export function parse(what: string | object | any[], parseThis: object) {
117
- var args = Array.from(arguments);
118
-
119
- if (args.length > 2) {
120
- while (args.length > 1) {
121
- args[0] = parse(args[0], args[1]);
122
- args.splice(1, 1);
123
- }
124
- return args[0];
125
- }
126
-
127
- if (typeof what === 'string') {
128
- what.match(/\{[^\{\}]*\}/g)?.forEach(function (pPropname) {
129
- var propname = pPropname.substring(1, pPropname.length - 1),
130
- value = nameSpace(propname, parseThis);
131
- //console.log(propname, value);
132
- if (typeof value !== 'undefined') {
133
- what = (what as string).replace(pPropname, value);
134
- }
135
- });
136
- } else if (isNativeObject(what)) {
137
- switch (what.constructor) {
138
- case Object:
139
- Object.keys(what).forEach(function (pKey) {
140
- if (what.hasOwnProperty(pKey)) {
141
- (what as LooseObject)[pKey] = parse((what as LooseObject)[pKey], parseThis);
142
- }
143
- });
144
- break;
145
- case Array:
146
- (what as Array<any>).forEach(function (pValue, pKey, original) {
147
- original[pKey] = parse((what as any)[pKey], parseThis);
148
- });
149
- break;
150
- }
151
- }
152
-
153
- return what;
154
- }
155
-
156
- /**
157
- * Converts a kebab-case string to PascalCase.
158
- * Example: 'my-component-name' -> 'MyComponentName'
159
- * @param str - The kebab-case string to convert.
160
- * @returns The converted PascalCase string.
161
- */
162
- export function kebabToPascal(str: string) {
163
- return str
164
- .split('-') // Split by hyphens
165
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) // Capitalize each part
166
- .join(''); // Join without spaces
167
- }
168
-
169
- /**
170
- * Creates a debounced version of a function that delays execution until after the specified
171
- * number of milliseconds have elapsed since the last time this function was invoked.
172
- * Useful for rate-limiting expensive operations like event handlers or API calls.
173
- * @param func - The function to debounce.
174
- * @param milliseconds - The number of milliseconds to delay execution.
175
- * @returns A debounced version of the input function.
176
- */
177
- export function debounce(func: Function, milliseconds: number): Function {
178
- let timeout: any;
179
- return () => {
180
- clearTimeout(timeout);
181
-
182
- timeout = setTimeout(function () {
183
- func(...arguments);
184
- }, milliseconds);
185
- };
186
- }
187
-
188
- /**
189
- * Parses an HTML string and returns an array of DOM elements.
190
- * Handles self-closing tags (XHTML style) by converting them to full closing tags first.
191
- * @param html - The HTML string to parse.
192
- * @returns An array of HTMLElement objects representing the parsed HTML nodes.
193
- */
194
- export function htmlToElements(html: string): HTMLElement[] {
195
- let template = document.createElement('template');
196
-
197
- html = html.replace(/<([A-Za-z0-9\-]*)([^>\/]*)(\/>)/gi, '<$1$2></$1>'); // replace XHTML closing tags by full closing tags
198
-
199
- template.innerHTML = html;
200
-
201
- template.content.normalize();
202
-
203
- return Array.from(template.content.childNodes) as HTMLElement[];
204
- }
205
-
206
- /**
207
- * Copies all getter and setter properties from a source object to a target object.
208
- * Does not copy methods or regular data properties—only accessor properties (get/set).
209
- * @param source - The source object with getters and/or setters to copy.
210
- * @param target - The target object to which the accessors will be copied.
211
- */
212
- export function copyGettersSetters(source: LooseObject, target: LooseObject) {
213
- const descriptors = Object.getOwnPropertyDescriptors(source);
214
- for (const [key, descriptor] of Object.entries(descriptors)) {
215
- if ('get' in descriptor || 'set' in descriptor) {
216
- Object.defineProperty(target, key, descriptor);
217
- }
218
- }
219
- }
package/dist/main.mjs DELETED
@@ -1 +0,0 @@
1
- function e(t){let n={};return[...Object.keys(t)].sort().forEach((a=>{var l;(l=t[a])&&l.constructor&&l.constructor===Object?n[a]=e(t[a]):n[a]=t[a]})),n}function t(n,a=!0){let l={};return[...Object.getOwnPropertyNames(n)].forEach((e=>{if(n[e]instanceof Object){const a=t(n[e],!1);l[e]={};for(const t in a)l[e+"."+t]=a[t]}else l[e]=n[e]})),a?e(l):l}function n(e,t){const n=Object.getOwnPropertyDescriptors(e);for(const[e,a]of Object.entries(n))("get"in a||"set"in a)&&Object.defineProperty(t,e,a)}function a(e){return"_mc"+e[0].toUpperCase()+e.substring(1)}function l(e,t,n,l=!1){return e.addEventListener(a(t),n,{capture:!1,once:l}),n}class r{stopped=!1;constructor(){}stop(){this.stopped=!0}}function s(e,t,n={},l="l",s){const i=/^_mc/.test(t)?t:a(t),o=s||new r,c=new CustomEvent(i,{detail:{data:n,control:o},bubbles:!1});o.stopped||/l/.test(l)&&(e.dispatchEvent(c),c.stopImmediatePropagation(),o.stopped)||/[ud]/.test(l)&&(-1===l.indexOf("l")&&(l+="l"),setTimeout((()=>{if(l.indexOf("u")>-1){let t=e.parentElement?.closest("[data-mc]");t&&t?._mc.trigger(i,n,l,o)}if(l.indexOf("d")>-1){let t=e._mc.children();t&&t.forEach((e=>e.trigger(i,n,l)))}}),0))}const i=new Function("try {return this===window;}catch(e){ return false;}");function o(e){return e&&e instanceof HTMLElement}class c{_target;constructor(e){var t;this._target=new WeakRef(e||{}),function(e){if(!i()||e.constructor===u)return;const t=e.target;if(t instanceof HTMLElement){let n=e;Object.getOwnPropertyNames(n.constructor.prototype).filter((e=>/^(on[A-Z]|one[A-Z])/.test(e)&&"function"==typeof n.constructor.prototype[e])).forEach((e=>{let a=/^one/.test(e),r=e.replace(/^on[e]{0,1}/,""),s=r.toLowerCase();if("on"+s in t){let a=t=>{n[e](t)};t.addEventListener(s,a)}else l(t,r,(t=>{n[e](t)}),a)}))}}(this),t=this,setTimeout((()=>{t.constructor!==u&&t.trigger("Init")}),0)}get target(){return this._target.deref()}}class u extends c{constructor(e){super(e)}static add(e,t,n={}){return function(e,t,n={}){let a=e;a._mc||(a._mc=new u(e));const l=a._mc;return t.length&&!l[t]&&(l[t]=n,e instanceof HTMLElement&&!e.getAttribute("data-mc")&&e.setAttribute("data-mc","")),a}(e,t,n)}static remove(e,t){!function(e,t){if(!t)return;const n=e?._mc||{};n&&n[t]&&delete n[t],!Object.keys(n).length&&e instanceof HTMLElement&&e.hasAttribute("data-mc")&&e.removeAttribute("data-mc")}(e,t)}off(e,t){!function(e,t,n){e.removeEventListener(a(t),n)}(this.target,e,t)}on(e,t){return l(this.target,e,t)}one(e,t){return function(e,t,n){return l(e,t,n,!0),n}(this.target,e,t)}trigger(e,t={},n="l",a){s(this.target,e,t,n,a)}ancestors(){let e=this?.target,t=[];if(!o(e))return[];for(;e=e.closest("[data-mc]");)e?._mc&&t.push(e?._mc);return t.length?t:null}closest(){let e=this.target;return o(e)&&e.closest("[data-mc]")?._mc||null}children(){let e=this?.target;return o(e)?function(e){const t=Math.random().toString().replace(".",""),n='[data-mc]:not([temp_id="'+t+'"] [data-mc] [data-mc])';e.setAttribute("temp_id",t);const a=[...e.querySelectorAll(n)].filter((e=>!!e._mc)).map((e=>e._mc));return e.removeAttribute("temp_id"),a.length?a:null}(e):null}descendants(){const e=this?.target;if(!o(e))return[];const t=[...e.querySelectorAll("[data-mc]")].filter((e=>!!e._mc)).map((e=>e._mc));return t.length?t:null}}function p(e){return null!=e&&"object"==typeof e}function d(e){let t=new Set,n=/\{[^\{\}]*\}/g;return(e.getAttributeNames?e.getAttributeNames():[]).filter((t=>e.getAttribute(t)?.match(n))).forEach((a=>{new Set(e.getAttribute(a)?.match(n)).forEach((e=>t.add(e.replace(/[\{\}]/g,""))))})),[...e.childNodes].filter((e=>3===e.nodeType&&(e.nodeValue||"").match(n))).forEach((e=>{(e?.nodeValue||"").match(n).forEach((e=>t.add(e.replace(/[\{\}]/g,""))))})),[...t]}new Set;const b=[HTMLInputElement,HTMLSelectElement,HTMLTextAreaElement];class m extends u{data={};oldDisplay="";constructor(e){super(e),d(e).forEach((e=>this.data[e]="")),g(this),this.#e()}#e(){let e=this.target,t=!1;(e.getAttributeNames?e.getAttributeNames():[]).filter((t=>e.getAttribute(t)?.match(/\{[^\{\}]*\}/g))).forEach((t=>{let n=new Set(e.getAttribute(t)?.match(/\{[^\{\}]*\}/g));var a;this.data.subscribe((a=e.getAttribute(t),l=>{let r=a;n.forEach((e=>{r=r.replace(e,l[e.replace(/[\{\}]/g,"")])})),e.setAttribute(t,r)}))})),[...e.childNodes].filter((e=>3===e.nodeType&&(e.nodeValue||"").match(/\{[^\{\}]*\}/g))).forEach((n=>{t=!0;let a=new Set(n.nodeValue.match(/\{[^\{\}]*\}/g));this.data.subscribe((t=>l=>{let r=t;a.forEach((e=>{r=r.replace(e,l[e.replace(/[\{\}]/g,"")])})),this.oldDisplay?e.style=this.oldDisplay:e.removeAttribute("style"),delete this.oldDisplay,n.nodeValue=r})(n.nodeValue)),this.oldDisplay=e.getAttribute("style")||"",t&&!b.includes(e.constructor)&&"FIELDSET"!==e.tagName&&(e.style.display="none")}))}onData(e){let t={};Object.keys(e.detail.data).forEach((n=>{this.data.hasOwnProperty(n)&&(t[n]=e.detail.data[n])})),JSON.stringify(t)!==JSON.stringify(this.data)&&Object.assign(this.data,t)}}const h={};function f(e){let n=e.constructor.name;return h[n]||function(e){const n=e.constructor,a=n.name;let l=class extends n{#t=!0;#n=[];constructor(e){super(e),this.constructor.callbacks=[],p(e)&&Object.getOwnPropertyNames(e).forEach((t=>{this[t]=e[t]}))}get callbacks(){return this.#n}set callbacks(e){this.#n=e}subscribe(e){return this.#n.push(e),this}bind(e){if(p(this.valueOf())){!function(e){var t;(t=e,[t].concat([...t.querySelectorAll("*")]).filter((e=>!!d(e).length)).filter((e=>"STYLE"!==e?.tagName)).reverse()).forEach((e=>{e?._mc?._placeholders||u.add(e,"_placeholders",new m(e))}))}(e);const n=[e,...e.querySelectorAll("[data-mc]")].filter((e=>!!e?._mc?._placeholders)),a=function(e){let a=t(e);n.forEach((e=>{e&&s(e,"data",a)}))};this.#n.push(a),this.notify()}else console.warn("IGNORED - cannot bind a non-Object to the dom, IS:",typeof this.valueOf(),this);return this}notify(e){let t=p(this.valueOf())&&this.valueOf()instanceof Array==0?structuredClone(Object.assign({},this)):this.valueOf();return void 0!==e&&(this.#t=e),this.#t&&this.#n.forEach((e=>{e(t)})),this}};return h[a]=l,l}(e)}const g=(e,a)=>((a?[a]:[...Object.getOwnPropertyNames(e)]).forEach((a=>{if(e.hasOwnProperty(a)&&"function"!=typeof e[a]&&!e?.[a]?.constructor?.prototype?.subscribe){if("_"===a[0]||void 0===e?.[a])return e;let l=e[a],r=new(f(l))(l),s=function(){let e;return()=>{clearTimeout(e),e=setTimeout((function(){!function(){r.notify()}(...arguments)}),0)}}();Object.defineProperty(e,a,{enumerable:!0,get(){if(p(r.valueOf())){let e=structuredClone(Object.assign({},r));setTimeout((function(){var n,a;n=structuredClone(Object.assign({},r)),a=e,JSON.stringify(t(n))!==JSON.stringify(t(a))&&s()}),0)}return r},set(e){if(typeof e==typeof r.valueOf())if(p(r.valueOf())&&r.valueOf()instanceof Array==0)void 0===r?.constructor?.prototype?.subscribe&&(r=new(f(e))(structuredClone(Object.assign({},e))),n(e,r)),Object.assign(r,e),r.notify();else{let t=r.valueOf(),n=r.callbacks;e!==t&&(r=new(f(e))(e),r.callbacks=n,s())}else console.warn("IGNORED - cannot change observable types, IS:",typeof r.valueOf(),r,"- CANNOT BECOME:",typeof e)}}),e[a]=l}})),e);class y{constructor(e){}}class v extends y{#a;constructor(e){super(e),this.#a=e}get val(){return new FormData(this.#a.form).get(this.#a.name)}set val(e){[...this.#a.form.querySelectorAll(`input[type="radio"][name="${this.#a.name}"]`)].filter((t=>t.getAttribute("value")===e)).shift().checked=!0}}class E extends y{#a;constructor(e){super(e),this.#a=e}get val(){return this.#a.checked}set val(e){this.#a.checked=e}}class O extends y{#a;constructor(e){super(e),this.#a=e}get val(){return this.#a.value}set val(e){this.#a.value=e}}class w extends y{#a;#l=!1;constructor(e){super(e),this.#a=e,this.#l=e.multiple}get val(){const e=[];return[...this.#a.querySelectorAll("option")].filter((e=>e.selected)).forEach((t=>{e.push(t.value)})),this.#l?e:e.length?e[0]:""}set val(e){e="string"==typeof e?[e]:e,[...this.#a.querySelectorAll("option")].forEach((t=>{e.indexOf(t.value)>-1?t.selected=!0:t.selected=!1}))}}class k extends y{#a;constructor(e){super(e),this.#a=e}get val(){return this.#a.value||""}set val(e){this.#a.value=e}}!function(e,t,n=[]){if(customElements.get(e))return void console.warn(`IGNORED: Custom element "${e}" is already defined!`);if(!e.includes("-"))throw new Error('createCustomElement: tagName must contain at least one hyphen ("-").');let a=e.split("-").map((e=>e.charAt(0).toUpperCase()+e.slice(1))).join("");t.prototype instanceof c?customElements.define(e,function(e,t){return Object.defineProperty(t,"name",{value:e})}(a,class extends HTMLElement{constructor(e){super(),u.add(this,a,new t(this,e))}connectedCallback(){this.dispatchEvent(new CustomEvent("_mcConnected"))}connectedMoveCallback(){this.dispatchEvent(new CustomEvent("_mcConnectedMove"))}disconnectedCallback(){this.dispatchEvent(new CustomEvent("_mcDisconnected"))}adoptedCallback(){this.dispatchEvent(new CustomEvent("_mcAdopted"))}attributeChangedCallback(e,t,n){this.dispatchEvent(new CustomEvent("_mcAttributeChanged",{detail:{name:e,oldValue:t,newValue:n}}))}formResetCallback(){this.dispatchEvent(new CustomEvent("_mcFormReset"))}static get observedAttributes(){return n}})):customElements.define(e,t)}("test-testform",class extends u{data;constructor(e){super(e);let t=this.target.attachShadow({mode:"open"});t.innerHTML=`<style>${this.css}</style>${this.template}`,function(e,t,a){let l=(e=>{let t=[...(e||document).querySelectorAll("input,select,textarea")],n={};return t.forEach((e=>{const t=e.getAttribute("name");if(t){let a=!1;switch((e=>e.tagName.toLowerCase())(e)){case"input":switch(e.type){case"radio":a=new v(e);break;case"checkbox":a=new E(e);break;default:a=new O(e)}break;case"select":a=new w(e);break;case"textarea":a=new k(e);break;default:console.warn("handler missing:",t,"for",e.tagName.toLowerCase(),e)}a&&(u.add(e,t,a),Object.defineProperty(n,t,{enumerable:!0,configurable:!0,get:()=>a.val,set:e=>{a.val!==e&&(a.val=e)}}))}})),n})(a);e[t]=Object.assign({},l),g(e,t),n(l,e[t]),[...a.querySelectorAll("input, textarea, select")].forEach((n=>{n.addEventListener("input",(n=>{e[t].notify()}))}))}(this,"data",t),this.data.bind(t),fetch("./testform.json").then((e=>e.json())).then((e=>{this.data=e}))}get template(){return'\n <form>\n \x3c!-- sample form to start with --\x3e\n <fieldset>\n <legend>Inputs</legend>\n <label>text</label><input type="text" name="textInput" value="Hallo" />&nbsp;<strong>2wB:&nbsp;</strong>{textInput}<br />\n <label>color</label><input type="color" name="colorInput" value="#000000" /><br />\n <label>date</label><input type="date" name="dateInput" value="" /><br />\n <label>datetime-local</label><input type="datetime-local" name="dtlInput" value="" /><br />\n <label>email</label><input type="email" name="email" value="" /><br />\n <label>file</label><input type="file" name="fileInput" value="" /><br />\n <label>hidden</label><input type="hidden" name="hiddenInput" value="hiddenVal" /><br />\n <label>month</label><input type="month" name="month" value="" /><br />\n <label>number</label><input type="number" name="number" value="" /><br />\n <label>password</label><input type="password" name="passInput" value="" /><br />\n <label>range</label><input type="range" name="rangeInput" value="" /><br />\n <label>search</label><input type="search" name="searchInput" value="" /><br />\n <label>tel</label><input type="tel" name="telInput" value="" /><br />\n <label>time</label><input type="time" name="timeInput" value="" /><br />\n <label>url</label><input type="url" name="urlInput" value="" /><br />\n <label>week</label><input type="week" name="weekInput" value="" /><br />\n </fieldset>\n\n <fieldset>\n <legend>Selects&nbsp;<strong>2wB:&nbsp;</strong>&nbsp;<span>"{singleSelect}"</span>&nbsp;<span>[{multiSelect}]</span></legend>\n\n <label>singleSelect</label>\n <select name="singleSelect">\n <option disabled selected value> -- select -- </option>\n <option value="1">Option 1</option>\n <option value="2">Option 2</option>\n <option value="3">Option 3</option>\n <option value="4">Option 4</option>\n </select><br />\n\n <label>multiSelect</label>\n <select name="multiSelect" multiple="multiple" type="select-multiple">\n <option value="1">Option 1</option>\n <option value="2" selected=true>Option 2</option>\n <option value="3">Option 3</option>\n <option value="4" selected=true>Option 4</option>\n </select>\n\n </fieldset>\n\n <fieldset>\n <legend>Radiobuttons&nbsp;<strong>2wB:&nbsp;</strong><span>"{myRadio}"</span></legend>\n\n <label>radio1</label><input type="radio" name="myRadio" value="radio1"><br />\n <label>radio2</label><input type="radio" name="myRadio" value="radio2"><br />\n <label>radio3</label><input type="radio" name="myRadio" value="radio3"><br />\n\n </fieldset>\n\n <fieldset>\n <legend>Checkboxes</legend>\n <label>cb1</label><input type="checkbox" name="cb1"><br />\n <label>cb2</label><input type="checkbox" name="cb2"><br />\n <label>cb3</label><input type="checkbox" name="cb3"><br />\n <label>cb4</label><input type="checkbox" name="cb4"><br />\n <label>cb5</label><input type="checkbox" name="cb5"><br />\n\n </fieldset>\n\n <fieldset>\n <legend>TextArea</legend>\n <label>textArea</label>\n <textarea name="textArea">abc</textarea>\n </fieldset>\n\n </form>\n '}get css(){return'\nlegend {\n display: inherit;\n margin: inherit;\n vertical-align: inherit;\n font-weight: bold;\n}\n\nlabel {\n display: inline-block;\n margin-top: 1em;\n margin-left: 5px;\n vertical-align: top;\n min-width:10em;\n text-align: right;\n}\n\nform input,\nform select,\nform textarea {\n display: inline-block;\n margin-left: 5px;\n margin-top: 1.1em;\n}\n\ninput[type="radio"],\ninput[type="checkbox"] {\n margin-top: 1.3em;\n}\n\nbody > form {\n display: block;\n}\n\nform fieldset {\n border: 1px solid #aaaaaa;\n border-radius: 3px;\n margin: 0.5em 0;\n}\n '}});
@@ -1,6 +0,0 @@
1
- 'use strict';
2
- export * from './helpers.js';
3
- export * from './MC.js';
4
- export * from './elements.js';
5
- export * from './observables.js';
6
- export * from './formValues.js';
@@ -1,11 +0,0 @@
1
- import { LooseObject } from './helpers.js';
2
- type Callback = (value: any) => void;
3
- export type Observable = {
4
- subscribe: (cb: Callback) => void;
5
- notify: () => void;
6
- bind: (target: HTMLElement | DocumentFragment | ShadowRoot) => void;
7
- callbacks: Array<Function>;
8
- };
9
- export declare const observe: (target: LooseObject, propertyName?: string) => LooseObject;
10
- export {};
11
- //# sourceMappingURL=observables.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"observables.d.ts","sourceRoot":"","sources":["../src/observables.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAA0D,MAAM,cAAc,CAAC;AAcnG,KAAK,QAAQ,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;AA0MrC,MAAM,MAAM,UAAU,GAAG;IACxB,SAAS,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,IAAI,CAAC;IAClC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,GAAG,gBAAgB,GAAG,UAAU,KAAK,IAAI,CAAC;IACpE,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;CAC3B,CAAC;AAwJF,eAAO,MAAM,OAAO,WAAY,WAAW,iBAAiB,MAAM,KAAG,WA2EpE,CAAC"}
@@ -1,228 +0,0 @@
1
- import { flattenObject, debounce, deepEqual, copyGettersSetters } from './helpers.js';
2
- import { DC } from './MC.js';
3
- import { trigger } from './events.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 attrPlaceholders = new Set(target.getAttribute(attr)?.match(regEx));
13
- attrPlaceholders.forEach((attrPlaceholder) => placeholders.add(attrPlaceholder.replace(/[\{\}]/g, '')));
14
- });
15
- [...target.childNodes]
16
- .filter((childNode) => {
17
- return (childNode.nodeType === 3 && (childNode.nodeValue || '').match(regEx));
18
- })
19
- .forEach((childNode) => {
20
- let textContent = childNode?.nodeValue || '';
21
- let contentPlaceholders = textContent.match(regEx);
22
- contentPlaceholders.forEach((textNodePlaceholder) => placeholders.add(textNodePlaceholder.replace(/[\{\}]/g, '')));
23
- });
24
- return [...placeholders];
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.subscribe(((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.subscribe(((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
- subscribe(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?.subscribe) {
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?.subscribe === 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
- };