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.
- package/examples/example-taskmanager.html +2 -1
- package/package.json +1 -1
- package/resonant.js +117 -60
- package/resonant.min.js +1 -1
|
@@ -49,7 +49,8 @@
|
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
function remove(task) {
|
|
52
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (
|
|
55
|
-
|
|
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.
|
|
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
|
-
|
|
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,
|
|
160
|
+
_triggerCallbacks(variableName, callbackData) {
|
|
86
161
|
if (this.callbacks[variableName]) {
|
|
87
|
-
this.callbacks[variableName].forEach(callback =>
|
|
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
|
|
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)}}
|