resonantjs 1.1.4 → 1.1.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.
@@ -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,434 @@
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 ~15KB 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 15KB 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. **Input Binding**
120
+ Two-way data binding for form elements:
121
+
122
+ ```html
123
+ <input type="text" res="user.name" />
124
+ <input type="checkbox" res="settings.darkMode" />
125
+ <select res="user.country">
126
+ <option value="us">United States</option>
127
+ <option value="uk">United Kingdom</option>
128
+ </select>
129
+ ```
130
+
131
+ ---
132
+
133
+ ## 🎯 Key Features
134
+
135
+ ### **Reactive Data Management**
136
+ ```javascript
137
+ const resonant = new Resonant();
138
+
139
+ // Add single variables
140
+ resonant.add('counter', 0);
141
+ resonant.add('user', { name: 'John', age: 30 });
142
+
143
+ // Batch initialization
144
+ resonant.addAll({
145
+ tasks: [],
146
+ settings: { theme: 'light' },
147
+ currentUser: { name: 'Alice', role: 'admin' }
148
+ });
149
+
150
+ // Changes automatically update the UI
151
+ user.name = 'Jane'; // DOM updates instantly
152
+ tasks.push({ title: 'New task' }); // Array renders new item
153
+ ```
154
+
155
+ ### **Event Callbacks**
156
+ React to data changes with custom logic:
157
+
158
+ ```javascript
159
+ resonant.addCallback('tasks', (newValue, item, action) => {
160
+ console.log(`Tasks ${action}:`, item);
161
+ updateTaskCounter();
162
+ saveToAPI();
163
+ });
164
+
165
+ resonant.addCallback('user', (newValue, item, action) => {
166
+ if (action === 'modified') {
167
+ showNotification('Profile updated');
168
+ }
169
+ });
170
+ ```
171
+
172
+ ### **Persistence**
173
+ Automatic localStorage integration:
174
+
175
+ ```javascript
176
+ // Data persists across browser sessions
177
+ resonant.add('userPreferences', { theme: 'dark' }, true);
178
+ resonant.add('appState', { currentView: 'dashboard' }, true);
179
+
180
+ // Changes are automatically saved
181
+ userPreferences.theme = 'light'; // Saved to localStorage
182
+ ```
183
+
184
+ ### **Advanced Array Operations**
185
+ Full array reactivity with custom methods:
186
+
187
+ ```javascript
188
+ // All operations trigger UI updates
189
+ items.push(newItem); // Add item
190
+ items.splice(index, 1); // Remove item
191
+ items.update([...newItems]); // Replace entire array
192
+ items.set(index, newValue); // Update specific index
193
+ items.delete(index); // Delete by index
194
+ ```
195
+
196
+ ---
197
+
198
+ ## 🏗️ Real-World Examples
199
+
200
+ ### Todo List with Filtering
201
+ ```html
42
202
  <div>
43
- <h2>Counter</h2>
44
- <p>Current count: <span res="counter"></span></p>
45
- <button onclick="counter++">Increment Counter</button>
203
+ <input res="newTask" placeholder="Add task..." />
204
+ <button onclick="addTask()">Add</button>
205
+
206
+ <select res="filter">
207
+ <option value="all">All Tasks</option>
208
+ <option value="active">Active</option>
209
+ <option value="completed">Completed</option>
210
+ </select>
211
+
212
+ <ul>
213
+ <li res="filteredTasks"
214
+ res-display="filter === 'all' || (filter === 'active' && !completed) || (filter === 'completed' && completed)">
215
+ <input type="checkbox" res-prop="completed" />
216
+ <span res-prop="title" res-style="completed ? 'completed-task' : ''"></span>
217
+ <button res-onclick="deleteTask(item)">Delete</button>
218
+ </li>
219
+ </ul>
46
220
  </div>
47
221
 
48
222
  <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
223
+ const resonant = new Resonant();
224
+ resonant.addAll({
225
+ tasks: [
226
+ { title: 'Learn ResonantJs', completed: false },
227
+ { title: 'Build awesome app', completed: false }
228
+ ],
229
+ newTask: '',
230
+ filter: 'all',
231
+ filteredTasks: []
232
+ });
233
+
234
+ function addTask() {
235
+ if (newTask.trim()) {
236
+ tasks.push({ title: newTask, completed: false });
237
+ newTask = '';
238
+ updateFilter();
239
+ }
240
+ }
241
+
242
+ function deleteTask(task) {
243
+ const index = tasks.indexOf(task);
244
+ tasks.splice(index, 1);
245
+ updateFilter();
246
+ }
247
+
248
+ function updateFilter() {
249
+ filteredTasks.splice(0);
250
+ tasks.forEach(task => filteredTasks.push(task));
251
+ }
252
+
253
+ resonant.addCallback('filter', updateFilter);
254
+ resonant.addCallback('tasks', updateFilter);
255
+ updateFilter();
54
256
  </script>
55
- </body>
56
- </html>
57
257
  ```
