resonantjs 1.0.4 → 1.0.5

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.
@@ -49,7 +49,8 @@
49
49
  });
50
50
 
51
51
  function remove(task) {
52
- const index = tasks.indexOf(task);
52
+ //get by name
53
+ const index = tasks.findIndex(t => t.name === task.name);
53
54
  tasks.splice(index, 1);
54
55
 
55
56
  //You could use as well, still trying to figure out if I want to leave this or not
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "resonantjs",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "A lightweight JavaScript framework that enables reactive data-binding for building dynamic and responsive web applications. It simplifies creating interactive UIs by automatically updating the DOM when your data changes.",
5
5
  "main": "resonant.js",
6
6
  "repository": {
package/resonant.js CHANGED
@@ -1,12 +1,94 @@
1
+ class ObservableArray extends Array {
2
+ constructor(variableName, resonantInstance, ...args) {
3
+ super(...args);
4
+ this.variableName = variableName;
5
+ this.resonantInstance = resonantInstance;
6
+ this.isDeleting = false;
7
+ }
8
+
9
+ push(...args) {
10
+ const result = super.push(...args);
11
+ this.resonantInstance.arrayDataChangeDetection[this.variableName] = this.slice();
12
+ args.forEach((item, index) => {
13
+ this.resonantInstance._queueUpdate(this.variableName, 'added', item, this.length - 1 - index);
14
+ });
15
+ return result;
16
+ }
17
+
18
+ splice(start, deleteCount, ...items) {
19
+ const removedItems = super.splice(start, deleteCount, ...items);
20
+ this.resonantInstance.arrayDataChangeDetection[this.variableName] = this.slice();
21
+
22
+ if (deleteCount > 0) {
23
+ removedItems.forEach((item, index) => {
24
+ this.resonantInstance._queueUpdate(this.variableName, 'removed', item, start + index);
25
+ });
26
+ }
27
+
28
+ if (items.length > 0) {
29
+ items.forEach((item, index) => {
30
+ this.resonantInstance._queueUpdate(this.variableName, 'added', item, start + index);
31
+ });
32
+ }
33
+
34
+ return removedItems;
35
+ }
36
+
37
+ set(index, value) {
38
+ if (this[index] !== value) {
39
+ if (this.isDeleting) {
40
+ return true;
41
+ }
42
+
43
+ const originalBeforeChange = this.resonantInstance.arrayDataChangeDetection[this.variableName];
44
+ let action = 'modified';
45
+
46
+ if (index >= originalBeforeChange.length) {
47
+ action = 'added';
48
+ } else if (originalBeforeChange[index] === undefined) {
49
+ action = 'added';
50
+ }
51
+
52
+ this.resonantInstance.arrayDataChangeDetection[this.variableName] = this.slice();
53
+
54
+ const oldValue = this[index];
55
+ this[index] = value;
56
+ this.resonantInstance._queueUpdate(this.variableName, action, this[index], index, oldValue);
57
+ }
58
+ return true;
59
+ }
60
+
61
+ delete(index) {
62
+ const oldValue = this[index];
63
+ this.isDeleting = true;
64
+ this.splice(index, 1);
65
+
66
+ this.resonantInstance.arrayDataChangeDetection[this.variableName] = this.slice();
67
+
68
+ this.resonantInstance._queueUpdate(this.variableName, 'removed', null, index, oldValue);
69
+ this.isDeleting = false;
70
+ return true;
71
+ }
72
+ }
73
+
1
74
  class Resonant {
2
75
  constructor() {
3
76
  this.data = {};
4
77
  this.callbacks = {};
5
- this.pendingUpdates = new Set();
78
+ this.pendingUpdates = new Map();
79
+ this.arrayDataChangeDetection = {}; // Added to keep track of array state
6
80
  }
7
81
 
8
82
  add(variableName, value) {
9
- this._assignValueToData(variableName, value);
83
+ if (Array.isArray(value)) {
84
+ this.data[variableName] = new ObservableArray(variableName, this, ...value);
85
+ this.arrayDataChangeDetection[variableName] = this.data[variableName].slice();
86
+ } else if (typeof value === 'object') {
87
+ this.data[variableName] = this._createObject(variableName, value);
88
+ } else {
89
+ this.data[variableName] = value;
90
+ }
91
+
10
92
  this._defineProperty(variableName);
11
93
  this.updateElement(variableName);
12
94
  }
@@ -17,16 +99,6 @@ class Resonant {
17
99
  });
18
100
  }
19
101
 
20
- _assignValueToData(variableName, value) {
21
- if (Array.isArray(value)) {
22
- this.data[variableName] = this._createArray(variableName, value);
23
- } else if (typeof value === 'object') {
24
- this.data[variableName] = this._createObject(variableName, value);
25
- } else {
26
- this.data[variableName] = value;
27
- }
28
- }
29
-
30
102
  _createObject(variableName, obj) {
31
103
  obj[Symbol('isProxy')] = true;
32
104
  return new Proxy(obj, {
@@ -41,40 +113,43 @@ class Resonant {
41
113
  });
42
114
  }
43
115
 
44
- _createArray(variableName, arr) {
45
- const self = this;
46
- return new Proxy(arr, {
47
- get(target, index) {
48
- if (typeof target[index] === 'object' && !target[index][Symbol('isProxy')]) {
49
- target[index] = self._createObject(`${variableName}[${index}]`, target[index]);
116
+ _defineProperty(variableName) {
117
+ Object.defineProperty(window, variableName, {
118
+ get: () => this.data[variableName],
119
+ set: (newValue) => {
120
+ if (Array.isArray(newValue)) {
121
+ this.data[variableName] = new ObservableArray(variableName, this, ...newValue);
122
+ this.arrayDataChangeDetection[variableName] = this.data[variableName].slice(); // Create a copy for change detection
123
+
124
+ } else if (typeof newValue === 'object') {
125
+ this.data[variableName] = this._createObject(variableName, newValue);
126
+ } else {
127
+ this.data[variableName] = newValue;
50
128
  }
51
- return target[index];
52
- },
53
- set(target, index, value) {
54
- if (target[index] !== value) {
55
- const action = target.hasOwnProperty(index) ? 'modified' : 'added';
56
- const oldValue = target[index];
57
- target[index] = value;
58
- self._queueUpdate(variableName, action, target[index], index, oldValue);
129
+ this.updateElement(variableName);
130
+ this.updateConditionalsFor(variableName);
131
+ this.updateStylesFor(variableName);
132
+ if (!Array.isArray(newValue) && typeof newValue !== 'object') {
133
+ this._queueUpdate(variableName, 'modified', this.data[variableName]);
59
134
  }
60
- return true;
61
- },
62
- deleteProperty(target, index) {
63
- const oldValue = target[index];
64
- target.splice(index, 1);
65
- self._queueUpdate(variableName, 'removed', oldValue, index);
66
- return true;
67
135
  }
68
136
  });
69
137
  }
70
138
 
71
139
  _queueUpdate(variableName, action, item, property, oldValue) {
72
-
73
140
  if (!this.pendingUpdates.has(variableName)) {
74
- this.pendingUpdates.add(variableName);
141
+ this.pendingUpdates.set(variableName, []);
142
+ }
143
+
144
+ this.pendingUpdates.get(variableName).push({ action, item, property, oldValue });
145
+
146
+ if (this.pendingUpdates.get(variableName).length === 1) {
75
147
  setTimeout(() => {
148
+ const updates = this.pendingUpdates.get(variableName);
76
149
  this.pendingUpdates.delete(variableName);
77
- this._triggerCallbacks(variableName, action, item, property, oldValue);
150
+ updates.forEach(update => {
151
+ this._triggerCallbacks(variableName, update);
152
+ });
78
153
  this.updateElement(variableName);
79
154
  this.updateConditionalsFor(variableName);
80
155
  this.updateStylesFor(variableName);
@@ -82,36 +157,22 @@ class Resonant {
82
157
  }
83
158
  }
84
159
 
85
- _triggerCallbacks(variableName, action, item, property, oldValue) {
160
+ _triggerCallbacks(variableName, callbackData) {
86
161
  if (this.callbacks[variableName]) {
87
- this.callbacks[variableName].forEach(callback => callback(this.data[variableName], item, action, property, oldValue));
162
+ this.callbacks[variableName].forEach(callback => {
163
+ const item = callbackData.item || callbackData.oldValue;
164
+ callback(this.data[variableName], item, callbackData.action);
165
+ });
88
166
  }
89
167
  }
90
168
 
91
- _defineProperty(variableName) {
92
- Object.defineProperty(window, variableName, {
93
- get: () => this.data[variableName],
94
- set: (newValue) => {
95
- this._assignValueToData(variableName, newValue);
96
- this.updateElement(variableName);
97
- this.updateConditionalsFor(variableName);
98
- this.updateStylesFor(variableName);
99
- if (!Array.isArray(newValue) && typeof newValue !== 'object') {
100
- this._queueUpdate(variableName, 'modified', this.data[variableName]);
101
- }
102
- }
103
- });
104
- }
105
-
106
169
  updateElement(variableName) {
107
170
  const elements = document.querySelectorAll(`[res="${variableName}"]`);
108
171
  const value = this.data[variableName];
109
172
 
110
-
111
-
112
173
  elements.forEach(element => {
174
+ element.value = value;
113
175
  if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
114
- element.value = value;
115
176
  if (!element.hasAttribute('data-resonant-bound')) {
116
177
  element.oninput = () => {
117
178
  this.data[variableName] = element.value;
@@ -280,8 +341,6 @@ class Resonant {
280
341
  const removeKey = onclickEl.getAttribute('res-onclick-remove');
281
342
 
282
343
  if (functionName) {
283
- onclickEl.onclick = null;
284
-
285
344
  onclickEl.onclick = () => {
286
345
  const func = new Function('item', `return ${functionName}(item)`);
287
346
  func(instance);
@@ -289,8 +348,6 @@ class Resonant {
289
348
  }
290
349
 
291
350
  if (removeKey) {
292
- onclickEl.onclick = null;
293
-
294
351
  onclickEl.onclick = () => {
295
352
  const index = this.data[variableName].findIndex(t => t[removeKey] === instance[removeKey]);
296
353
  if (index !== -1) {
package/resonant.min.js CHANGED
@@ -1 +1 @@
1
- class Resonant{constructor(){this.data={},this.callbacks={},this.pendingUpdates=new Set}add(e,t){this._assignValueToData(e,t),this._defineProperty(e),this.updateElement(e)}addAll(e){Object.entries(e).forEach(([e,t])=>{this.add(e,t)})}_assignValueToData(e,t){Array.isArray(t)?this.data[e]=this._createArray(e,t):this.data[e]="object"==typeof t?this._createObject(e,t):t}_createObject(n,e){return e[Symbol("isProxy")]=!0,new Proxy(e,{set:(e,t,a)=>{var s;return e[t]!==a&&(s=e[t],e[t]=a,this._queueUpdate(n,"modified",e,t,s)),!0}})}_createArray(l,e){const i=this;return new Proxy(e,{get(e,t){return"object"!=typeof e[t]||e[t][Symbol("isProxy")]||(e[t]=i._createObject(l+`[${t}]`,e[t])),e[t]},set(e,t,a){var s,n;return e[t]!==a&&(s=e.hasOwnProperty(t)?"modified":"added",n=e[t],e[t]=a,i._queueUpdate(l,s,e[t],t,n)),!0},deleteProperty(e,t){var a=e[t];return e.splice(t,1),i._queueUpdate(l,"removed",a,t),!0}})}_queueUpdate(e,t,a,s,n){this.pendingUpdates.has(e)||(this.pendingUpdates.add(e),setTimeout(()=>{this.pendingUpdates.delete(e),this._triggerCallbacks(e,t,a,s,n),this.updateElement(e),this.updateConditionalsFor(e),this.updateStylesFor(e)},0))}_triggerCallbacks(t,a,s,n,l){this.callbacks[t]&&this.callbacks[t].forEach(e=>e(this.data[t],s,a,n,l))}_defineProperty(t){Object.defineProperty(window,t,{get:()=>this.data[t],set:e=>{this._assignValueToData(t,e),this.updateElement(t),this.updateConditionalsFor(t),this.updateStylesFor(t),Array.isArray(e)||"object"==typeof e||this._queueUpdate(t,"modified",this.data[t])}})}updateElement(a){var e=document.querySelectorAll(`[res="${a}"]`);const s=this.data[a];e.forEach(e=>{"INPUT"===e.tagName||"TEXTAREA"===e.tagName?(e.value=s,e.hasAttribute("data-resonant-bound")||(e.oninput=()=>{this.data[a]=e.value,this._queueUpdate(a,"modified",this.data[a])},e.setAttribute("data-resonant-bound","true"))):Array.isArray(s)?(e.querySelectorAll(`[res="${a}"][res-rendered=true]`).forEach(e=>e.remove()),this._renderArray(a,e)):"object"==typeof s?e.querySelectorAll("[res-prop]").forEach(e=>{const t=e.getAttribute("res-prop");t&&t in s&&(e.hasAttribute("data-resonant-bound")?"INPUT"===e.tagName||"TEXTAREA"===e.tagName?"checkbox"===e.type?e.checked=s[t]:e.value=s[t]:e.innerHTML=s[t]:("INPUT"===e.tagName||"TEXTAREA"===e.tagName?"checkbox"===e.type?(e.checked=s[t],e.onchange=()=>{this.data[a][t]=e.checked}):(e.value=s[t],e.oninput=()=>{this.data[a][t]=e.value}):e.innerHTML=s[t],e.setAttribute("data-resonant-bound","true")))}):e.innerHTML=s}),this.updateConditionalsFor(a),this.updateStylesFor(a)}updateConditionalsFor(variableName){const conditionalElements=document.querySelectorAll(`[res-conditional*="${variableName}"]`);conditionalElements.forEach(conditionalElement=>{const condition=conditionalElement.getAttribute("res-conditional");try{eval(condition)?conditionalElement.style.display="":conditionalElement.style.display="none"}catch(e){}})}updateStylesFor(variableName){const styleElements=document.querySelectorAll(`[res-style*="${variableName}"]`);styleElements.forEach(styleElement=>{let styleCondition=styleElement.getAttribute("res-style");try{let parent=styleElement,index=null;for(;parent&&!index;)index=parent.getAttribute("res-index"),parent=parent.parentElement;if(null!==index){const item=this.data[variableName][index],styleClass=(styleCondition=styleCondition.replace(new RegExp(`\\b${variableName}\\b`,"g"),"item"),new Function("item","return "+styleCondition)(item));var elementHasStyle;styleClass?styleElement.classList.add(styleClass):(elementHasStyle=styleElement.classList.contains(styleClass),elementHasStyle&&styleElement.classList.remove(styleClass))}else{const styleClass=eval(styleCondition);var elementHasStyle;styleClass?styleElement.classList.add(styleClass):(elementHasStyle=styleElement.classList.contains(styleClass),elementHasStyle&&styleElement.classList.remove(styleClass))}}catch(e){}})}_renderArray(n,l){let i=l.cloneNode(!0);l.innerHTML="",window[n+"_template"]?i=window[n+"_template"]:window[n+"_template"]=i,this.data[n].forEach((s,e)=>{var t=i.cloneNode(!0);t.setAttribute("res-index",e);for(let e in s){const a=t.querySelector(`[res-prop="${e}"]`);a&&(a.hasAttribute("data-resonant-bound")?"INPUT"===a.tagName||"TEXTAREA"===a.tagName?"checkbox"===a.type?a.checked=s[e]:a.value=s[e]:a.innerHTML=s[e]:("INPUT"===a.tagName||"TEXTAREA"===a.tagName?"checkbox"===a.type?(a.checked=s[e],a.onchange=()=>{s[e]=a.checked,this._queueUpdate(n,"modified",s,e,s[e])}):(a.value=s[e],a.oninput=()=>{s[e]=a.value,this._queueUpdate(n,"modified",s,e,s[e])}):a.innerHTML=s[e],a.setAttribute("data-resonant-bound","true")))}t.querySelectorAll("[res-onclick], [res-onclick-remove]").forEach(e=>{const t=e.getAttribute("res-onclick"),a=e.getAttribute("res-onclick-remove");t&&(e.onclick=null,e.onclick=()=>{new Function("item",`return ${t}(item)`)(s)}),a&&(e.onclick=null,e.onclick=()=>{var e=this.data[n].findIndex(e=>e[a]===s[a]);-1!==e&&this.data[n].splice(e,1)})}),t.setAttribute("res-rendered",!0),l.appendChild(t)})}addCallback(e,t){this.callbacks[e]||(this.callbacks[e]=[]),this.callbacks[e].push(t)}}
1
+ class ObservableArray extends Array{constructor(e,t,...a){super(...a),this.variableName=e,this.resonantInstance=t,this.isDeleting=!1}push(...e){var t=super.push(...e);return this.resonantInstance.arrayDataChangeDetection[this.variableName]=this.slice(),e.forEach((e,t)=>{this.resonantInstance._queueUpdate(this.variableName,"added",e,this.length-1-t)}),t}splice(a,e,...t){var s=super.splice(a,e,...t);return this.resonantInstance.arrayDataChangeDetection[this.variableName]=this.slice(),0<e&&s.forEach((e,t)=>{this.resonantInstance._queueUpdate(this.variableName,"removed",e,a+t)}),0<t.length&&t.forEach((e,t)=>{this.resonantInstance._queueUpdate(this.variableName,"added",e,a+t)}),s}set(t,a){if(this[t]!==a){if(this.isDeleting)return!0;var s=this.resonantInstance.arrayDataChangeDetection[this.variableName];let e="modified";(t>=s.length||void 0===s[t])&&(e="added"),this.resonantInstance.arrayDataChangeDetection[this.variableName]=this.slice();s=this[t];this[t]=a,this.resonantInstance._queueUpdate(this.variableName,e,this[t],t,s)}return!0}delete(e){var t=this[e];return this.isDeleting=!0,this.splice(e,1),this.resonantInstance.arrayDataChangeDetection[this.variableName]=this.slice(),this.resonantInstance._queueUpdate(this.variableName,"removed",null,e,t),!(this.isDeleting=!1)}}class Resonant{constructor(){this.data={},this.callbacks={},this.pendingUpdates=new Map,this.arrayDataChangeDetection={}}add(e,t){Array.isArray(t)?(this.data[e]=new ObservableArray(e,this,...t),this.arrayDataChangeDetection[e]=this.data[e].slice()):this.data[e]="object"==typeof t?this._createObject(e,t):t,this._defineProperty(e),this.updateElement(e)}addAll(e){Object.entries(e).forEach(([e,t])=>{this.add(e,t)})}_createObject(i,e){return e[Symbol("isProxy")]=!0,new Proxy(e,{set:(e,t,a)=>{var s;return e[t]!==a&&(s=e[t],e[t]=a,this._queueUpdate(i,"modified",e,t,s)),!0}})}_defineProperty(t){Object.defineProperty(window,t,{get:()=>this.data[t],set:e=>{Array.isArray(e)?(this.data[t]=new ObservableArray(t,this,...e),this.arrayDataChangeDetection[t]=this.data[t].slice()):this.data[t]="object"==typeof e?this._createObject(t,e):e,this.updateElement(t),this.updateConditionalsFor(t),this.updateStylesFor(t),Array.isArray(e)||"object"==typeof e||this._queueUpdate(t,"modified",this.data[t])}})}_queueUpdate(t,e,a,s,i){this.pendingUpdates.has(t)||this.pendingUpdates.set(t,[]),this.pendingUpdates.get(t).push({action:e,item:a,property:s,oldValue:i}),1===this.pendingUpdates.get(t).length&&setTimeout(()=>{var e=this.pendingUpdates.get(t);this.pendingUpdates.delete(t),e.forEach(e=>{this._triggerCallbacks(t,e)}),this.updateElement(t),this.updateConditionalsFor(t),this.updateStylesFor(t)},0)}_triggerCallbacks(a,s){this.callbacks[a]&&this.callbacks[a].forEach(e=>{var t=s.item||s.oldValue;e(this.data[a],t,s.action)})}updateElement(a){var e=document.querySelectorAll(`[res="${a}"]`);const s=this.data[a];e.forEach(e=>{e.value=s,"INPUT"===e.tagName||"TEXTAREA"===e.tagName?e.hasAttribute("data-resonant-bound")||(e.oninput=()=>{this.data[a]=e.value,this._queueUpdate(a,"modified",this.data[a])},e.setAttribute("data-resonant-bound","true")):Array.isArray(s)?(e.querySelectorAll(`[res="${a}"][res-rendered=true]`).forEach(e=>e.remove()),this._renderArray(a,e)):"object"==typeof s?e.querySelectorAll("[res-prop]").forEach(e=>{const t=e.getAttribute("res-prop");t&&t in s&&(e.hasAttribute("data-resonant-bound")?"INPUT"===e.tagName||"TEXTAREA"===e.tagName?"checkbox"===e.type?e.checked=s[t]:e.value=s[t]:e.innerHTML=s[t]:("INPUT"===e.tagName||"TEXTAREA"===e.tagName?"checkbox"===e.type?(e.checked=s[t],e.onchange=()=>{this.data[a][t]=e.checked}):(e.value=s[t],e.oninput=()=>{this.data[a][t]=e.value}):e.innerHTML=s[t],e.setAttribute("data-resonant-bound","true")))}):e.innerHTML=s}),this.updateConditionalsFor(a),this.updateStylesFor(a)}updateConditionalsFor(variableName){const conditionalElements=document.querySelectorAll(`[res-conditional*="${variableName}"]`);conditionalElements.forEach(conditionalElement=>{const condition=conditionalElement.getAttribute("res-conditional");try{eval(condition)?conditionalElement.style.display="":conditionalElement.style.display="none"}catch(e){}})}updateStylesFor(variableName){const styleElements=document.querySelectorAll(`[res-style*="${variableName}"]`);styleElements.forEach(styleElement=>{let styleCondition=styleElement.getAttribute("res-style");try{let parent=styleElement,index=null;for(;parent&&!index;)index=parent.getAttribute("res-index"),parent=parent.parentElement;if(null!==index){const item=this.data[variableName][index],styleClass=(styleCondition=styleCondition.replace(new RegExp(`\\b${variableName}\\b`,"g"),"item"),new Function("item","return "+styleCondition)(item));var elementHasStyle;styleClass?styleElement.classList.add(styleClass):(elementHasStyle=styleElement.classList.contains(styleClass),elementHasStyle&&styleElement.classList.remove(styleClass))}else{const styleClass=eval(styleCondition);var elementHasStyle;styleClass?styleElement.classList.add(styleClass):(elementHasStyle=styleElement.classList.contains(styleClass),elementHasStyle&&styleElement.classList.remove(styleClass))}}catch(e){}})}_renderArray(i,n){let r=n.cloneNode(!0);n.innerHTML="",window[i+"_template"]?r=window[i+"_template"]:window[i+"_template"]=r,this.data[i].forEach((s,e)=>{var t=r.cloneNode(!0);t.setAttribute("res-index",e);for(let e in s){const a=t.querySelector(`[res-prop="${e}"]`);a&&(a.hasAttribute("data-resonant-bound")?"INPUT"===a.tagName||"TEXTAREA"===a.tagName?"checkbox"===a.type?a.checked=s[e]:a.value=s[e]:a.innerHTML=s[e]:("INPUT"===a.tagName||"TEXTAREA"===a.tagName?"checkbox"===a.type?(a.checked=s[e],a.onchange=()=>{s[e]=a.checked,this._queueUpdate(i,"modified",s,e,s[e])}):(a.value=s[e],a.oninput=()=>{s[e]=a.value,this._queueUpdate(i,"modified",s,e,s[e])}):a.innerHTML=s[e],a.setAttribute("data-resonant-bound","true")))}t.querySelectorAll("[res-onclick], [res-onclick-remove]").forEach(e=>{const t=e.getAttribute("res-onclick"),a=e.getAttribute("res-onclick-remove");t&&(e.onclick=()=>{new Function("item",`return ${t}(item)`)(s)}),a&&(e.onclick=()=>{var e=this.data[i].findIndex(e=>e[a]===s[a]);-1!==e&&this.data[i].splice(e,1)})}),t.setAttribute("res-rendered",!0),n.appendChild(t)})}addCallback(e,t){this.callbacks[e]||(this.callbacks[e]=[]),this.callbacks[e].push(t)}}