resonantjs 1.1.5 → 1.1.7

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/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  **ResonantJs** is a lightweight, powerful JavaScript framework that brings **reactive data-binding** to vanilla JavaScript applications. Build dynamic, responsive UIs with minimal code and zero dependencies.
7
7
 
8
- > **Zero dependencies • ~15KB minified • Lightning fast • Easy to learn**
8
+ > **Zero dependencies • ~11.5KB minified • Lightning fast • Easy to learn**
9
9
 
10
10
  ## ✨ Why Choose ResonantJs?
11
11
 
@@ -15,7 +15,7 @@
15
15
  - 💾 **Built-in Persistence**: Automatic localStorage integration for data persistence
16
16
  - 🎨 **Dynamic Styling**: Conditional CSS classes and styles based on your data
17
17
  - ⚡ **Performance**: Efficient updates with minimal overhead
18
- - 📦 **Tiny Footprint**: Under 15KB minified - perfect for any project size
18
+ - 📦 **Tiny Footprint**: Under 11.5KB minified - perfect for any project size
19
19
 
20
20
  ---
21
21
 
@@ -116,7 +116,25 @@ Bind click events with context:
116
116
  <button res-onclick-remove="true">Delete Item</button>
117
117
  ```
118
118
 
119
- ### 7. **Input Binding**
119
+ ### 7. **Computed Properties**
120
+ Create reactive derived values that automatically update:
121
+
122
+ ```javascript
123
+ const resonant = new Resonant();
124
+ resonant.add('firstName', 'John');
125
+ resonant.add('lastName', 'Doe');
126
+
127
+ // Computed property automatically updates when dependencies change
128
+ resonant.computed('fullName', () => {
129
+ return firstName + ' ' + lastName;
130
+ });
131
+ ```
132
+
133
+ ```html
134
+ <span res="fullName"></span> <!-- Automatically shows "John Doe" -->
135
+ ```
136
+
137
+ ### 8. **Input Binding**
120
138
  Two-way data binding for form elements:
121
139
 
122
140
  ```html
@@ -181,6 +199,27 @@ resonant.add('appState', { currentView: 'dashboard' }, true);
181
199
  userPreferences.theme = 'light'; // Saved to localStorage
182
200
  ```
183
201
 
202
+ ### **Computed Properties**
203
+ Reactive derived values that automatically recalculate:
204
+
205
+ ```javascript
206
+ resonant.add('firstName', 'John');
207
+ resonant.add('lastName', 'Doe');
208
+
209
+ // Automatically updates when firstName or lastName changes
210
+ resonant.computed('fullName', () => {
211
+ return firstName + ' ' + lastName;
212
+ });
213
+
214
+ // Cannot be set directly - read-only
215
+ // fullName = 'Something'; // Will log warning and be ignored
216
+
217
+ // Chain computed properties
218
+ resonant.computed('greeting', () => {
219
+ return 'Hello, ' + fullName + '!';
220
+ });
221
+ ```
222
+
184
223
  ### **Advanced Array Operations**
185
224
  Full array reactivity with custom methods:
186
225
 
@@ -324,16 +363,54 @@ company.departments[0].teams[0].members.push(newMember);
324
363
  ```
325
364
 
326
365
  ### **Computed Properties**
327
- Create reactive calculated values:
366
+ Create reactive calculated values that automatically update when dependencies change:
328
367
 
329
368
  ```javascript
330
- resonant.addCallback('tasks', () => {
331
- stats.totalTasks = tasks.length;
332
- stats.completedTasks = tasks.filter(t => t.completed).length;
333
- stats.completionRate = stats.totalTasks > 0
334
- ? Math.round((stats.completedTasks / stats.totalTasks) * 100)
335
- : 0;
369
+ const resonant = new Resonant();
370
+ resonant.add('tasks', [
371
+ { title: 'Task 1', completed: true },
372
+ { title: 'Task 2', completed: false },
373
+ { title: 'Task 3', completed: true }
374
+ ]);
375
+
376
+ // Computed properties automatically recalculate when 'tasks' changes
377
+ resonant.computed('totalTasks', () => {
378
+ return tasks.length;
379
+ });
380
+
381
+ resonant.computed('completedTasks', () => {
382
+ return tasks.filter(t => t.completed).length;
336
383
  });
384
+
385
+ resonant.computed('completionRate', () => {
386
+ return totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0;
387
+ });
388
+
389
+ // Shopping cart example
390
+ resonant.add('items', [
391
+ { name: 'Widget', price: 10, quantity: 2 },
392
+ { name: 'Gadget', price: 15, quantity: 1 }
393
+ ]);
394
+ resonant.add('taxRate', 0.08);
395
+
396
+ resonant.computed('subtotal', () => {
397
+ return items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
398
+ });
399
+
400
+ resonant.computed('tax', () => {
401
+ return subtotal * taxRate;
402
+ });
403
+
404
+ resonant.computed('total', () => {
405
+ return subtotal + tax;
406
+ });
407
+ ```
408
+
409
+ ```html
410
+ <!-- These automatically update when items change -->
411
+ <div>Subtotal: $<span res="subtotal"></span></div>
412
+ <div>Tax: $<span res="tax"></span></div>
413
+ <div>Total: $<span res="total"></span></div>
337
414
  ```