58
258
 
59
- ## Features Overview
259
+ ### Dashboard with Statistics
260
+ ```html
261
+ <div class="dashboard">
262
+ <div class="stats">
263
+ <div class="stat-card">
264
+ <h3 res="stats.totalTasks"></h3>
265
+ <p>Total Tasks</p>
266
+ </div>
267
+ <div class="stat-card">
268
+ <h3 res="stats.completedTasks"></h3>
269
+ <p>Completed</p>
270
+ </div>
271
+ <div class="stat-card">
272
+ <h3 res="stats.completionRate"></h3>
273
+ <p>% Complete</p>
274
+ </div>
275
+ </div>
276
+
277
+ <div class="projects">
278
+ <div res="projects">
279
+ <div res-prop="" class="project-card">
280
+ <h3 res-prop="name"></h3>
281
+ <div class="progress-bar">
282
+ <div class="progress" res-style="'width: ' + progress + '%'"></div>
283
+ </div>
284
+ <div res-prop="tasks">
285
+ <div res-prop="" res-style="'task priority-' + priority">
286
+ <span res-prop="title"></span>
287
+ <span res-prop="assignee"></span>
288
+ </div>
289
+ </div>
290
+ </div>
291
+ </div>
292
+ </div>
293
+ </div>
294
+ ```
295
+
296
+ ---
297
+
298
+ ## 📚 Advanced Patterns
299
+
300
+ ### **Nested Data Structures**
301
+ Handle complex, deeply nested data:
302
+
303
+ ```javascript
304
+ resonant.add('company', {
305
+ departments: [
306
+ {
307
+ name: 'Engineering',
308
+ teams: [
309
+ {
310
+ name: 'Frontend',
311
+ members: [
312
+ { name: 'Alice', role: 'Senior Dev', skills: ['React', 'Vue'] },
313
+ { name: 'Bob', role: 'Junior Dev', skills: ['HTML', 'CSS'] }
314
+ ]
315
+ }
316
+ ]
317
+ }
318
+ ]
319
+ });
320
+
321
+ // All levels are reactive
322
+ company.departments[0].teams[0].members[0].name = 'Alice Johnson';
323
+ company.departments[0].teams[0].members.push(newMember);
324
+ ```
325
+
326
+ ### **Computed Properties**
327
+ Create reactive calculated values:
328
+
329
+ ```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;
336
+ });
337
+ ```
338
+
339
+ ### **Component-Like Patterns**
340
+ Organize code into reusable patterns:
341
+
342
+ ```javascript
343
+ function createTaskManager(containerId) {
344
+ const resonant = new Resonant();
345
+
346
+ resonant.addAll({
347
+ tasks: [],
348
+ filter: 'all',
349
+ newTask: ''
350
+ });
351
+
352
+ resonant.addCallback('tasks', updateStats);
353
+
354
+ return {
355
+ addTask: () => { /* implementation */ },
356
+ removeTask: (task) => { /* implementation */ },
357
+ setFilter: (filter) => { /* implementation */ }
358
+ };
359
+ }
360
+ ```
361
+
362
+ ---
363
+
364
+ ## 🎨 Examples & Demos
365
+
366
+ Explore our comprehensive examples:
60
367
 
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.
368
+ - **[Basic Counter](./examples/example-basic.html)** - Simple reactive counter
369
+ - **[Task Manager](./examples/example-taskmanager.html)** - Complete task management app
370
+ - **[Houses Demo](./examples/example-houses.html)** - Complex nested data structures
371
+ - **[Advanced Demo](./examples/example-taskmanager-simple-demo.html)** - Full-featured application
70
372
 
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.
373
+ Each example demonstrates different aspects of ResonantJs and can serve as starting points for your projects.
76
374
 
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/).
375
+ ---
79
376
 
