@arnelirobles/rnxjs
Version:
Minimalist Vanilla JS component system with reactive data binding.
306 lines (271 loc) • 11.9 kB
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>