338
415
 
339
416
  ### **Component-Like Patterns**
package/aiagent.md ADDED
@@ -0,0 +1,231 @@
1
+ # Using ResonantJs with AI Agents
2
+
3
+ ResonantJs is a lightweight framework that adds reactive data-binding to vanilla JavaScript applications. The library can be leveraged by AI agents when generating or manipulating front-end code. Below is a high-level overview of its features and how an agent might use them.
4
+
5
+ ## Core Concepts
6
+
7
+ - **Data Binding (`res`)** – Attach a JavaScript variable to an element so that UI updates whenever the value changes.
8
+ - **Object Properties (`res-prop`)** – Bind specific properties of an object to nested elements.
9
+ - **Array Rendering** – Automatically generate a list from an array of data using a template element.
10
+ - **Conditional Display (`res-display`)** – Show or hide elements based on expressions.
11
+ - **Dynamic Styling (`res-style`)** – Conditionally apply CSS classes or inline styles.
12
+ - **Event Handling (`res-onclick`)** – Link element events to functions with access to bound data.
13
+ - **Input Binding** – Two-way binding for input elements such as text fields, checkboxes, or select menus.
14
+
15
+ ## Key Features
16
+
17
+ - **Reactive Data Management** – Initialize variables with `add` or `addAll`. Any update to these values immediately reflects in the DOM.
18
+ - **Event Callbacks** – Register callbacks via `addCallback` to react to data changes and chain side effects.
19
+ - **Persistence** – Optionally persist values to `localStorage` so state survives page reloads.
20
+ - **Advanced Array Operations** – Observable arrays expose methods like `update`, `set`, and `delete` to trigger UI refreshes when items change.
21
+ - **Child Object Support** – Nested objects (e.g. `user.profile.email`) remain reactive through automatic proxy wrapping.
22
+ - **Computed Properties** – Define reactive derived values using `computed()` that automatically update when their dependencies change.
23
+ - **Component Patterns** – Encapsulate related variables and callbacks in functions for reuse across the page.
24
+
25
+ ## Example Workflow for an AI Agent
26
+
27
+ 1. **Initialize** a new `Resonant` instance when generating the page.
28
+ 2. **Add Variables** using `add` or `addAll` for any data that needs to be reactive. Enable persistence if needed.
29
+ 3. **Define Computed Properties** using `computed()` for derived values that should automatically update when dependencies change.
30
+ 4. **Bind Elements** in generated HTML using `res`, `res-prop`, and related attributes so DOM updates automatically.
31
+ 5. **Attach Callbacks** with `addCallback` when additional logic is required after data changes. This can be used to interact with other services.
32
+ 6. **Manipulate Data** directly in generated scripts. Changes propagate to the interface with no manual DOM manipulation.
33
+
34
+ ## When to Use
35
+
36
+ - Building dynamic UI components without importing a heavy framework.
37
+ - Keeping local state in sync with DOM elements in generated pages.
38
+ - Rapidly prototyping interactive features that persist across sessions using localStorage.
39
+
40
+ For more detailed examples, see the `examples` folder and the full README documentation in this repository.
41
+
42
+ ## Examples
43
+
44
+ ### Simple Counter
45
+
46
+ ```html
47
+ <h1>Counter: <span res="counter"></span></h1>
48
+ <button onclick="counter++">Increment</button>
49
+
50
+ <script>
51
+ const r = new Resonant();
52
+ r.add('counter', 0, true); // persisted value
53
+ </script>
54
+ ```
55
+
56
+ ### Todo List
57
+
58
+ ```html
59
+ <input res="newTodo" placeholder="Add task" />
60
+ <button onclick="addTodo()">Add</button>
61
+
62
+ <ul>
63
+ <li res="todos">
64
+ <input type="checkbox" res-prop="done" />
65
+ <span res-prop="title"></span>
66
+ </li>
67
+ </ul>
68
+
69
+ <script>
70
+ const r = new Resonant();
71
+ r.addAll({
72
+ todos: [{ title: 'Try Resonant', done: false }],
73
+ newTodo: ''
74
+ });
75
+
76
+ function addTodo() {
77
+ if (newTodo.trim()) {
78
+ todos.push({ title: newTodo, done: false });
79
+ newTodo = '';
80
+ }
81
+ }
82
+ </script>
83
+ ```
84
+
85
+ ### Callbacks
86
+
87
+ ```html
88
+ <ul>
89
+ <li res="tasks">
90
+ <span res-prop="name"></span>
91
+ <button onclick="remove(item)">Remove</button>
92
+ </li>
93
+ </ul>
94
+
95
+ <script>
96
+ const r = new Resonant();
97
+ r.addAll({ tasks: [] });
98
+ r.addCallback('tasks', (tasks, task, action) => {
99
+ console.log(`Action: ${action}`, task);
100
+ });
101
+
102
+ function remove(task) {
103
+ const idx = tasks.indexOf(task);
104
+ tasks.delete(idx);
105
+ }
106
+ </script>
107
+ ```
108
+
109
+ ### Nested Child Objects
110
+
111
+ ```html
112
+ <div res="company.departments">
113
+ <h3 res-prop="name"></h3>
114
+ <div res-prop="teams">
115
+ <span res-prop="name"></span>
116
+ <ul res-prop="members">
117
+ <li res-prop="name"></li>
118
+ </ul>
119
+ </div>
120
+ </div>
121
+
122
+ <script>
123
+ const r = new Resonant();
124
+ r.add('company', {
125
+ departments: [
126
+ {
127
+ name: 'Engineering',
128
+ teams: [
129
+ { name: 'Frontend', members: [{ name: 'Alice' }] }
130
+ ]
131
+ }
132
+ ]
133
+ });
134
+ </script>
135
+ ```
136
+
137
+ ### Computed Properties
138
+
139
+ ```html
140
+ <div>
141
+ <input res="firstName" placeholder="First name" />
142
+ <input res="lastName" placeholder="Last name" />
143
+ <h2>Welcome, <span res="fullName"></span>!</h2>
144
+ </div>
145
+
146
+ <script>
147
+ const r = new Resonant();
148
+ r.addAll({
149
+ firstName: 'John',
150
+ lastName: 'Doe'
151
+ });
152
+
153
+ // Computed property automatically updates when firstName or lastName changes
154
+ r.computed('fullName', () => {
155
+ return firstName + ' ' + lastName;
156
+ });
157
+ </script>
158
+ ```
159
+
160
+ ### Shopping Cart with Computed Totals
161
+
162
+ ```html
163
+ <div res="items">
164
+ <div>
165
+ <span res-prop="name"></span> -
166
+ $<span res-prop="price"></span> x
167
+ <span res-prop="quantity"></span>
168
+ </div>
169
+ </div>
170
+ <div>
171
+ <strong>Subtotal: $<span res="subtotal"></span></strong><br>
172
+ <strong>Tax: $<span res="tax"></span></strong><br>
173
+ <strong>Total: $<span res="total"></span></strong>
174
+ </div>
175
+
176
+ <script>
177
+ const r = new Resonant();
178
+ r.addAll({
179
+ items: [
180
+ { name: 'Widget', price: 10, quantity: 2 },
181
+ { name: 'Gadget', price: 15, quantity: 1 }
182
+ ],
183
+ taxRate: 0.08
184
+ });
185
+
186
+ // Computed properties automatically recalculate when items change
187
+ r.computed('subtotal', () => {
188
+ return items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
189
+ });
190
+
191
+ r.computed('tax', () => {
192
+ return subtotal * taxRate;
193
+ });
194
+
195
+ r.computed('total', () => {
196
+ return subtotal + tax;
197
+ });
198
+ </script>
199
+ ```
200
+
201
+ ### User Profile with Computed Display
202
+
203
+ ```html
204
+ <div>
205
+ <input res="user.age" type="number" placeholder="Age" />
206
+ <input res="user.memberSince" type="number" placeholder="Member since year" />
207
+ <div>Status: <span res="membershipStatus"></span></div>
208
+ <div>Category: <span res="ageCategory"></span></div>
209
+ </div>
210
+
211
+ <script>
212
+ const r = new Resonant();
213
+ r.add('user', {
214
+ age: 25,
215
+ memberSince: 2020
216
+ });
217
+
218
+ r.computed('membershipStatus', () => {
219
+ const yearsAsMember = new Date().getFullYear() - user.memberSince;
220
+ if (yearsAsMember >= 5) return 'Gold Member';
221
+ if (yearsAsMember >= 2) return 'Silver Member';
222
+ return 'Bronze Member';
223
+ });
224
+
225
+ r.computed('ageCategory', () => {
226
+ if (user.age < 18) return 'Minor';
227
+ if (user.age < 65) return 'Adult';
228
+ return 'Senior';
229
+ });
230
+ </script>
231
+ ```
@@ -98,6 +98,48 @@
98
98
  </code>