80
- ## License
377
+ ## 🚀 Performance & Browser Support
378
+
379
+ ### **Performance**
380
+ - **Minimal overhead**: Only updates affected DOM elements
381
+ - **Efficient diffing**: Smart change detection for nested objects
382
+ - **Lazy evaluation**: Conditional expressions only run when dependencies change
383
+ - **Memory efficient**: Automatic cleanup of unused observers
384
+
385
+ ### **Browser Support**
386
+ - ✅ Chrome 60+
387
+ - ✅ Firefox 55+
388
+ - ✅ Safari 12+
389
+ - ✅ Edge 79+
390
+ - ✅ Mobile browsers (iOS Safari, Chrome Mobile)
391
+
392
+ ---
393
+
394
+ ## 🤝 Contributing
395
+
396
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
397
+
398
+ ### Development Setup
399
+ ```bash
400
+ git clone https://github.com/amurgola/ResonantJs.git
401
+ cd ResonantJs
402
+ npm install
403
+ npm test
404
+ ```
81
405
 
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.
406
+ ### Running Tests
407
+ ```bash
408
+ npm test # Run all tests
409
+ npm test -- test/specific.test.js # Run specific test file
410
+ ```
411
+
412
+ ---
413
+
414
+ ## 📄 License
415
+
416
+ ResonantJs is released under the **MIT License**. See [LICENSE](LICENSE) file for details.
417
+
418
+ ---
419
+
420
+ ## 🙋‍♂️ Support & Community
421
+
422
+ - **Issues**: [GitHub Issues](https://github.com/amurgola/ResonantJs/issues)
423
+ - **Discussions**: [GitHub Discussions](https://github.com/amurgola/ResonantJs/discussions)
424
+ - **Documentation**: [Full API Documentation](https://github.com/amurgola/ResonantJs/wiki)
425
+
426
+ ---
427
+
428
+ <div align="center">
429
+
430
+ **[⭐ 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)**
431
+
432
+ *Built with ❤️ by [Andrew Paul Murgola](https://github.com/amurgola)*
433
+
434
+ </div>
@@ -0,0 +1,69 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>Resonant.js Nested Objects Demo</title>
5
+ <link
6
+ rel="stylesheet"
7
+ href="https://cdn.jsdelivr.net/npm/@picocss/pico@2.0.6/css/pico.min.css"
8
+ />
9
+ <script src="../resonant.js"></script>
10
+ <style>
11
+ section.grid {
12
+ padding: 1rem;
13
+ margin-bottom: 2rem;
14
+ border: 1px solid #e0e0e0;
15
+ border-radius: 0.5rem;
16
+ }
17
+ </style>
18
+ </head>
19
+ <body>
20
+ <main class="container">
21
+ <h1>Nested Objects Demo - Houses</h1>
22
+
23
+ <section class="grid">
24
+ <h2>Houses</h2>
25
+ <div res="houses">
26
+ <h3 res-prop="name"></h3>
27
+ <div res-display="people.length > 0">
28
+ <h4>People:</h4>
29
+ <div res-prop="people">
30
+ <span res-prop="name"></span>
31
+ <br/>
32
+ Contact(s):
33
+ <div res-prop="contact">
34
+ <span res-prop="email"></span>,
35
+ <span res-prop="phone"></span>
36
+ </div>
37
+ </div>
38
+
39
+ </div>
40
+ <div res-display="people.length == 0" class="conditional">
41
+ No people in this house.
42
+ </div>
43
+ </div>
44
+ </section>
45
+ </main>
46
+
47
+ <script>
48
+ const resonantJs = new Resonant();
49
+ resonantJs.addAll({
50
+ houses: [
51
+ {
52
+ name: 'Blue House',
53
+ people: [
54
+ {
55
+ name: 'John Doe',
56
+ contact: [{ email: 'john@example.com', phone: '123-456-7890' },{ email: 'john@asdasdas.com', phone: '123-sss456-7890' }]
57
+ },
58
+ {
59
+ name: 'Jane Doe',
60
+ contact: [{ email: 'jane@example.com', phone: '222-555-1234' }]
61
+ }
62
+ ],
63
+ newPerson: { name: '', contact: { email: '', phone: '' } }
64
+ }
65
+ ]
66
+ });
67
+ </script>
68
+ </body>
69
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "resonantjs",
3
- "version": "1.1.4",
3
+ "version": "1.1.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": {
@@ -14,6 +14,10 @@
14
14
  },
15
15
  "homepage": "https://github.com/amurgola/ResonantJs#readme",
16
16
  "scripts": {
17
- "test": "echo \"Error: no test specified\" && exit 1"
17
+ "test": "node --test",
18
+ "build": "node scripts/build.js"
19
+ },
20
+ "devDependencies": {
21
+ "terser": "^5.34.1"
18
22
  }
19
23
  }