resonantjs 1.0.0 → 1.0.2

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/Demo.gif ADDED
Binary file
package/README.md CHANGED
@@ -7,98 +7,62 @@ Resonant.js is an open-source lightweight JavaScript framework that enables reac
7
7
  - **Reactive Data Binding**: Automatically synchronize your data with the UI.
8
8
  - **Dynamic List Rendering**: Easily render lists that react to data changes.
9
9
  - **Bidirectional Input Binding**: Bind HTML input fields directly to your data model.
10
+ - **Efficient Conditional Updates**: Only evaluate conditional expressions tied to specific variable changes.
10
11
  - **Lightweight and Easy to Integrate**: Minimal setup required to get started.
11
12
  - **Compatible with Modern Browsers**: Works seamlessly across all modern web browsers.
13
+ ## Installation
14
+ ## NPM
15
+ To install via NPM, use the following command:
16
+
17
+ ```bash
18
+ npm i resonantjs
19
+ ```
20
+
21
+ ## CDN
22
+ To use via CDN, include the following URLs in your HTML file:
23
+
24
+ ```html
25
+ <script src="https://unpkg.com/resonantjs@latest/resonant.js"></script>
26
+ ```
27
+
28
+ ## Demo
29
+ ![](https://github.com/amurgola/ResonantJs/blob/main/Demo.gif)
12
30
 
13
31
  ## Usage
14
32
  Include resonant.js in your HTML file, and use the following example to understand how to integrate it into your web application.
15
33
 
16
- ```javascript
34
+ ```html
17
35
  <!DOCTYPE html>
18
36
  <html lang="en">
19
37
  <head>
20
- <title>Resonant.js Quick Demo</title>
21
- <script src="./resonant.js"></script>
38
+ <title>Resonant.js Basic Example</title>
39
+ <script src="https://unpkg.com/resonantjs@latest/resonant.js"></script>
22
40
  </head>
23
41
  <body>
24
- <h1>Resonant.js Quick Demo</h1>
25
-
26
- <!-- Display and update a single item -->
27
- <div>
28
- <h2>Counter</h2>
29
- <p>Current count: <span res="counter"></span></p>
30
- <button onclick="incrementCounter()">Increment Counter</button>
31
- </div>
32
-
33
- <!-- Demonstrate object property binding -->
34
- <div>
35
- <h2>Person Information</h2>
36
- <div res="person">
37
- <span res-prop="firstname"></span>
38
- <span res-prop="lastname"></span>
39
- <br/><br/>
40
- First Name: <input type="text" res-prop="firstname">
41
- Last Name: <input type="text" res-prop="lastname">
42
- </div>
43
- </div>
44
-
45
- <!-- Demonstrate dynamic list rendering -->
46
- <div>
47
- <h2>Team Members</h2>
48
- <ul res="team">
49
- <li>
50
- <span res-prop="name"></span> - <span res-prop="role"></span>
51
- </li>
52
- </ul>
53
- <button onclick="addTeamMember()">Add Team Member</button>
54
- </div>
55
-
56
- <script>
57
- const resonantJs = new Resonant();
58
-
59
- // Initialize a counter
60
- resonantJs.add("counter", 0);
61
-
62
- // Initialize a single person object
63
- resonantJs.add("person", {
64
- firstname: "Andy",
65
- lastname: "Murgola"
66
- });
67
-
68
- // Example of a callback
69
- resonantJs.addCallback("person", exampleCallbackOutput);
70
-
71
- // Initialize a list of people with dynamic properties
72
- resonantJs.add("team", [
73
- { name: "Alice", role: "Developer" },
74
- { name: "Bob", role: "Designer" }
75
- ]);
76
-
77
- function incrementCounter() {
78
- counter++;
79
- }
80
-
81
- function addTeamMember() {
82
- const newMember = { name: "Charlie", role: "Product Manager" };
83
- team.push(newMember);
84
- }
85
-
86
- function exampleCallbackOutput(result) {
87
- console.log(result.firstname + " " + result.lastname);
88
- }
89
-
90
- </script>
42
+ <h1>Resonant.js Basic Example</h1>
43
+ <div>
44
+ <h2>Counter</h2>
45
+ <p>Current count: <span res="counter"></span></p>
46
+ <button onclick="counter++">Increment Counter</button>
47
+ </div>
48
+
49
+ <script>
50
+ const resonantJs = new Resonant();
51
+ resonantJs.add("counter", 0);
52
+ </script>
91
53
  </body>
92
54
  </html>
93
-
94
55
  ```
56
+
95
57
  ## Features Overview
96
58
 
97
59
  ### Core Concepts
98
60
  - **`res` and `res-prop` Attributes**: Bind HTML elements to your data model seamlessly.
99
61
  - `res` is used to identify an overarching data model.
100
62
  - `res-prop` links individual properties within that model to corresponding UI elements.
101
-
63
+ - **`res-conditional` Attribute**: Conditionally display elements based on the data model's properties.
64
+ - **`res-onclick` Attribute**: Triggers a function when an element is clicked, allowing for custom event handling.
65
+ - **`res-onclick-remove` Attribute**: Removes an item from an array when the associated element is clicked.
102
66
  - **Automatic UI Updates**: Changes to your JavaScript objects instantly reflect in the associated UI components, reducing manual DOM manipulation.
103
67
 
104
68
  ### Advanced Features
@@ -106,10 +70,80 @@ Include resonant.js in your HTML file, and use the following example to understa
106
70
  - **Event Callbacks**: Register custom functions to execute whenever your data model changes.
107
71
  - **Bidirectional Input Binding**: Bind form input fields directly to your data, making two-way synchronization simple.
108
72
 
109
- ### Example Applications
110
- - **Single-Page Applications**: Build dynamic and responsive single-page applications quickly.
111
- - **Admin Dashboards**: Create data-rich dashboards that reflect real-time changes in your database.
112
- - **Form-Based Applications**: Automate forms, surveys, and user profiles for seamless data entry.
73
+ ### New Features in Version 1.0.2
74
+
75
+ #### Pending Updates Mechanism
76
+ - Introduced to prevent redundant updates and ensure callbacks are only triggered once per update cycle, improving performance and user experience.
77
+
78
+ #### Callback Parameter Enhancement
79
+ - Callbacks now receive detailed parameters including the specific action taken (`added`, `modified`, `removed`), the item affected, and the previous value. This provides better context for handling updates.
80
+
81
+ #### Batched Updates for Object Properties
82
+ - Improved handling of object property updates to ensure changes are batched together, preventing multiple redundant callback triggers.
83
+
84
+ #### Refined Data Binding
85
+ - Enhanced data binding between model and view to ensure consistent synchronization without unnecessary updates.
86
+
87
+ ### Task Manager Example
88
+
89
+ Here is an example of a task manager application using Resonant.js:
90
+
91
+ ```html
92
+ <!DOCTYPE html>
93
+ <html lang="en">
94
+ <head>
95
+ <title>Resonant.js Task Manager Demo</title>
96
+ <script src="https://cdn.jsdelivr.net/npm/resonantjs@latest/dist/resonant.min.js"></script>
97
+ </head>
98
+ <body>
99
+ <h1>Resonant.js Task Manager Demo</h1>
100
+
101
+ <!-- Task Input -->
102
+ <div>
103
+ <h2>Add New Task</h2>
104
+ <input type="text" placeholder="Task Name" res="taskName" />
105
+ <button onclick="addTask()">Add Task</button>
106
+ </div>
107
+
108
+ <!-- Task List -->
109
+ <div>
110
+ <h2>Task List</h2>
111
+ <ul res="tasks">
112
+ <li>
113
+ <input type="checkbox" res-prop="done" />
114
+ <span res-prop="name"></span>
115
+ <button res-onclick-remove="name">Remove</button>
116
+ </li>
117
+ </ul>
118
+ </div>
119
+
120
+ <script>
121
+ const resonantJs = new Resonant();
122
+
123
+ // Initialize variables using a configuration object
124
+ resonantJs.addAll({
125
+ tasks: [
126
+ { name: "Task 1", done: false },
127
+ { name: "Task 2", done: true }
128
+ ],
129
+ taskName: ""
130
+ });
131
+
132
+ // Add a callback to log actions taken on tasks
133
+ resonantJs.addCallback("tasks", (tasks, task, action) => {
134
+ console.log(`Action taken: ${action} for ${task.name}`);
135
+ });
136
+
137
+ // Add a function to add a new task
138
+ function addTask() {
139
+ const newTask = { name: taskName, done: false };
140
+ tasks.push(newTask);
141
+ taskName = '';
142
+ }
143
+ </script>
144
+ </body>
145
+ </html>
146
+ ```
113
147
 
114
148
  ## Future Enhancements
115
149
 
@@ -0,0 +1,88 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>Resonant.js Practical Demo</title>
5
+ <script src="../resonant.js"></script>
6
+ </head>
7
+ <body>
8
+ <h1>Resonant.js Practical Demo</h1>
9
+
10
+ <!-- Display and update a single item -->
11
+ <div>
12
+ <h2>Counter</h2>
13
+ <p>
14
+ Current count: <span res="counter"></span>
15
+ </p>
16
+ <div res-conditional="counter < 10">
17
+ Only shows when counter is less than 10
18
+ </div>
19
+ <div res-conditional="counter >= 10">
20
+ Only shows when counter is greater than or equal to 10
21
+ </div>
22
+ <button onclick="counter++">Increment Counter</button>
23
+ </div>
24
+
25
+ <!-- Demonstrate object property binding -->
26
+ <div>
27
+ <h2>User Information</h2>
28
+ <div res="user">
29
+ <span res-prop="firstname"></span>
30
+ <span res-prop="lastname"></span>
31
+ <br/>
32
+ <div res-conditional="user.firstname == 'John' && user.lastname == 'Doe'">
33
+ Only shows when firstname is John and lastname is Doe
34
+ </div>
35
+ <br/>
36
+ First Name: <input type="text" res-prop="firstname" />
37
+ Last Name: <input type="text" res-prop="lastname" />
38
+ </div>
39
+ </div>
40
+
41
+ <!-- Demonstrate dynamic list rendering -->
42
+ <div>
43
+ <h2>Project Members</h2>
44
+ <ul res="projectTeam">
45
+ <li>
46
+ <span res-prop="name"></span> - <span res-prop="role"></span>
47
+ </li>
48
+ </ul>
49
+ <button onclick="addProjectMember()">Add Project Member</button>
50
+ </div>
51
+
52
+ <script>
53
+ const resonantJs = new Resonant();
54
+
55
+ // Initialize a single object with add method
56
+ resonantJs.add("counter", 0);
57
+
58
+ // Initialize variables using a configuration object
59
+ resonantJs.addAll({
60
+ user: {
61
+ firstname: "John",
62
+ lastname: "Doe",
63
+ email: ""
64
+ },
65
+ projectTeam: [
66
+ { name: "Alice", role: "Developer" },
67
+ { name: "Bob", role: "Designer" }
68
+ ]
69
+ });
70
+
71
+ // Chain together callbacks
72
+ resonantJs.addCallback("user", (user) => {
73
+ console.log(`User updated: ${user.firstname} ${user.lastname}`);
74
+ // You can nest updates within callbacks
75
+ counter++;
76
+ });
77
+
78
+ resonantJs.addCallback("counter", (count) => {
79
+ console.log(`Counter updated: ${count}`);
80
+ });
81
+
82
+ function addProjectMember() {
83
+ const newMember = { name: "Charlie", role: "Product Manager" };
84
+ projectTeam.push(newMember);
85
+ }
86
+ </script>
87
+ </body>
88
+ </html>
@@ -0,0 +1,54 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>Resonant.js Task Manager Demo</title>
5
+ <script src="../resonant.js"></script>
6
+ </head>
7
+ <body>
8
+ <h1>Resonant.js Task Manager Demo</h1>
9
+
10
+ <!-- Task Input -->
11
+ <div>
12
+ <h2>Add New Task</h2>
13
+ <input type="text" placeholder="Task Name" res="taskName" />
14
+ <button onclick="addTask()">Add Task</button>
15
+ </div>
16
+
17
+ <!-- Task List -->
18
+ <div>
19
+ <h2>Task List</h2>
20
+ <ul res="tasks">
21
+ <li>
22
+ <input type="checkbox" res-prop="done" />
23
+ <span res-prop="name"></span>
24
+ <button res-onclick-remove="name">Remove</button>
25
+ </li>
26
+ </ul>
27
+ </div>
28
+
29
+ <script>
30
+ const resonantJs = new Resonant();
31
+
32
+ // Initialize variables using a configuration object
33
+ resonantJs.addAll({
34
+ tasks: [
35
+ { name: "Task 1", done: false },
36
+ { name: "Task 2", done: true }
37
+ ],
38
+ taskName: ""
39
+ });
40
+
41
+ // Add a callback to log actions taken on tasks
42
+ resonantJs.addCallback("tasks", (tasks, task, action) => {
43
+ console.log(`Action taken: ${action} for ${task.name}`);
44
+ });
45
+
46
+ // Add a function to add a new task
47
+ function addTask() {
48
+ const newTask = { name: taskName, done: false };
49
+ tasks.push(newTask);
50
+ taskName = '';
51
+ }
52
+ </script>
53
+ </body>
54
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "resonantjs",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
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
@@ -2,21 +2,21 @@ class Resonant {
2
2
  constructor() {
3
3
  this.data = {};
4
4
  this.callbacks = {};
5
+ this.pendingUpdates = new Set();
5
6
  }
6
7
 
7
- add(variableName, ...values) {
8
- let value;
9
- if (values.length > 1) {
10
- value = values;
11
- } else {
12
- value = values[0];
13
- }
14
-
8
+ add(variableName, value) {
15
9
  this._assignValueToData(variableName, value);
16
10
  this._defineProperty(variableName);
17
11
  this.updateElement(variableName);
18
12
  }
19
13
 
14
+ addAll(config) {
15
+ Object.entries(config).forEach(([variableName, value]) => {
16
+ this.add(variableName, value);
17
+ });
18
+ }
19
+
20
20
  _assignValueToData(variableName, value) {
21
21
  if (Array.isArray(value)) {
22
22
  this.data[variableName] = this._createArray(variableName, value);
@@ -27,39 +27,75 @@ class Resonant {
27
27
  }
28
28
  }
29
29
 
30
- _createObject(parentName, obj) {
30
+ _createObject(variableName, obj) {
31
31
  obj[Symbol('isProxy')] = true;
32
32
  return new Proxy(obj, {
33
33
  set: (target, property, value) => {
34
- target[property] = value;
35
- this.updateElement(parentName);
34
+ if (target[property] !== value) {
35
+ const oldValue = target[property];
36
+ target[property] = value;
37
+ this._queueUpdate(variableName, 'modified', target, property, oldValue);
38
+ }
36
39
  return true;
37
40
  }
38
41
  });
39
42
  }
40
43
 
41
44
  _createArray(variableName, arr) {
45
+ const self = this;
42
46
  return new Proxy(arr, {
43
- get: (target, index) => {
44
- if (typeof target[index] === 'object') {
45
- target[index] = this._createObject(`${variableName}[${index}]`, target[index]);
47
+ get(target, index) {
48
+ if (typeof target[index] === 'object' && !target[index][Symbol('isProxy')]) {
49
+ target[index] = self._createObject(`${variableName}[${index}]`, target[index]);
46
50
  }
47
51
  return target[index];
48
52
  },
49
- set: (target, index, value) => {
50
- target[index] = value;
51
- this.updateElement(variableName);
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);
59
+ }
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);
52
66
  return true;
53
67
  }
54
68
  });
55
69
  }
56
70
 
71
+ _queueUpdate(variableName, action, item, property, oldValue) {
72
+ if (!this.pendingUpdates.has(variableName)) {
73
+ this.pendingUpdates.add(variableName);
74
+ setTimeout(() => {
75
+ this.pendingUpdates.delete(variableName);
76
+ this._triggerCallbacks(variableName, action, item, property, oldValue);
77
+ this.updateElement(variableName);
78
+ this.updateConditionalsFor(variableName);
79
+ }, 0);
80
+ }
81
+ }
82
+
83
+ _triggerCallbacks(variableName, action, item, property, oldValue) {
84
+ if (this.callbacks[variableName]) {
85
+ this.callbacks[variableName].forEach(callback => callback(this.data[variableName], item, action, property, oldValue));
86
+ }
87
+ }
88
+
57
89
  _defineProperty(variableName) {
58
90
  Object.defineProperty(window, variableName, {
59
91
  get: () => this.data[variableName],
60
92
  set: (newValue) => {
61
93
  this._assignValueToData(variableName, newValue);
62
94
  this.updateElement(variableName);
95
+ this.updateConditionalsFor(variableName);
96
+ if (!Array.isArray(newValue) && typeof newValue !== 'object') {
97
+ this._queueUpdate(variableName, 'modified', this.data[variableName]);
98
+ }
63
99
  }
64
100
  });
65
101
  }
@@ -69,20 +105,39 @@ class Resonant {
69
105
  const value = this.data[variableName];
70
106
 
71
107
  elements.forEach(element => {
72
- if (Array.isArray(value)) {
73
- element.querySelectorAll(`[res="${variableName}"]` && "[res-rendered=true]").forEach(el => el.remove());
108
+ if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
109
+ if (!element.hasAttribute('data-resonant-bound')) {
110
+ element.value = value;
111
+ element.oninput = () => {
112
+ this.data[variableName] = element.value;
113
+ };
114
+ element.setAttribute('data-resonant-bound', 'true');
115
+ }
116
+ } else if (Array.isArray(value)) {
117
+ element.querySelectorAll(`[res="${variableName}"][res-rendered=true]`).forEach(el => el.remove());
74
118
  this._renderArray(variableName, element);
75
119
  } else if (typeof value === 'object') {
76
120
  const subElements = element.querySelectorAll(`[res-prop]`);
77
-
78
121
  subElements.forEach(subEl => {
79
122
  const key = subEl.getAttribute('res-prop');
80
123
  if (key && key in value) {
81
- if (subEl.tagName === 'INPUT' || subEl.tagName === 'TEXTAREA') {
82
- subEl.value = value[key];
83
- subEl.oninput = () => this.data[variableName][key] = subEl.value;
84
- } else {
85
- subEl.innerHTML = value[key];
124
+ if (!subEl.hasAttribute('data-resonant-bound')) {
125
+ if (subEl.tagName === 'INPUT' || subEl.tagName === 'TEXTAREA') {
126
+ if (subEl.type === 'checkbox') {
127
+ subEl.checked = value[key];
128
+ subEl.onchange = () => {
129
+ this.data[variableName][key] = subEl.checked;
130
+ };
131
+ } else {
132
+ subEl.value = value[key];
133
+ subEl.oninput = () => {
134
+ this.data[variableName][key] = subEl.value;
135
+ };
136
+ }
137
+ } else {
138
+ subEl.innerHTML = value[key];
139
+ }
140
+ subEl.setAttribute('data-resonant-bound', 'true');
86
141
  }
87
142
  }
88
143
  });
@@ -91,11 +146,24 @@ class Resonant {
91
146
  }
92
147
  });
93
148
 
94
- if (this.callbacks[variableName]) {
95
- this.callbacks[variableName](value);
96
- }
149
+ this.updateConditionalsFor(variableName);
97
150
  }
98
151
 
152
+ updateConditionalsFor(variableName) {
153
+ const conditionalElements = document.querySelectorAll(`[res-conditional*="${variableName}"]`);
154
+ conditionalElements.forEach(conditionalElement => {
155
+ const condition = conditionalElement.getAttribute('res-conditional');
156
+ try {
157
+ if (eval(condition)) {
158
+ conditionalElement.style.display = '';
159
+ } else {
160
+ conditionalElement.style.display = 'none';
161
+ }
162
+ } catch (e) {
163
+ console.error(`Error evaluating condition for ${variableName}: ${condition}`, e);
164
+ }
165
+ });
166
+ }
99
167
 
100
168
  _renderArray(variableName, el) {
101
169
  let template = el.cloneNode(true);
@@ -107,25 +175,72 @@ class Resonant {
107
175
  template = window[variableName + "_template"];
108
176
  }
109
177
 
110
- this.data[variableName].forEach((instance) => {
178
+ this.data[variableName].forEach((instance, index) => {
111
179
  const clonedEl = template.cloneNode(true);
180
+ clonedEl.setAttribute("res-index", index);
112
181
  for (let key in instance) {
113
182
  const subEl = clonedEl.querySelector(`[res-prop="${key}"]`);
114
183
  if (subEl) {
115
- if (subEl.tagName === 'INPUT' || subEl.tagName === 'TEXTAREA') {
116
- subEl.value = instance[key];
117
- subEl.oninput = () => instance[key] = subEl.value;
118
- } else {
119
- subEl.innerHTML = instance[key];
184
+ if (!subEl.hasAttribute('data-resonant-bound')) {
185
+ if (subEl.tagName === 'INPUT' || subEl.tagName === 'TEXTAREA') {
186
+ if (subEl.type === 'checkbox') {
187
+ subEl.checked = instance[key];
188
+ subEl.onchange = () => {
189
+ instance[key] = subEl.checked;
190
+ this._queueUpdate(variableName, 'modified', instance, key, instance[key]);
191
+ };
192
+ } else {
193
+ subEl.value = instance[key];
194
+ subEl.oninput = () => {
195
+ instance[key] = subEl.value;
196
+ this._queueUpdate(variableName, 'modified', instance, key, instance[key]);
197
+ };
198
+ }
199
+ } else {
200
+ subEl.innerHTML = instance[key];
201
+ }
202
+ subEl.setAttribute('data-resonant-bound', 'true');
120
203
  }
121
204
  }
122
205
  }
206
+
207
+ // Handle res-onclick
208
+ const onclickElements = clonedEl.querySelectorAll('[res-onclick], [res-onclick-remove]');
209
+ onclickElements.forEach(onclickEl => {
210
+ const functionName = onclickEl.getAttribute('res-onclick');
211
+ const removeKey = onclickEl.getAttribute('res-onclick-remove');
212
+
213
+ if (functionName) {
214
+ // Remove any existing event listeners to prevent duplicates
215
+ onclickEl.onclick = null;
216
+
217
+ onclickEl.onclick = () => {
218
+ const func = new Function('item', `return ${functionName}(item)`);
219
+ func(instance);
220
+ };
221
+ }
222
+
223
+ if (removeKey) {
224
+ onclickEl.onclick = null;
225
+
226
+ onclickEl.onclick = () => {
227
+ const index = this.data[variableName].findIndex(t => t[removeKey] === instance[removeKey]);
228
+ if (index !== -1) {
229
+ this.data[variableName].splice(index, 1);
230
+ }
231
+ };
232
+ }
233
+ });
234
+
123
235
  clonedEl.setAttribute("res-rendered", true);
124
236
  el.appendChild(clonedEl);
125
237
  });
126
238
  }
127
239
 
128
240
  addCallback(variableName, method) {
129
- this.callbacks[variableName] = method;
241
+ if (!this.callbacks[variableName]) {
242
+ this.callbacks[variableName] = [];
243
+ }
244
+ this.callbacks[variableName].push(method);
130
245
  }
131
246
  }
package/example.html DELETED
@@ -1,83 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <title>Resonant.js Quick Demo</title>
5
- <script src="resonant.js"></script>
6
- </head>
7
- <body>
8
- <h1>Resonant.js Quick Demo</h1>
9
-
10
- <!-- Display and update a single item -->
11
- <div>
12
- <h2>Counter</h2>
13
- <p>
14
- Current count: <span res="counter"></span>
15
- </p>
16
- <button onclick="incrementCounter()">Increment Counter</button>
17
- </div>
18
-
19
- <!-- Demonstrate object property binding -->
20
- <div>
21
- <h2>Person Information</h2>
22
- <div res="person">
23
- <span res-prop="firstname"></span>
24
- <span res-prop="lastname"></span>
25
- <br/>
26
- <br/>
27
-
28
- First Name: <input type="text" res-prop="firstname" />
29
- Last Name: <input type="text" res-prop="lastname" />
30
- </div>
31
- </div>
32
-
33
- <!-- Demonstrate dynamic list rendering -->
34
- <div>
35
- <h2>Team Members</h2>
36
- <ul res="team">
37
- <li>
38
- <span res-prop="name"></span> - <span res-prop="role"></span>
39
- </li>
40
- </ul>
41
- <button onclick="addTeamMember()">Add Team Member</button>
42
- </div>
43
-
44
- <script>
45
- const resonantJs = new Resonant();
46
-
47
- // Initialize a counter
48
- resonantJs.add("counter", 0);
49
-
50
- function exampleCallbackOutput(result) {
51
- console.log(result.firstname + " " + result.lastname);
52
- }
53
-
54
- // Initialize a single person object
55
- resonantJs.add("person", {
56
- firstname: "Andy",
57
- lastname: "Murgola"
58
- });
59
-
60
- // Initialize a list of people with dynamic properties
61
- resonantJs.add("team", [
62
- { name: "Alice", role: "Developer" },
63
- { name: "Bob", role: "Designer" }
64
- ]);
65
-
66
- // Example of a callback
67
- resonantJs.addCallback("person", exampleCallbackOutput);
68
-
69
- function incrementCounter() {
70
- counter++;
71
- }
72
-
73
- function updatePersonInfo(prop, value) {
74
- person[prop] = value;
75
- }
76
-
77
- function addTeamMember() {
78
- const newMember = { name: "Charlie", role: "Product Manager" };
79
- team.push(newMember);
80
- }
81
- </script>
82
- </body>
83
- </html>