resonantjs 1.0.2 → 1.0.4

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.
@@ -4,6 +4,11 @@
4
4
  <title>Resonant.js Task Manager Demo</title>
5
5
  <script src="../resonant.js"></script>
6
6
  </head>
7
+ <style>
8
+ .done {
9
+ text-decoration: line-through;
10
+ }
11
+ </style>
7
12
  <body>
8
13
  <h1>Resonant.js Task Manager Demo</h1>
9
14
 
@@ -18,10 +23,10 @@
18
23
  <div>
19
24
  <h2>Task List</h2>
20
25
  <ul res="tasks">
21
- <li>
26
+ <li res-style="tasks.done ? 'done' : ''">
22
27
  <input type="checkbox" res-prop="done" />
23
28
  <span res-prop="name"></span>
24
- <button res-onclick-remove="name">Remove</button>
29
+ <button res-onclick="remove">Remove</button>
25
30
  </li>
26
31
  </ul>
27
32
  </div>
@@ -43,6 +48,13 @@
43
48
  console.log(`Action taken: ${action} for ${task.name}`);
44
49
  });
45
50
 
51
+ function remove(task) {
52
+ const index = tasks.indexOf(task);
53
+ tasks.splice(index, 1);
54
+
55
+ //You could use as well, still trying to figure out if I want to leave this or not
56
+ }
57
+
46
58
  // Add a function to add a new task
47
59
  function addTask() {
48
60
  const newTask = { name: taskName, done: false };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "resonantjs",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
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
@@ -69,6 +69,7 @@ class Resonant {
69
69
  }
70
70
 
71
71
  _queueUpdate(variableName, action, item, property, oldValue) {
72
+
72
73
  if (!this.pendingUpdates.has(variableName)) {
73
74
  this.pendingUpdates.add(variableName);
74
75
  setTimeout(() => {
@@ -76,6 +77,7 @@ class Resonant {
76
77
  this._triggerCallbacks(variableName, action, item, property, oldValue);
77
78
  this.updateElement(variableName);
78
79
  this.updateConditionalsFor(variableName);
80
+ this.updateStylesFor(variableName);
79
81
  }, 0);
80
82
  }
81
83
  }
@@ -93,6 +95,7 @@ class Resonant {
93
95
  this._assignValueToData(variableName, newValue);
94
96
  this.updateElement(variableName);
95
97
  this.updateConditionalsFor(variableName);
98
+ this.updateStylesFor(variableName);
96
99
  if (!Array.isArray(newValue) && typeof newValue !== 'object') {
97
100
  this._queueUpdate(variableName, 'modified', this.data[variableName]);
98
101
  }
@@ -104,12 +107,15 @@ class Resonant {
104
107
  const elements = document.querySelectorAll(`[res="${variableName}"]`);
105
108
  const value = this.data[variableName];
106
109
 
110
+
111
+
107
112
  elements.forEach(element => {
108
113
  if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
114
+ element.value = value;
109
115
  if (!element.hasAttribute('data-resonant-bound')) {
110
- element.value = value;
111
116
  element.oninput = () => {
112
117
  this.data[variableName] = element.value;
118
+ this._queueUpdate(variableName, 'modified', this.data[variableName]);
113
119
  };
114
120
  element.setAttribute('data-resonant-bound', 'true');
115
121
  }
@@ -138,6 +144,16 @@ class Resonant {
138
144
  subEl.innerHTML = value[key];
139
145
  }
140
146
  subEl.setAttribute('data-resonant-bound', 'true');
147
+ } else {
148
+ if (subEl.tagName === 'INPUT' || subEl.tagName === 'TEXTAREA') {
149
+ if (subEl.type === 'checkbox') {
150
+ subEl.checked = value[key];
151
+ } else {
152
+ subEl.value = value[key];
153
+ }
154
+ } else {
155
+ subEl.innerHTML = value[key];
156
+ }
141
157
  }
142
158
  }
143
159
  });
@@ -147,6 +163,7 @@ class Resonant {
147
163
  });
148
164
 
149
165
  this.updateConditionalsFor(variableName);
166
+ this.updateStylesFor(variableName);
150
167
  }
151
168
 
152
169
  updateConditionalsFor(variableName) {
@@ -165,6 +182,49 @@ class Resonant {
165
182
  });
166
183
  }
167
184
 
185
+ updateStylesFor(variableName) {
186
+ const styleElements = document.querySelectorAll(`[res-style*="${variableName}"]`);
187
+
188
+ styleElements.forEach(styleElement => {
189
+ let styleCondition = styleElement.getAttribute('res-style');
190
+ try {
191
+ let parent = styleElement;
192
+ let index = null;
193
+ while (parent && !index) {
194
+ index = parent.getAttribute('res-index');
195
+ parent = parent.parentElement;
196
+ }
197
+
198
+ if (index !== null) {
199
+ const item = this.data[variableName][index];
200
+ styleCondition = styleCondition.replace(new RegExp(`\\b${variableName}\\b`, 'g'), 'item');
201
+ const styleClass = new Function('item', `return ${styleCondition}`)(item);
202
+
203
+ if (styleClass) {
204
+ styleElement.classList.add(styleClass);
205
+ } else {
206
+ var elementHasStyle = styleElement.classList.contains(styleClass);
207
+ if (elementHasStyle) {
208
+ styleElement.classList.remove(styleClass);
209
+ }
210
+ }
211
+ } else {
212
+ const styleClass = eval(styleCondition);
213
+ if (styleClass) {
214
+ styleElement.classList.add(styleClass);
215
+ } else {
216
+ var elementHasStyle = styleElement.classList.contains(styleClass);
217
+ if (elementHasStyle) {
218
+ styleElement.classList.remove(styleClass);
219
+ }
220
+ }
221
+ }
222
+ } catch (e) {
223
+ console.error(`Error evaluating style for ${variableName}: ${styleCondition}`, e);
224
+ }
225
+ });
226
+ }
227
+
168
228
  _renderArray(variableName, el) {
169
229
  let template = el.cloneNode(true);
170
230
  el.innerHTML = '';
@@ -200,18 +260,26 @@ class Resonant {
200
260
  subEl.innerHTML = instance[key];
201
261
  }
202
262
  subEl.setAttribute('data-resonant-bound', 'true');
263
+ } else {
264
+ if (subEl.tagName === 'INPUT' || subEl.tagName === 'TEXTAREA') {
265
+ if (subEl.type === 'checkbox') {
266
+ subEl.checked = instance[key];
267
+ } else {
268
+ subEl.value = instance[key];
269
+ }
270
+ } else {
271
+ subEl.innerHTML = instance[key];
272
+ }
203
273
  }
204
274
  }
205
275
  }
206
276
 
207
- // Handle res-onclick
208
277
  const onclickElements = clonedEl.querySelectorAll('[res-onclick], [res-onclick-remove]');
209
278
  onclickElements.forEach(onclickEl => {
210
279
  const functionName = onclickEl.getAttribute('res-onclick');
211
280
  const removeKey = onclickEl.getAttribute('res-onclick-remove');
212
281
 
213
282
  if (functionName) {
214
- // Remove any existing event listeners to prevent duplicates
215
283
  onclickEl.onclick = null;
216
284
 
217
285
  onclickEl.onclick = () => {
@@ -0,0 +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)}}