99
99
  </section>
100
100
 
101
+ <!-- Demonstrate computed properties -->
102
+ <section class="grid">
103
+ <div>
104
+ <h2>Computed Properties</h2>
105
+ <p>Full Name: <strong res="fullName"></strong></p>
106
+ <p>Greeting: <em res="greeting"></em></p>
107
+ <p>Initials: <span res="initials"></span></p>
108
+ <div res-display="fullName.length > 10" class="conditional">
109
+ Full name is longer than 10 characters
110
+ </div>
111
+ </div>
112
+ <div>
113
+ <p style="color: #666; font-size: 0.9em;">
114
+ These values automatically update when you change the first or last name above.
115
+ Computed properties cannot be edited directly.
116
+ </p>
117
+ </div>
118
+ </section>
119
+
120
+ <section class="grid">
121
+ <code class="code">
122
+ // Computed properties automatically update<br/>
123
+ resonantJs.computed('fullName', () => {<br/>
124
+ &nbsp;&nbsp;return user.firstname + ' ' + user.lastname;<br/>
125
+ });<br/><br/>
126
+
127
+ resonantJs.computed('greeting', () => {<br/>
128
+ &nbsp;&nbsp;return 'Hello, ' + fullName + '!';<br/>
129
+ });<br/><br/>
130
+
131
+ resonantJs.computed('initials', () => {<br/>
132
+ &nbsp;&nbsp;return (user.firstname[0] || '') + (user.lastname[0] || '');<br/>
133
+ });
134
+ </code>
135
+ <code class="code">
136
+ &lt;p&gt;Full Name: &lt;strong res="fullName"&gt;&lt;/strong&gt;&lt;/p&gt;<br/>
137
+ &lt;p&gt;Greeting: &lt;em res="greeting"&gt;&lt;/em&gt;&lt;/p&gt;<br/>
138
+ &lt;p&gt;Initials: &lt;span res="initials"&gt;&lt;/span&gt;&lt;/p&gt;<br/>
139
+ &lt;div res-display="fullName.length &gt; 10"&gt;...&lt;/div&gt;
140
+ </code>
141
+ </section>
142
+
101
143
  <!-- Demonstrate dynamic list rendering -->
102
144
  <section class="grid">
103
145
  <div>
@@ -185,6 +227,10 @@
185
227
  <td>resonantJs.addCallback("variableName", (objectReturned) => {})</td>
186
228
  <td>Bind a callback function to a variable</td>
187
229
  </tr>
230
+ <tr>
231
+ <td>resonantJs.computed("computedName", () => { return expression; })</td>
232
+ <td>Create a computed property that automatically updates when dependencies change</td>
233
+ </tr>
188
234
  </table>
189
235
  </section>
190
236
  </main>
@@ -207,7 +253,18 @@
207
253
  ]
208
254
  });
209
255
 
256
+ // Define computed properties that automatically update
257
+ resonantJs.computed('fullName', () => {
258
+ return user.firstname + ' ' + user.lastname;
259
+ });
260
+
261
+ resonantJs.computed('greeting', () => {
262
+ return 'Hello, ' + fullName + '!';
263
+ });
210
264
 
265
+ resonantJs.computed('initials', () => {
266
+ return (user.firstname[0] || '') + (user.lastname[0] || '');
267
+ });
211
268
 
212
269
  // Chain together callbacks
213
270
  resonantJs.addCallback("user", (user) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "resonantjs",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
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": {