UNPKG

@arnelirobles/rnxjs

Version:

Minimalist Vanilla JS component system with reactive data binding.

306 lines (271 loc) 11.9 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>rnxJS - Reactive Data Binding Demo</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"> <style> .demo-section { margin-bottom: 2rem; padding: 1.5rem; background: #f8f9fa; border-radius: 8px; } .state-display { background: #fff; padding: 1rem; border-left: 4px solid #0d6efd; margin-top: 1rem; font-family: 'Courier New', monospace; } </style> </head> <body class="p-4"> <Container> <h1 class="mb-4">🎯 Reactive Data Binding Demo</h1> <p class="lead">Experience automatic two-way data binding with rnxJS</p> <!-- Demo 1: Basic Two-Way Binding --> <div class="demo-section"> <h3>1️⃣ Basic Two-Way Binding</h3> <p class="text-muted">Type in the input to see real-time updates</p> <div class="mb-3"> <label class="form-label">Your Name:</label> <input type="text" class="form-control" data-bind="user.name" placeholder="Enter your name"> </div> <div class="alert alert-primary"> <strong>Hello, <span data-bind="user.name">stranger</span>! 👋</strong> </div> </div> <!-- Demo 2: Multiple Bindings --> <div class="demo-section"> <h3>2️⃣ Multiple Elements, Same Binding</h3> <p class="text-muted">All elements stay in sync automatically</p> <Row> <Col class="col-md-6"> <label class="form-label">Email:</label> <input type="email" class="form-control mb-2" data-bind="user.email" placeholder="email@example.com"> </Col> <Col class="col-md-6"> <label class="form-label">Email (Mirror):</label> <input type="email" class="form-control mb-2" data-bind="user.email" placeholder="Type in either field"> </Col> </Row> <p class="mt-2">Email entered: <strong data-bind="user.email">none</strong></p> </div> <!-- Demo 3: Form with Nested Properties --> <div class="demo-section"> <h3>3️⃣ Profile Form (Nested Objects)</h3> <p class="text-muted">Demonstrates nested property paths</p> <div class="row g-3"> <div class="col-md-6"> <label class="form-label">First Name:</label> <input type="text" class="form-control" data-bind="profile.firstName" placeholder="John"> </div> <div class="col-md-6"> <label class="form-label">Last Name:</label> <input type="text" class="form-control" data-bind="profile.lastName" placeholder="Doe"> </div> <div class="col-md-6"> <label class="form-label">Age:</label> <input type="number" class="form-control" data-bind="profile.age" placeholder="25"> </div> <div class="col-md-6"> <label class="form-label">City:</label> <input type="text" class="form-control" data-bind="profile.city" placeholder="New York"> </div> </div> <div class="state-display mt-3"> <strong>Profile Summary:</strong><br> Name: <span data-bind="profile.firstName">-</span> <span data-bind="profile.lastName">-</span><br> Age: <span data-bind="profile.age">-</span><br> City: <span data-bind="profile.city">-</span> </div> </div> <!-- Demo 4: Checkboxes and Options --> <div class="demo-section"> <h3>4️⃣ Checkboxes & Options</h3> <p class="text-muted">Boolean binding with checkboxes</p> <div class="form-check mb-3"> <input class="form-check-input" type="checkbox" data-bind="preferences.newsletter" id="newsletterCheck"> <label class="form-check-label" for="newsletterCheck"> Subscribe to newsletter </label> </div> <div class="form-check mb-3"> <input class="form-check-input" type="checkbox" data-bind="preferences.darkMode" id="darkModeCheck"> <label class="form-check-label" for="darkModeCheck"> Enable dark mode </label> </div> <div class="alert alert-info"> Newsletter: <strong data-bind="preferences.newsletter">false</strong><br> Dark Mode: <strong data-bind="preferences.darkMode">false</strong> </div> </div> <!-- Demo 5: Textarea Binding --> <div class="demo-section"> <h3>5️⃣ Textarea Binding</h3> <p class="text-muted">Multi-line text synchronization</p> <label class="form-label">Write a message:</label> <textarea class="form-control mb-3" rows="4" data-bind="message.text" placeholder="Type your message here..."></textarea> <div class="card"> <div class="card-header">Preview</div> <div class="card-body"> <p data-bind="message.text" style="white-space: pre-wrap;">Your message will appear here...</p> </div> </div> </div> <!-- Demo 6: Real-time Calculation --> <div class="demo-section"> <h3>6️⃣ Shopping Cart</h3> <p class="text-muted">Update quantity and product name</p> <div class="row g-3 mb-3"> <div class="col-md-6"> <label class="form-label">Product Name:</label> <input type="text" class="form-control" data-bind="cart.product" placeholder="Widget"> </div> <div class="col-md-3"> <label class="form-label">Quantity:</label> <input type="number" class="form-control" data-bind="cart.quantity" placeholder="1" value="1"> </div> <div class="col-md-3"> <label class="form-label">Price ($):</label> <input type="number" class="form-control" data-bind="cart.price" placeholder="10" value="10"> </div> </div> <div class="alert alert-success"> <strong>Cart Summary:</strong><br> Product: <span data-bind="cart.product">-</span><br> Quantity: <span data-bind="cart.quantity">0</span><br> Unit Price: $<span data-bind="cart.price">0</span><br> <hr> <strong>Total: $<span id="cartTotal">0</span></strong> </div> </div> <!-- State Inspector --> <div class="demo-section"> <h3>🔍 State Inspector</h3> <p class="text-muted">Current reactive state (updates in real-time)</p> <pre id="stateInspector" class="bg-white p-3 rounded border">Loading...</pre> </div> </Container> <!-- Load rnxJS --> <script src="../dist/rnx.global.js"></script> <script> // Create reactive state const state = rnx.createReactiveState({ user: { name: '', email: '' }, profile: { firstName: '', lastName: '', age: '', city: '' }, preferences: { newsletter: false, darkMode: false }, message: { text: '' }, cart: { product: '', quantity: 1, price: 10 } }); // Auto-register and load components rnx.autoRegisterComponents(); rnx.loadComponents(document, state); // Calculate cart total (example of reacting to state changes) function updateCartTotal() { const quantity = parseInt(state.cart.quantity) || 0; const price = parseFloat(state.cart.price) || 0; const total = quantity * price; document.getElementById('cartTotal').textContent = total.toFixed(2); } // Subscribe to cart changes state.subscribe('cart.quantity', updateCartTotal); state.subscribe('cart.price', updateCartTotal); updateCartTotal(); // Initial calculation // State inspector - updates in real-time function updateStateInspector() { const stateObj = { user: { name: state.user.name, email: state.user.email }, profile: { firstName: '', lastName: '', age: '', city: '' }, preferences: { newsletter: false, darkMode: false }, message: { text: '' }, cart: { product: '', quantity: 1, price: 10 } }); // Auto-register and load components rnx.autoRegisterComponents(); rnx.loadComponents(document, state); // Calculate cart total (example of reacting to state changes) function updateCartTotal() { const quantity = parseInt(state.cart.quantity) || 0; const price = parseFloat(state.cart.price) || 0; const total = quantity * price; document.getElementById('cartTotal').textContent = total.toFixed(2); } // Subscribe to cart changes state.subscribe('cart.quantity', updateCartTotal); state.subscribe('cart.price', updateCartTotal); updateCartTotal(); // Initial calculation // State inspector - updates in real-time function updateStateInspector() { const stateObj = { user: { name: state.user.name, email: state.user.email }, profile: { firstName: state.profile.firstName, lastName: state.profile.lastName, age: state.profile.age, city: state.profile.city }, preferences: { newsletter: state.preferences.newsletter, darkMode: state.preferences.darkMode }, message: { text: state.message.text }, cart: { product: state.cart.product, quantity: state.cart.quantity, price: state.cart.price } }; document.getElementById('stateInspector').textContent = JSON.stringify(stateObj, null, 2); } // Subscribe to all state changes for inspector ['user', 'profile', 'preferences', 'message', 'cart'].forEach(path => { state.subscribe(path, updateStateInspector); }); updateStateInspector(); // Initial display </script> </body> </html>