resonantjs 1.1.4 → 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.
@@ -16,6 +16,7 @@ jobs:
16
16
  with:
17
17
  node-version: 16
18
18
  - run: npm ci
19
+ - run: npm run build
19
20
 
20
21
  publish-npm:
21
22
  needs: build
@@ -27,6 +28,7 @@ jobs:
27
28
  node-version: 16
28
29
  registry-url: https://registry.npmjs.org/
29
30
  - run: npm ci
31
+ - run: npm run build
30
32
  - run: npm publish
31
33
  env:
32
34
  NODE_AUTH_TOKEN: ${{secrets.npm_token}}
package/README.md CHANGED
@@ -1,82 +1,511 @@
1
- # Resonant.js
1
+ # ResonantJs 🚀
2
2
 
3
- Resonant.js is an open-source 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.
3
+ [![npm version](https://badge.fury.io/js/resonantjs.svg)](https://badge.fury.io/js/resonantjs)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
5
 
5
- ## Features
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.
6
7
 
7
- - **Reactive Data Binding**: Automatically synchronize your data with the UI.
8
- - **Dynamic List Rendering**: Easily render lists that react to data changes.
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.
11
- - **Lightweight and Easy to Integrate**: Minimal setup required to get started.
12
- ## Installation
13
- ## NPM
14
- To install via NPM, use the following command:
8
+ > **Zero dependencies ~11.5KB minified Lightning fast Easy to learn**
15
9
 
16
- ```bash
17
- npm i resonantjs
18
- ```
10
+ ## ✨ Why Choose ResonantJs?
19
11
 
20
- ## CDN
21
- To use via CDN, include the following URLs in your HTML file:
12
+ - 🔄 **True Reactivity**: Data changes automatically update the DOM - no manual manipulation needed
13
+ - 🎯 **Attribute-Based**: Use simple HTML attributes to create powerful data bindings
14
+ - 🏗️ **Deep Object Support**: Handles nested objects and arrays with full reactivity
15
+ - 💾 **Built-in Persistence**: Automatic localStorage integration for data persistence
16
+ - 🎨 **Dynamic Styling**: Conditional CSS classes and styles based on your data
17
+ - ⚡ **Performance**: Efficient updates with minimal overhead
18
+ - 📦 **Tiny Footprint**: Under 11.5KB minified - perfect for any project size
22
19
 
23
- ```html
24
- <script src="https://unpkg.com/resonantjs@latest/resonant.js"></script>
20
+ ---
21
+
22
+ ## 🚀 Quick Start
23
+
24
+ ### Installation
25
+
26
+ #### NPM
27
+ ```bash
28
+ npm install resonantjs
25
29
  ```
26
30
 
27
- ## Demo
28
- ![](https://github.com/amurgola/ResonantJs/blob/main/Demo.gif)
31
+ #### CDN
32
+ ```html
33
+ <script src="https://unpkg.com/resonantjs@latest/resonant.js"></script>
34
+ ```
29
35
 
30
- ## Usage
31
- Include resonant.js in your HTML file, and use the following example to understand how to integrate it into your web application.
36
+ ### Hello World Example
32
37
 
33
38
  ```html
34
39
  <!DOCTYPE html>
35
- <html lang="en">
40
+ <html>
36
41
  <head>
37
- <title>Resonant.js Basic Example</title>
42
+ <title>ResonantJs Demo</title>
38
43
  <script src="https://unpkg.com/resonantjs@latest/resonant.js"></script>
39
44
  </head>
40
45
  <body>
41
- <h1>Resonant.js Basic Example</h1>
46
+ <h1>Counter: <span res="counter"></span></h1>
47
+ <button onclick="counter++">Increment</button>
48
+ <button onclick="counter--">Decrement</button>
49
+
50
+ <script>
51
+ const resonant = new Resonant();
52
+ resonant.add("counter", 0, true); // value, localStorage persistence
53
+ </script>
54
+ </body>
55
+ </html>
56
+ ```
57
+
58
+ That's it! Your counter will automatically update the DOM and persist to localStorage.
59
+
60
+ ---
61
+
62
+ ## 📖 Core Concepts
63
+
64
+ ### 1. **Data Binding** (`res`)
65
+ Bind HTML elements directly to your JavaScript variables:
66
+
67
+ ```html
68
+ <span res="username"></span> <!-- Simple variable -->
69
+ <div res="user.profile.name"></div> <!-- Nested object property -->
70
+ ```
71
+
72
+ ### 2. **Object Properties** (`res-prop`)
73
+ Bind to specific properties within objects:
74
+
75
+ ```html
76
+ <div res="user">
77
+ <span res-prop="name"></span>
78
+ <span res-prop="email"></span>
79
+ </div>
80
+ ```
81
+
82
+ ### 3. **Array Rendering**
83
+ Automatically render arrays with template-based elements:
84
+
85
+ ```html
86
+ <ul>
87
+ <li res="todoItems">
88
+ <span res-prop="title"></span>
89
+ <button res-onclick="removeItem(item)">Delete</button>
90
+ </li>
91
+ </ul>
92
+ ```
93
+
94
+ ### 4. **Conditional Display** (`res-display`)
95
+ Show/hide elements based on conditions:
96
+
97
+ ```html
98
+ <div res-display="user.isActive">Welcome back!</div>
99
+ <div res-display="tasks.length > 0">You have tasks</div>
100
+ <div res-display="user.role === 'admin'">Admin Panel</div>
101
+ ```
102
+
103
+ ### 5. **Dynamic Styling** (`res-style`)
104
+ Apply conditional CSS classes and styles:
105
+
106
+ ```html
107
+ <div res-style="task.completed ? 'completed' : 'pending'">Task</div>
108
+ <span res-style="'priority-' + task.priority">High Priority</span>
109
+ ```
110
+
111
+ ### 6. **Event Handling** (`res-onclick`)
112
+ Bind click events with context:
113
+
114
+ ```html
115
+ <button res-onclick="editTask(item)">Edit</button>
116
+ <button res-onclick-remove="true">Delete Item</button>
117
+ ```
118
+
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**
138
+ Two-way data binding for form elements:
139
+
140
+ ```html
141
+ <input type="text" res="user.name" />
142
+ <input type="checkbox" res="settings.darkMode" />
143
+ <select res="user.country">
144
+ <option value="us">United States</option>
145
+ <option value="uk">United Kingdom</option>
146
+ </select>
147
+ ```
148
+
149
+ ---
150
+
151
+ ## 🎯 Key Features
152
+
153
+ ### **Reactive Data Management**
154
+ ```javascript
155
+ const resonant = new Resonant();
156
+
157
+ // Add single variables
158
+ resonant.add('counter', 0);
159
+ resonant.add('user', { name: 'John', age: 30 });
160
+
161
+ // Batch initialization
162
+ resonant.addAll({
163
+ tasks: [],
164
+ settings: { theme: 'light' },
165
+ currentUser: { name: 'Alice', role: 'admin' }
166
+ });
167
+
168
+ // Changes automatically update the UI
169
+ user.name = 'Jane'; // DOM updates instantly
170
+ tasks.push({ title: 'New task' }); // Array renders new item
171
+ ```
172
+
173
+ ### **Event Callbacks**
174
+ React to data changes with custom logic:
175
+
176
+ ```javascript
177
+ resonant.addCallback('tasks', (newValue, item, action) => {
178
+ console.log(`Tasks ${action}:`, item);
179
+ updateTaskCounter();
180
+ saveToAPI();
181
+ });
182
+
183
+ resonant.addCallback('user', (newValue, item, action) => {
184
+ if (action === 'modified') {
185
+ showNotification('Profile updated');
186
+ }
187
+ });
188
+ ```
189
+
190
+ ### **Persistence**
191
+ Automatic localStorage integration:
192
+
193
+ ```javascript
194
+ // Data persists across browser sessions
195
+ resonant.add('userPreferences', { theme: 'dark' }, true);
196
+ resonant.add('appState', { currentView: 'dashboard' }, true);
197
+
198
+ // Changes are automatically saved
199
+ userPreferences.theme = 'light'; // Saved to localStorage
200
+ ```
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
+
223
+ ### **Advanced Array Operations**
224
+ Full array reactivity with custom methods:
225
+
226
+ ```javascript
227
+ // All operations trigger UI updates
228
+ items.push(newItem); // Add item
229
+ items.splice(index, 1); // Remove item
230
+ items.update([...newItems]); // Replace entire array
231
+ items.set(index, newValue); // Update specific index
232
+ items.delete(index); // Delete by index
233
+ ```
234
+
235
+ ---
236
+
237
+ ## 🏗️ Real-World Examples
238
+
239
+ ### Todo List with Filtering
240
+ ```html
42
241
  <div>
43
- <h2>Counter</h2>
44
- <p>Current count: <span res="counter"></span></p>
45
- <button onclick="counter++">Increment Counter</button>
242
+ <input res="newTask" placeholder="Add task..." />
243
+ <button onclick="addTask()">Add</button>
244
+
245
+ <select res="filter">
246
+ <option value="all">All Tasks</option>
247
+ <option value="active">Active</option>
248
+ <option value="completed">Completed</option>
249
+ </select>
250
+
251
+ <ul>
252
+ <li res="filteredTasks"
253
+ res-display="filter === 'all' || (filter === 'active' && !completed) || (filter === 'completed' && completed)">
254
+ <input type="checkbox" res-prop="completed" />
255
+ <span res-prop="title" res-style="completed ? 'completed-task' : ''"></span>
256
+ <button res-onclick="deleteTask(item)">Delete</button>
257
+ </li>
258
+ </ul>
46
259
  </div>
47
260
 
48
261
  <script>
49
- const resonantJs = new Resonant();
50
- resonantJs.add("counter", 0, true);
51
- //counter will instantiate the variable with name "counter"
52
- //0 is the initial value of the variable
53
- //true is optional, if true the variable will be stored in local storage
262
+ const resonant = new Resonant();
263
+ resonant.addAll({
264
+ tasks: [
265
+ { title: 'Learn ResonantJs', completed: false },
266
+ { title: 'Build awesome app', completed: false }
267
+ ],
268
+ newTask: '',
269
+ filter: 'all',
270
+ filteredTasks: []
271
+ });
272
+
273
+ function addTask() {
274
+ if (newTask.trim()) {
275
+ tasks.push({ title: newTask, completed: false });
276
+ newTask = '';
277
+ updateFilter();
278
+ }
279
+ }
280
+
281
+ function deleteTask(task) {
282
+ const index = tasks.indexOf(task);
283
+ tasks.splice(index, 1);
284
+ updateFilter();
285
+ }
286
+
287
+ function updateFilter() {
288
+ filteredTasks.splice(0);
289
+ tasks.forEach(task => filteredTasks.push(task));
290
+ }
291
+
292
+ resonant.addCallback('filter', updateFilter);
293
+ resonant.addCallback('tasks', updateFilter);
294
+ updateFilter();
54
295
  </script>
55
- </body>
56
- </html>
57
296
  ```
58
297
 
59
- ## Features Overview
298
+ ### Dashboard with Statistics
299
+ ```html
300
+ <div class="dashboard">
301
+ <div class="stats">
302
+ <div class="stat-card">
303
+ <h3 res="stats.totalTasks"></h3>
304
+ <p>Total Tasks</p>
305
+ </div>
306
+ <div class="stat-card">
307
+ <h3 res="stats.completedTasks"></h3>
308
+ <p>Completed</p>
309
+ </div>
310
+ <div class="stat-card">
311
+ <h3 res="stats.completionRate"></h3>
312
+ <p>% Complete</p>
313
+ </div>
314
+ </div>
315
+
316
+ <div class="projects">
317
+ <div res="projects">
318
+ <div res-prop="" class="project-card">
319
+ <h3 res-prop="name"></h3>
320
+ <div class="progress-bar">
321
+ <div class="progress" res-style="'width: ' + progress + '%'"></div>
322
+ </div>
323
+ <div res-prop="tasks">
324
+ <div res-prop="" res-style="'task priority-' + priority">
325
+ <span res-prop="title"></span>
326
+ <span res-prop="assignee"></span>
327
+ </div>
328
+ </div>
329
+ </div>
330
+ </div>
331
+ </div>
332
+ </div>
333
+ ```
334
+
335
+ ---
336
+
337
+ ## 📚 Advanced Patterns
338
+
339
+ ### **Nested Data Structures**
340
+ Handle complex, deeply nested data:
341
+
342
+ ```javascript
343
+ resonant.add('company', {
344
+ departments: [
345
+ {
346
+ name: 'Engineering',
347
+ teams: [
348
+ {
349
+ name: 'Frontend',
350
+ members: [
351
+ { name: 'Alice', role: 'Senior Dev', skills: ['React', 'Vue'] },
352
+ { name: 'Bob', role: 'Junior Dev', skills: ['HTML', 'CSS'] }
353
+ ]
354
+ }
355
+ ]
356
+ }
357
+ ]
358
+ });
359
+
360
+ // All levels are reactive
361
+ company.departments[0].teams[0].members[0].name = 'Alice Johnson';
362
+ company.departments[0].teams[0].members.push(newMember);
363
+ ```
364
+
365
+ ### **Computed Properties**
366
+ Create reactive calculated values that automatically update when dependencies change:
367
+
368
+ ```javascript
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;
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>
414
+ ```
415
+
416
+ ### **Component-Like Patterns**
417
+ Organize code into reusable patterns:
418
+
419
+ ```javascript
420
+ function createTaskManager(containerId) {
421
+ const resonant = new Resonant();
422
+
423
+ resonant.addAll({
424
+ tasks: [],
425
+ filter: 'all',
426
+ newTask: ''
427
+ });
428
+
429
+ resonant.addCallback('tasks', updateStats);
430
+
431
+ return {
432
+ addTask: () => { /* implementation */ },
433
+ removeTask: (task) => { /* implementation */ },
434
+ setFilter: (filter) => { /* implementation */ }
435
+ };
436
+ }
437
+ ```
438
+
439
+ ---
440
+
441
+ ## 🎨 Examples & Demos
442
+
443
+ Explore our comprehensive examples:
444
+
445
+ - **[Basic Counter](./examples/example-basic.html)** - Simple reactive counter
446
+ - **[Task Manager](./examples/example-taskmanager.html)** - Complete task management app
447
+ - **[Houses Demo](./examples/example-houses.html)** - Complex nested data structures
448
+ - **[Advanced Demo](./examples/example-taskmanager-simple-demo.html)** - Full-featured application
449
+
450
+ Each example demonstrates different aspects of ResonantJs and can serve as starting points for your projects.
451
+
452
+ ---
453
+
454
+ ## 🚀 Performance & Browser Support
60
455
 
61
- ### Core Concepts
62
- - **`res` and `res-prop` Attributes**: Bind HTML elements to your data model seamlessly.
63
- - `res` is used to identify an overarching data model.
64
- - `res-prop` links individual properties within that model to corresponding UI elements.
65
- - **`res-display` Attribute**: Conditionally display elements based on the data model's properties.
66
- - **`res-onclick` Attribute**: Triggers a function when an element is clicked, allowing for custom event handling.
67
- - **`res-onclick-remove` Attribute**: Removes an item from an array when the associated element is clicked.
68
- - **`res-style` Attribute**: Dynamically update CSS styles based on data model properties.
69
- - **Automatic UI Updates**: Changes to your JavaScript objects instantly reflect in the associated UI components, reducing manual DOM manipulation.
456
+ ### **Performance**
457
+ - **Minimal overhead**: Only updates affected DOM elements
458
+ - **Efficient diffing**: Smart change detection for nested objects
459
+ - **Lazy evaluation**: Conditional expressions only run when dependencies change
460
+ - **Memory efficient**: Automatic cleanup of unused observers
70
461
 
71
- ### Advanced Features
72
- - **Dynamic Arrays and Objects**: Easily handle collections and nested objects to dynamically add or remove elements based on your data structures.
73
- - **Event Callbacks**: Register custom functions to execute whenever your data model changes.
74
- - **Bidirectional Input Binding**: Bind form input fields directly to your data, making two-way synchronization simple.
75
- - **Optional Persistent Data**: Save your data model to local storage for easy retrieval and persistence across sessions.
462
+ ### **Browser Support**
463
+ - Chrome 60+
464
+ - Firefox 55+
465
+ - Safari 12+
466
+ - Edge 79+
467
+ - ✅ Mobile browsers (iOS Safari, Chrome Mobile)
76
468
 
77
- ## Other Information
78
- - The demo HTML file uses the Pico CSS framework for styling. You can find more information about Pico CSS [here](https://picocss.com/).
469
+ ---
79
470
 
80
- ## License
471
+ ## 🤝 Contributing
81
472
 
82
- Resonant.js is released under the MIT License. You can find a copy of the MIT License in the LICENSE file included with the package.
473
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
474
+
475
+ ### Development Setup
476
+ ```bash
477
+ git clone https://github.com/amurgola/ResonantJs.git
478
+ cd ResonantJs
479
+ npm install
480
+ npm test
481
+ ```
482
+
483
+ ### Running Tests
484
+ ```bash
485
+ npm test # Run all tests
486
+ npm test -- test/specific.test.js # Run specific test file
487
+ ```
488
+
489
+ ---
490
+
491
+ ## 📄 License
492
+
493
+ ResonantJs is released under the **MIT License**. See [LICENSE](LICENSE) file for details.
494
+
495
+ ---
496
+
497
+ ## 🙋‍♂️ Support & Community
498
+
499
+ - **Issues**: [GitHub Issues](https://github.com/amurgola/ResonantJs/issues)
500
+ - **Discussions**: [GitHub Discussions](https://github.com/amurgola/ResonantJs/discussions)
501
+ - **Documentation**: [Full API Documentation](https://github.com/amurgola/ResonantJs/wiki)
502
+
503
+ ---
504
+
505
+ <div align="center">
506
+
507
+ **[⭐ Star us on GitHub](https://github.com/amurgola/ResonantJs)** • **[🚀 Try the Demo](./examples/example-taskmanager-simple-demo.html)** • **[📖 Read the Docs](https://github.com/amurgola/ResonantJs/wiki)**
508
+
509
+ *Built with ❤️ by [Andrew Paul Murgola](https://github.com/amurgola)*
510
+
511
+ </div>